All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fiftyone.devicedetection.hash.engine.onpremise.flowelements.DeviceDataHashDefault Maven / Gradle / Ivy

The newest version!
/* *********************************************************************
 * This Original Work is copyright of 51 Degrees Mobile Experts Limited.
 * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House,
 * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU.
 *
 * This Original Work is licensed under the European Union Public Licence
 * (EUPL) v.1.2 and is subject to its terms as set out below.
 *
 * If a copy of the EUPL was not distributed with this file, You can obtain
 * one at https://opensource.org/licenses/EUPL-1.2.
 *
 * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be
 * amended by the European Commission) shall be deemed incompatible for
 * the purposes of the Work and the provisions of the compatibility
 * clause in Article 5 of the EUPL shall not apply.
 *
 * If using the Work as, or as part of, a network application, by
 * including the attribution notice(s) required under Article 5 of the EUPL
 * in the end user terms of the application under an appropriate heading,
 * such notice(s) shall fulfill the requirements of that article.
 * ********************************************************************* */

package fiftyone.devicedetection.hash.engine.onpremise.flowelements;

import fiftyone.devicedetection.hash.engine.onpremise.Enums;
import fiftyone.devicedetection.hash.engine.onpremise.data.DeviceDataHash;
import fiftyone.devicedetection.hash.engine.onpremise.interop.Swig;
import fiftyone.devicedetection.hash.engine.onpremise.interop.swig.*;
import fiftyone.devicedetection.shared.DeviceDataBaseOnPremise;
import fiftyone.pipeline.core.data.FlowData;
import fiftyone.pipeline.core.data.TryGetResult;
import fiftyone.pipeline.core.data.types.JavaScript;
import fiftyone.pipeline.engines.data.AspectPropertyValue;
import fiftyone.pipeline.engines.data.AspectPropertyValueDefault;
import fiftyone.pipeline.engines.services.MissingPropertyService;
import org.slf4j.Logger;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static fiftyone.pipeline.util.StringManipulation.stringJoin;

/**
 * Internal implementation of the {@link DeviceDataHash} interface. This can
 * only be constructed by the {@link DeviceDetectionHashEngine}.
 * @see Specification
 */
public class DeviceDataHashDefault
    extends DeviceDataBaseOnPremise
    implements DeviceDataHash, AutoCloseable {

    /**
     * True if the {@link #close()} method has been called.
     */
    private boolean closed = false;

    /**
     * Pre-populated list of match methods. This to avoid calling the
     * '.values()' method every time.
     */
    private static final Enums.MatchMethods[] matchMethods =
            Enums.MatchMethods.values();

    /**
     * List of native results which make up this result instance. Usually, this
     * will only be one.
     */
    private final List resultsList = new ArrayList<>();

    /**
     * Constructs a new instance.
     * @param logger used for logging
     * @param flowData the {@link FlowData} instance this element data will be
     *                 associated with
     * @param engine the engine which created the instance
     * @param missingPropertyService service used to determine the reason for
     *                               a property value being missing
     */
    DeviceDataHashDefault(
        Logger logger,
        FlowData flowData,
        DeviceDetectionHashEngine engine,
        MissingPropertyService missingPropertyService) {
        super(logger, flowData, engine, missingPropertyService);
    }
    
    /**
     * Add the native results to the list of results contained in this instance.
     * @param results the results to add
     */
    void setResults(ResultsHashSwig results) {
        checkState();
        resultsList.add(results);
    }

    /**
     * Get a single native results instance. If there is only one available,
     * that is what is returned. If there are more, then the first one which
     * contains values for the requested property is returned.
     * @param propertyName used to select results from list
     * @return single native results instance
     */
    private ResultsHashSwig getSingleResults(String propertyName) {
        return resultsList.size() == 1 ?
            resultsList.get(0) : getResultsContainingProperty(propertyName);
    }

    /**
     * Get the first native results in {@link #resultsList} which contains
     * values for the requested property.
     * @param propertyName name of the requested property
     * @return native results containing values, or null if none were found
     */
    private ResultsHashSwig getResultsContainingProperty(String propertyName) {
        for (ResultsHashSwig results : resultsList) {
            if (results.containsProperty(Swig.asBytes(propertyName))) {
                return results;
            }
        }
        return null;
    }

    /**
     * Get the device id from the native results.
     * @return device id
     */
    private AspectPropertyValue getDeviceIdInternal() {
        if (resultsList.size() == 1) {
            // Only one Engine has added results, so return the device
            // id from those results.
            return new AspectPropertyValueDefault<>(
                    resultsList.get(0).getDeviceId());
        } else {
            // Multiple Engines have added results, so construct a device
            // id from the results.
            List result = new ArrayList<>();
            List deviceIds = new ArrayList<>();
            for (ResultsHashSwig results : resultsList) {
                deviceIds.add(results.getDeviceId().split("-"));
            }
            int max = 0;
            for (String[] deviceId : deviceIds) {
                if (deviceId.length > max) {
                    max = deviceId.length;
                }
            }
            for (int i = 0; i < max; i++) {
                String profileId = "0";
                for (String[] deviceId : deviceIds) {
                    if (deviceId.length > i && deviceId[i].equals("0") == false) {
                        profileId = deviceId[i];
                    }
                }
                result.add(profileId);
            }
            return new AspectPropertyValueDefault<>(
                    stringJoin(result, "-"));
        }
    }

    /**
     * Get the difference from the native results.
     * @return difference
     */
    private AspectPropertyValue getDifferenceInternal() {
        int total = 0;
        for (ResultsHashSwig results : resultsList) {
            total += results.getDifference();
        }
        return new AspectPropertyValueDefault<>(total);
    }

    /**
     * Get the drift from the native results.
     * @return drift
     */
    private AspectPropertyValue getDriftInternal() {
        int result = Integer.MAX_VALUE;
        for (ResultsHashSwig results : resultsList) {
            if (results.getDrift() < result) {
                result = results.getDrift();
            }
        }
        return new AspectPropertyValueDefault<>(result);
    }

    /**
     * Get the number of iterations from the native results.
     * @return iterations
     */
    private AspectPropertyValue getIterationsInternal() {
        int result = 0;
        for (ResultsHashSwig results : resultsList) {
            result += results.getIterations();
        }
        return new AspectPropertyValueDefault<>(result);
    }

    /**
     * Get the number of matched nodes from the native results.
     * @return matched nodes
     */
    private AspectPropertyValue getMatchedNodesInternal() {
        int result = 0;
        for (ResultsHashSwig results : resultsList) {
            result += results.getMatchedNodes();
        }
        return new AspectPropertyValueDefault<>(result);
    }

    /**
     * Get the match method from the native results.
     * @return match method
     */
    private AspectPropertyValue getMethodInternal() {
        int result = 0;
        for (ResultsHashSwig results : resultsList) {
            if (results.getMethod() > result) {
                result = results.getMethod();
            }
        }
        return new AspectPropertyValueDefault<>(matchMethods[result].name());
    }

    /**
     * Get the matched User-Agent strings from the native results.
     * @return matched User-Agents
     */
    private AspectPropertyValue> getUserAgentsInternal() {
        List result = new ArrayList<>();
        for (ResultsHashSwig results : resultsList) {
            for (int i = 0; i < results.getUserAgents(); i++) {
                String userAgent = results.getUserAgent(i);
                if (result.contains(userAgent) == false) {
                    result.add(userAgent);
                }
            }
        }
        return new AspectPropertyValueDefault<>(result);
    }

    @Override
    protected boolean propertyIsAvailable(String propertyName) {
        checkState();
        for (ResultsHashSwig results : resultsList) {
            if (results.containsProperty(Swig.asBytes(propertyName))) {
                return true;
            }
        }
        return false;
    }

    @Override
    public AspectPropertyValue> getValues(String propertyName) {
        checkState();
        AspectPropertyValue> result =
            new AspectPropertyValueDefault<>();
        ResultsHashSwig results = getSingleResults(propertyName);
        if (results != null) {
            try (VectorStringValuesSwig value = results.getValues(
                Swig.asBytes(propertyName))) {
                if (value.hasValue()) {
                    try (VectorStringSwig vector = value.getValue()) {
                        result.setValue(Collections.unmodifiableList(
                            Swig.asList(vector)));
                    }
                }
                else {
                    result.setNoValueMessage(value.getNoValueMessage());
                }
            }
        }
        return result;
    }

    @Override
    protected AspectPropertyValue getValueAsString(String propertyName) {
        AspectPropertyValue result = new AspectPropertyValueDefault<>();
        ResultsHashSwig results = getSingleResults(propertyName);
        if (results != null) {

            try (StringValueSwig value = results.getValueAsString(
                Swig.asBytes(propertyName))) {
                if (value.hasValue()) {
                    result.setValue(value.getValue());
                }
                else {
                    result.setNoValueMessage(value.getNoValueMessage());
                }
            }
        }
        return result;
    }

    @Override
    protected AspectPropertyValue getValueAsJavaScript(
        String propertyName) {
        AspectPropertyValue result =
            new AspectPropertyValueDefault<>();
        ResultsHashSwig results = getSingleResults(propertyName);
        if (results != null) {
            try (StringValueSwig value = results.getValueAsString(
                Swig.asBytes(propertyName))) {
                if (value.hasValue()) {
                    result.setValue(new JavaScript(value.getValue()));
                }
                else {
                    result.setNoValueMessage(value.getNoValueMessage());
                }
            }
        }
        return result;
    }

    @Override
    protected AspectPropertyValue getValueAsInteger(
        String propertyName) {
        AspectPropertyValue result = new AspectPropertyValueDefault<>();
        ResultsHashSwig results = getSingleResults(propertyName);
        if (results != null) {
            try (IntegerValueSwig value = results.getValueAsInteger(
                Swig.asBytes(propertyName))) {
                if (value.hasValue()) {
                    result.setValue(value.getValue());
                }
                else {
                    result.setNoValueMessage(value.getNoValueMessage());
                }
            }
        }
        return result;
    }

    @Override
    protected AspectPropertyValue getValueAsBool(String propertyName) {
        AspectPropertyValue result = new AspectPropertyValueDefault<>();
        ResultsHashSwig results = getSingleResults(propertyName);
        if (results != null) {
            try (BoolValueSwig value = results.getValueAsBool(
                Swig.asBytes(propertyName))) {
                if (value.hasValue()) {
                    result.setValue(value.getValue());
                }
                else {
                    result.setNoValueMessage(value.getNoValueMessage());
                }
            }
        }
        return result;
    }

    @Override
    protected AspectPropertyValue getValueAsDouble(String propertyName) {
        AspectPropertyValue result = new AspectPropertyValueDefault<>();
        ResultsHashSwig results = getSingleResults(propertyName);
        if (results != null) {
            try (DoubleValueSwig value = results.getValueAsDouble(
                Swig.asBytes(propertyName))) {
                if (value.hasValue()) {
                    result.setValue(value.getValue());
                }
                else {
                    result.setNoValueMessage(value.getNoValueMessage());
                }
            }
        }
        return result;
    }

    @SuppressWarnings("unchecked")
	@Override
    protected  TryGetResult tryGetValue(
        String key,
        Class type,
        Class... parameterisedTypes) {
        checkState();
        TryGetResult result = super.tryGetValue(key, type, parameterisedTypes);

        if (result.hasValue() == false) {
            boolean objSet = false;
            Object obj = null;
            if (key.equalsIgnoreCase("DeviceId")) {
                obj = getDeviceIdInternal();
                objSet = true;
            } else if (key.equalsIgnoreCase("Difference")) {
                obj = getDifferenceInternal();
                objSet = true;
            } else if (key.equalsIgnoreCase("UserAgents")) {
                obj = getUserAgentsInternal();
                objSet = true;
            } else if (key.equalsIgnoreCase("Drift")) {
                obj = getDriftInternal();
                objSet = true;
            } else if (key.equalsIgnoreCase("Iterations")) {
                obj = getIterationsInternal();
                objSet = true;
            }
            else if (key.equalsIgnoreCase("MatchedNodes")) {
                obj = getMatchedNodesInternal();
                objSet = true;
            }
            else if (key.equalsIgnoreCase("Method")){
                obj = getMethodInternal();
                objSet = true;
            }
            if (objSet == true) {
                try {
                    T value;
                    if (type.isPrimitive()) {
                        value = (T) primitiveTypes.get(type).cast(obj);
                    } else {
                        value = type.cast(obj);
                    }
                    result.setValue(value);
                } catch (ClassCastException e) {
                    throw new ClassCastException(
                        "Expected property '" + key + "' to be of " +
                            "type '" + type.getSimpleName() + "' but it is " +
                            "'" + obj.getClass().getSimpleName() + "'");
                }
            }
        }
        return result;
    }

    private void checkState() {
        if (closed == true) {
            throw new IllegalStateException("The DeviceDataHash instance has " +
                "been closed, and cannot be used. Any result processing should " +
                "be carried out within a 'try-with-resource' block which " +
                "closes the FlowData and any AutoCloseable elements.");
        }
    }

    @Override
    public void close() {
        closed = true;
        for (ResultsHashSwig result : resultsList) {
            try {
                result.close();
            } catch (Exception e) {
                logger.error("Failed to close native results instance. " +
                    "A DeviceDataHash instance contains native unmanaged " +
                    "memory which needs to be closed. Failing to close " +
                    "could lead to memory leaks.", e);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy