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

fiftyone.mobile.detection.entities.Value Maven / Gradle / Ivy

The newest version!
/* *********************************************************************
 * This Source Code Form is copyright of 51Degrees Mobile Experts Limited. 
 * Copyright © 2017 51Degrees Mobile Experts Limited, 5 Charlotte Close,
 * Caversham, Reading, Berkshire, United Kingdom RG4 7BY
 * 
 * This Source Code Form is the subject of the following patents and patent
 * applications, owned by 51Degrees Mobile Experts Limited of 5 Charlotte
 * Close, Caversham, Reading, Berkshire, United Kingdom RG4 7BY: 
 * European Patent No. 2871816;
 * European Patent Application No. 17184134.9;
 * United States Patent Nos. 9,332,086 and 9,350,823; and
 * United States Patent Application No. 15/686,066.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0.
 * 
 * If a copy of the MPL was not distributed with this file, You can obtain
 * one at http://mozilla.org/MPL/2.0/.
 * 
 * This Source Code Form is "Incompatible With Secondary Licenses", as
 * defined by the Mozilla Public License, v. 2.0.
 * ********************************************************************* */
package fiftyone.mobile.detection.entities;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import fiftyone.mobile.detection.Dataset;
import fiftyone.mobile.detection.WrappedIOException;
import fiftyone.mobile.detection.readers.BinaryReader;
import fiftyone.mobile.detection.search.SearchArrays;

import java.util.Collections;

/**
 * A value associated with a property and component within the dataset.
 * 

* Every {@link Property} can return one of many values, or multiple values if it's a list property. For example: * SupportedBearers returns a list of the bearers that the device can support. *

* Class contains metadata related to this value including the display name, description and URL to find out additional * information. Metadata can be used by UI developers to provide users with more information about the meaning and * intended use of this value. Access metadata like: {@code value.getDescription();} and {@code value.getUrl();}. *

* Objects of this class should not be created directly as they are part of the internal logic. Use the relevant {@link * Dataset} method to access these objects. *

* For more information see: * 51Degrees pattern data model. */ public class Value extends BaseEntity { /** * The value as an integer. Integer object instead of integer primitive because of the nullable requirement. */ private volatile Integer asInt; /** * The length in bytes of the value record in the data file. */ public static final int RECORD_LENGTH = (4 * 3) + 2; /** * Used to find profiles with values that include this one. */ private static final SearchValues valuesIndexSearch = new SearchValues(); /** * @return The name of the value as string. * @throws IOException if there was a problem accessing data file. */ @SuppressWarnings("DoubleCheckedLocking") public String getName() throws IOException { String localName = name; if (localName == null) { synchronized (this) { localName = name; if (localName == null) { name = localName = getDataSet().strings.get(nameIndex).toString(); } } } return localName; } private volatile String name; private final int nameIndex; /** * @return array containing the {@link Signature signatures} that the value * is associated with. * @throws IOException if there was a problem accessing data file. */ @SuppressWarnings("DoubleCheckedLocking") public Signature[] getSignatures() throws IOException { Signature[] localSignatures = signatures; if (localSignatures == null) { synchronized (this) { localSignatures = signatures; if (localSignatures == null) { signatures = localSignatures = doGetSignatures(); } } } return localSignatures; } @SuppressWarnings("VolatileArrayField") private volatile Signature[] signatures; /** * @return List containing the {@link Profile profiles} this value is associated with. * @throws IOException if there was a problem accessing data file. */ public List getProfiles() throws IOException { ArrayList list = new ArrayList(); for (int profileIndex : getProfileIndexes()) { list.add(dataSet.profiles.get(profileIndex)); } return Collections.unmodifiableList(list); } @SuppressWarnings("DoubleCheckedLocking") public List getProfileIndexes() throws IOException { List localProfileIndexes = profileIndexes; if (localProfileIndexes == null) { synchronized (this) { localProfileIndexes = profileIndexes; if (localProfileIndexes == null) { profileIndexes = localProfileIndexes = doGetProfileIndexes(); } } } return localProfileIndexes; } /** * Used by {@link Property} to set profile indexes. This method is quicker * than using {@code getProfileIndexes} for each of the * {@link Property Property's} {@link Value Values}. * @param profileIndexesToSet List of {@link Profile} indexes. */ protected void setProfileIndexes(List profileIndexesToSet) { List localProfileIndexes = profileIndexes; if (localProfileIndexes == null) { synchronized (this) { localProfileIndexes = profileIndexes; if (localProfileIndexes == null) { localProfileIndexes = new ArrayList(); for (Integer profileIndex : profileIndexesToSet) { localProfileIndexes.add(profileIndex); } profileIndexes = localProfileIndexes; } } } } @SuppressWarnings("VolatileArrayField") private volatile List profileIndexes; /** * @return The {@link Property} the value relates to. * @throws IOException if there was a problem accessing data file. */ @SuppressWarnings("DoubleCheckedLocking") public Property getProperty() throws IOException { Property localProperty = property; if (localProperty == null) { synchronized (this) { localProperty = property; if (localProperty == null) { property = localProperty = getDataSet().getProperties().get(propertyIndex); } } } return localProperty; } private volatile Property property; final int propertyIndex; /** * @return The {@link Component} the value relates to. * @throws IOException if there was a problem accessing data file. */ public Component getComponent() throws IOException { return getProperty().getComponent(); } /** * @return a description of the value suitable to be displayed to end users via a user interface. * @throws IOException if there was a problem accessing data file. */ @SuppressWarnings("DoubleCheckedLocking") public String getDescription() throws IOException { String localDescription = description; if (descriptionIndex >= 0 && localDescription == null) { synchronized (this) { localDescription = description; if (localDescription == null) { description = localDescription = getDataSet().strings .get(descriptionIndex).toString(); } } } return localDescription; } private volatile String description; private final int descriptionIndex; /** * @return A URL to more information about the value if present. * @throws IOException if there was a problem accessing data file. */ @SuppressWarnings("DoubleCheckedLocking") public URL getUrl() throws IOException { URL localUrl = url; if (urlIndex >= 0 && localUrl == null) { synchronized (this) { localUrl = url; if (localUrl == null) { url = localUrl = new URL(getDataSet().strings.get(urlIndex) .toString()); } } } return localUrl; } private volatile URL url; private final int urlIndex; /** * Constructs a new instance of Value. * * @param dataSet the {@link Dataset} the value is contained within. * @param index the index in the data structure to the value. * @param reader Reader connected to the source data structure and positioned to start reading. */ public Value(Dataset dataSet, int index, BinaryReader reader) { super(dataSet, index); this.propertyIndex = reader.readInt16(); this.nameIndex = reader.readInt32(); this.descriptionIndex = reader.readInt32(); this.urlIndex = reader.readInt32(); } /** * Constructs a new instance of Value. * * @param dataSet the {@link Dataset} the value is contained within. * @param property the dynamic value will relate to. * @param value string name of the dynamic value. */ public Value(Dataset dataSet, Property property, String value) { super (dataSet, -1); this.propertyIndex = property.index; this.name = value; this.nameIndex = -1; this.descriptionIndex = -1; this.urlIndex = -1; } /** * Called after the entire data set has been loaded to ensure any further initialisation steps that require other * items in the data set can be completed. The Profiles and Signatures are not initialised as they are very rarely * used and take a long time to initialise. *

* This method should not be called as it is part of the internal logic. * * @throws IOException if there was a problem accessing data file. */ public void init() throws IOException { name = getDataSet().strings.get(nameIndex).toString(); property = getDataSet().getProperties().get(propertyIndex); if (descriptionIndex >= 0) { description = getDataSet().strings.get(descriptionIndex) .toString(); } if (urlIndex >= 0) { try { url = new URL(getDataSet().strings.get(urlIndex) .toString()); } catch (MalformedURLException e) { url = null; } } } /** * Compares with another Value instance by index. * * @param obj type of Value to compare against * @return true if of type Value and equal, otherwise false. */ @Override public boolean equals(Object obj) { if (this==obj) return true; if (obj == null) return false; if (this.getClass() != obj.getClass()) return false; return index == ((Value)obj).index; } /** * Compares two values by name. * * @param value name of value to compare the current value against. * @return integer representing difference. */ public int compareTo(String value) { try { return getName().compareTo(value); } catch (IOException ex) { throw new WrappedIOException(ex.getMessage()); } } /** * Compares this value to another using the index field if they're in the same list other wise the name value. * * @param other The value to be compared against. * @return Indication of relative value based on index field. */ public int compareTo(Value other) { if (getDataSet() == other.getDataSet()) { return getIndex() - other.getIndex(); } try { return getName().compareTo(other.getName()); } catch (IOException ex) { throw new WrappedIOException(ex.getMessage()); } } /** * @return the value name as a string. */ @Override public String toString() { try { return getName(); } catch (IOException e) { return super.toString(); } } /** * @return Returns the value as a number. * @throws IOException if there was a problem accessing data file. */ @SuppressWarnings("DoubleCheckedLocking") public double toDouble() throws IOException { Double localAsNumber = asNumber; if (localAsNumber == null) { synchronized (this) { localAsNumber = asNumber; if (localAsNumber == null) { try { asNumber = localAsNumber = Double.parseDouble(getName()); } catch (NumberFormatException e) { if (this != getProperty().getDefaultValue()) { asNumber = localAsNumber = getProperty().getDefaultValue().toDouble(); } else { asNumber = localAsNumber = (double) 0; } } } } } return (double) localAsNumber; } private volatile Double asNumber; /** * @return Returns the value as a boolean. * @throws IOException if there was a problem accessing data file. */ @SuppressWarnings("DoubleCheckedLocking") public boolean toBool() throws IOException { Boolean localAsBool = asBool; if (localAsBool == null) { synchronized (this) { localAsBool = asBool; if (localAsBool == null) { asBool = localAsBool = Boolean.parseBoolean(getName()); } } } return (boolean) localAsBool; } private volatile Boolean asBool; /** * Returns true if the value is the null value for the property. If the property has no null value false is always * returned. * * @return true if the value is the null value for the property. If the property has no null value false is always * returned. * @throws IOException if there was a problem accessing data file. */ public boolean getIsDefault() throws IOException { Value defaultValue = property.getDefaultValue(); if (defaultValue != null) { return this.getName().equals(defaultValue.getName()); } return false; } /** * Returns the value as an integer. * * @return If the value can not convert to an integer and the value is not equal to the null value then the null * value for the property will be used. If no conversion is possible 0 is returned. * @throws java.io.IOException if there was a problem accessing data file. */ @SuppressWarnings("DoubleCheckedLocking") public int toInt() throws IOException { Integer localAsInt = asInt; if (localAsInt == null) { synchronized (this) { localAsInt = asInt; if (localAsInt == null) { Double d = toDouble(); asInt = localAsInt = d.intValue(); } } } return localAsInt; } // private List doGetProfileIndexes() throws IOException { List list = new ArrayList(); for (Profile profile : getComponent().getProfiles()) { if (valuesIndexSearch.binarySearch( profile.getValues(), getIndex()) >= 0) { list.add(profile.index); } } return list; } /** * Gets all the signatures associated with the value. * * @return Returns the signatures associated with the value * @throws IOException if there was a problem accessing data file. */ private Signature[] doGetSignatures() throws IOException { // Get a distinct list of signature indexes. List list = new ArrayList(); for (Profile profile : getProfiles()) { for (Integer signatureIndex : profile.getSignatureIndexes()) { int localIndex = Collections.binarySearch(list, signatureIndex); if (localIndex < 0) { list.add(~localIndex, signatureIndex); } } } // Turn that list into an array of signatures. Signature[] result = new Signature[list.size()]; for (int i = 0; i < result.length; i++) { result[i] = getDataSet().getSignatures().get(list.get(i)); } return result; } // // /** * Provides access to binary search and overrides the compareTo method. */ private static class SearchValues extends SearchArrays { @Override public int compareTo(Value item, Integer key) { return item.compareTo(key); } } // int[] toIntArray(List list) { int[] ret = new int[list.size()]; int i = 0; for (Integer e : list) ret[i++] = e.intValue(); return ret; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy