diff --git a/CTIC_README b/CTIC_README
new file mode 100644
index 0000000..e08af46
--- /dev/null
+++ b/CTIC_README
@@ -0,0 +1,60 @@
+Performance Requirements
+During the use of OpenDDR in a real environment a set of performance requirements has been detected:
+1 - High-load environments need a high number of device identifications per second.
+2 - In real browsing situations, the same User-Agent string is used repeatedly.
+
+To improve the performance of OpenDDR in these situations the effort was focused in the addition of Cache mechanisms and the optimization in the use of Regular Expressions.
+
+
+Regular Expressions Optimization.
+
+This task is accomplished in the org.openddr.simpleapi.oddr.builder.vitamineddevice package.
+The package implements some computational optimizations for the original OpenDDR device builders. These device builders make intensive use of regular expressions.
+
+User Agent Strings are matched with Strings tokens by the use of two kinds of sentences:
+Case A - java.util.String.matches();
+Example - userAgent.getCompleteUserAgent().matches(".*" + token + ".*");
+
+Case B - java.util.regex.Pattern + java.util.regex.Matcher
+Example - Pattern loosePattern = Pattern.compile("(?i).*" + looseToken + ".*");
+ loosePattern.matcher(userAgent.getCompleteUserAgent()).matches());
+
+Both approaches are similar but inefficient because of the high number of "Pattern.compile" calls (implicit calls in Case A as can be shown in [1] and [2]).
+The idea is to avoid the use of String.matches and to precompile the regex Patterns as far as possible. A new Token class is implemented in order to reach this optimization. This Token class replaces the token string used in the builders method "elaborateDeviceWithToken" and includes the most frequently used "regex.Patterns" instantiated in the OpenDDR initialization phase. Tokens are stored into the builders and each device identification reuses these "regex.Patterns".
+
+A new version of BuilderDataSource.xml and BuilderDataSourcePatch.xml has been created. These files are equivalent to the original files but with references to the new device builders.
+
+The "VitaminedTwoStepDeviceBuilder.elaborateTwoStepDeviceWithToken" method keeps its original signature and doesn't make use of the Token class because of the lack of performance improvement.
+
+
+
+Cache mechanisms.
+
+In real browsing situations a single request sent from a web browser will generate several requests to the web server or proxy -one for the markup and one for each external resource (CSS, JS, ...)- If identification of these agents for each request can be avoided, a big amount of time will be saved.
+
+In order to improve the performance of the identification process, a set of caches has been used. A new java interface has been created: "org.openddr.simpleapi.oddr.identificator.CachedIdentificator" providing cache support to identificators.
+
+New "identificator" classes has also been generated implementing "CachedIdentificator" interface:
+- org.openddr.simpleapi.oddr.identificator.CachedDeviceIdentificator
+- org.openddr.simpleapi.oddr.identificator.CachedBrowserIdentificator
+- org.openddr.simpleapi.oddr.identificator.CachedIdentifcator
+
+Each of these classes provides two different caches:
+- notFoundUa Cache: LRU Map [3] used to store the User Agent string of unknown devices.
+- object Cache: LRU Map -indexed by User Agent string- used to store the capabilities of previously requested devices.
+
+Three new properties have been added to the oddr.properties files to set the max size of the caches:
+- oddr.cache.device.size: maximum size of devices cache
+- oddr.cache.browser.size: maximum size of browsers cache
+- oddr.cache.os.size: maximum size of operating systems cache
+- oddr.cache.unknownUAs.size: maximum size of unknown devices cache
+
+
+
+Finally, the service "org.openddr.simpleapi.oddr.VitaminedODDRService" has been created cloning "org.openddr.simpleapi.oddr.ODDRService" but using CachedIdentificators instead of OpenDDR default identificators.
+
+
+
+[1] http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#matches(java.lang.String)
+[2] http://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html#matches(java.lang.String, java.lang.CharSequence)
+[3] http://commons.apache.org/collections/apidocs/org/apache/commons/collections/map/LRUMap.html
\ No newline at end of file
diff --git a/lib/commons-collections-3.2.1.jar b/lib/commons-collections-3.2.1.jar
new file mode 100644
index 0000000..c35fa1f
Binary files /dev/null and b/lib/commons-collections-3.2.1.jar differ
diff --git a/src/oddr.properties b/src/oddr.properties
index 8e4ec4f..2d0f07b 100644
--- a/src/oddr.properties
+++ b/src/oddr.properties
@@ -1,6 +1,18 @@
-oddr.ua.device.builder.path=/FILESYSTEM_PATH_TO_RESOURCES/BuilderDataSource.xml
+#oddr.ua.device.builder.path=/FILESYSTEM_PATH_TO_/FILESYSTEM_PATH_TO_RESOURCES/BuilderDataSource.xml
+#oddr.ua.device.datasource.path=/FILESYSTEM_PATH_TO_/FILESYSTEM_PATH_TO_RESOURCES/DeviceDataSource.xml
+#oddr.ua.device.builder.patch.paths=/FILESYSTEM_PATH_TO_/FILESYSTEM_PATH_TO_RESOURCES/BuilderDataSourcePatch.xml
+#oddr.ua.device.datasource.patch.paths=/FILESYSTEM_PATH_TO_/FILESYSTEM_PATH_TO_RESOURCES/DeviceDataSourcePatch.xml
+#oddr.ua.browser.datasource.path=/FILESYSTEM_PATH_TO_/FILESYSTEM_PATH_TO_RESOURCES/BrowserDataSource.xml
+#oddr.ua.operatingSystem.datasource.path=/FILESYSTEM_PATH_TO_/FILESYSTEM_PATH_TO_RESOURCES/OperatingSystemDataSource.xml
+#ddr.vocabulary.core.path=/FILESYSTEM_PATH_TO_/FILESYSTEM_PATH_TO_RESOURCES/coreVocabulary.xml
+#oddr.vocabulary.path=/FILESYSTEM_PATH_TO_/FILESYSTEM_PATH_TO_RESOURCES/oddrVocabulary.xml
+#oddr.limited.vocabulary.path=/FILESYSTEM_PATH_TO_/FILESYSTEM_PATH_TO_RESOURCES/oddrLimitedVocabulary.xml
+#oddr.vocabulary.device=http://www.openddr.org/oddr-vocabulary
+#oddr.threshold=70
+
+oddr.ua.device.builder.path=/FILESYSTEM_PATH_TO_RESOURCES/VitaminedBuilderDataSource.xml
oddr.ua.device.datasource.path=/FILESYSTEM_PATH_TO_RESOURCES/DeviceDataSource.xml
-oddr.ua.device.builder.patch.paths=/FILESYSTEM_PATH_TO_RESOURCES/BuilderDataSourcePatch.xml
+oddr.ua.device.builder.patch.paths=/FILESYSTEM_PATH_TO_RESOURCES/VitaminedBuilderDataSourcePatch.xml
oddr.ua.device.datasource.patch.paths=/FILESYSTEM_PATH_TO_RESOURCES/DeviceDataSourcePatch.xml
oddr.ua.browser.datasource.path=/FILESYSTEM_PATH_TO_RESOURCES/BrowserDataSource.xml
oddr.ua.operatingSystem.datasource.path=/FILESYSTEM_PATH_TO_RESOURCES/OperatingSystemDataSource.xml
@@ -8,4 +20,9 @@ ddr.vocabulary.core.path=/FILESYSTEM_PATH_TO_RESOURCES/coreVocabulary.xml
oddr.vocabulary.path=/FILESYSTEM_PATH_TO_RESOURCES/oddrVocabulary.xml
oddr.limited.vocabulary.path=/FILESYSTEM_PATH_TO_RESOURCES/oddrLimitedVocabulary.xml
oddr.vocabulary.device=http://www.openddr.org/oddr-vocabulary
-oddr.threshold=70
\ No newline at end of file
+oddr.threshold=70
+
+oddr.cache.device.size= 5000
+oddr.cache.browser.size= 5000
+oddr.cache.os.size= 5000
+oddr.cache.unknownUAs.size= 500
\ No newline at end of file
diff --git a/src/org/openddr/simpleapi/oddr/VitaminedODDRService.java b/src/org/openddr/simpleapi/oddr/VitaminedODDRService.java
new file mode 100644
index 0000000..f240f5f
--- /dev/null
+++ b/src/org/openddr/simpleapi/oddr/VitaminedODDRService.java
@@ -0,0 +1,917 @@
+/**
+ * Copyright 2012 Fundación CTIC
+ * This software is distributed under the terms of the GNU Lesser General Public License.
+ *
+ *
+ * This file is part of OpenDDR Simple APIs.
+ * OpenDDR Simple APIs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * OpenDDR Simple APIs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Simple APIs. If not, see .
+ *
+ * @author José Quiroga Álvarez
+ * @author Diego Martínez Ballesteros
+ *
+ */
+package org.openddr.simpleapi.oddr;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.commons.jexl2.Expression;
+import org.apache.commons.jexl2.JexlContext;
+import org.apache.commons.jexl2.JexlEngine;
+import org.apache.commons.jexl2.MapContext;
+import org.openddr.simpleapi.oddr.builder.Builder;
+import org.openddr.simpleapi.oddr.builder.browser.DefaultBrowserBuilder;
+import org.openddr.simpleapi.oddr.builder.device.DeviceBuilder;
+import org.openddr.simpleapi.oddr.builder.os.DefaultOSBuilder;
+import org.openddr.simpleapi.oddr.documenthandler.BrowserDatasourceHandler;
+import org.openddr.simpleapi.oddr.documenthandler.DeviceBuilderHandler;
+import org.openddr.simpleapi.oddr.documenthandler.DeviceDatasourceHandler;
+import org.openddr.simpleapi.oddr.documenthandler.OperatingSystemDatasourceHandler;
+import org.openddr.simpleapi.oddr.identificator.CachedBrowserIdentificator;
+import org.openddr.simpleapi.oddr.identificator.CachedDeviceIdentificator;
+import org.openddr.simpleapi.oddr.identificator.CachedOSIdentificator;
+import org.openddr.simpleapi.oddr.model.BufferedODDRHTTPEvidence;
+import org.openddr.simpleapi.oddr.model.ODDRHTTPEvidence;
+import org.openddr.simpleapi.oddr.model.ODDRPropertyName;
+import org.openddr.simpleapi.oddr.model.ODDRPropertyRef;
+import org.openddr.simpleapi.oddr.model.ODDRPropertyValue;
+import org.openddr.simpleapi.oddr.model.ODDRPropertyValues;
+import org.openddr.simpleapi.oddr.model.UserAgent;
+import org.openddr.simpleapi.oddr.model.UserAgentFactory;
+import org.openddr.simpleapi.oddr.model.browser.Browser;
+import org.openddr.simpleapi.oddr.model.device.Device;
+import org.openddr.simpleapi.oddr.model.os.OperatingSystem;
+import org.openddr.simpleapi.oddr.model.vocabulary.Vocabulary;
+import org.openddr.simpleapi.oddr.model.vocabulary.VocabularyProperty;
+import org.openddr.simpleapi.oddr.vocabulary.VocabularyHolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.ddr.simple.Evidence;
+import org.w3c.ddr.simple.PropertyName;
+import org.w3c.ddr.simple.PropertyRef;
+import org.w3c.ddr.simple.PropertyValue;
+import org.w3c.ddr.simple.PropertyValues;
+import org.w3c.ddr.simple.Service;
+import org.w3c.ddr.simple.exception.InitializationException;
+import org.w3c.ddr.simple.exception.NameException;
+import org.xml.sax.SAXException;
+
+public class VitaminedODDRService implements Service {
+
+ public static final String ASPECT_DEVICE = "device";
+ public static final String ASPECT_WEB_BROWSER = "webBrowser";
+ public static final String ASPECT_OPERATIVE_SYSTEM = "operativeSystem";
+ public static final String ASPECT_GROUP = "group";
+ public static final String ODDR_UA_DEVICE_BUILDER_PATH_PROP = "oddr.ua.device.builder.path";
+ public static final String ODDR_UA_DEVICE_DATASOURCE_PATH_PROP = "oddr.ua.device.datasource.path";
+ public static final String ODDR_UA_DEVICE_BUILDER_PATCH_PATHS_PROP = "oddr.ua.device.builder.patch.paths";
+ public static final String ODDR_UA_DEVICE_DATASOURCE_PATCH_PATHS_PROP = "oddr.ua.device.datasource.patch.paths";
+ public static final String ODDR_UA_BROWSER_DATASOURCE_PATH_PROP = "oddr.ua.browser.datasource.path";
+ public static final String ODDR_UA_OPERATINGSYSTEM_DATASOURCE_PATH_PROP = "oddr.ua.operatingSystem.datasource.path";
+ public static final String ODDR_UA_DEVICE_BUILDER_STREAM_PROP = "oddr.ua.device.builder.stream";
+ public static final String ODDR_UA_DEVICE_DATASOURCE_STREAM_PROP = "oddr.ua.device.datasource.stream";
+ public static final String ODDR_UA_DEVICE_BUILDER_PATCH_STREAMS_PROP = "oddr.ua.device.builder.patch.streams";
+ public static final String ODDR_UA_DEVICE_DATASOURCE_PATCH_STREAMS_PROP = "oddr.ua.device.datasource.patch.streams";
+ public static final String ODDR_UA_BROWSER_DATASOURCE_STREAM_PROP = "oddr.ua.browser.datasource.stream";
+ public static final String ODDR_UA_OPERATINGSYSTEM_DATASOURCE_STREAM_PROP = "oddr.ua.operatingSystem.datasource.stream";
+ public static final String ODDR_THRESHOLD_PROP = "oddr.threshold";
+ public static final String ODDR_VOCABULARY_IRI = "oddr.vocabulary.device";
+
+ // cache constants
+ public static final String ODDR_CACHE_DEVICE_SIZE = "oddr.cache.device.size";
+ public static final String ODDR_CACHE_BROWSER_SIZE = "oddr.cache.browser.size";
+ public static final String ODDR_CACHE_OS_SIZE = "oddr.cache.os.size";
+ public static final String ODDR_CACHE_UNKNOWNUAS_SIZE = "oddr.cache.unknownUAs.size";
+
+ private static final String ODDR_API_VERSION = "1.0.0";
+ private static final String ODDR_DATA_VERSION = "2012";
+ private static final int ODDR_DEFAULT_THRESHOLD = 70;
+ private String defaultVocabularyIRI = null;
+ private CachedDeviceIdentificator deviceIdentificator = null;
+ private CachedBrowserIdentificator browserIdentificator = null;
+ private CachedOSIdentificator osIdentificator = null;
+ private VocabularyHolder vocabularyHolder = null;
+ private int threshold = ODDR_DEFAULT_THRESHOLD;
+ private static final String GROUP_REGEXPR = "\\$([^ ]+)";
+ private Pattern groupRegexprPattern = Pattern.compile(GROUP_REGEXPR);
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ public void initialize(String defaultVocabularyIRI, Properties prprts) throws NameException, InitializationException {
+ if (defaultVocabularyIRI == null || defaultVocabularyIRI.trim().length() == 0) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new NullPointerException("defaultVocabularyIRI can not be null"));
+ }
+
+ /*Initializing VocabularyHolder*/
+ ODDRVocabularyService oddrVocabularyService = new ODDRVocabularyService();
+ oddrVocabularyService.initialize(prprts);
+
+ vocabularyHolder = oddrVocabularyService.getVocabularyHolder();
+ vocabularyHolder.existVocabulary(defaultVocabularyIRI);
+
+ String oddrUaDeviceBuilderPath = prprts.getProperty(ODDR_UA_DEVICE_BUILDER_PATH_PROP);
+ String oddrUaDeviceDatasourcePath = prprts.getProperty(ODDR_UA_DEVICE_DATASOURCE_PATH_PROP);
+ String oddrUaDeviceBuilderPatchPaths = prprts.getProperty(ODDR_UA_DEVICE_BUILDER_PATCH_PATHS_PROP);
+ String oddrUaDeviceDatasourcePatchPaths = prprts.getProperty(ODDR_UA_DEVICE_DATASOURCE_PATCH_PATHS_PROP);
+ String oddrUaBrowserDatasourcePaths = prprts.getProperty(ODDR_UA_BROWSER_DATASOURCE_PATH_PROP);
+ String oddrUaOperatingSystemDatasourcePaths = prprts.getProperty(ODDR_UA_OPERATINGSYSTEM_DATASOURCE_PATH_PROP);
+
+ Integer maxDeviceCache= new Integer(prprts.getProperty(ODDR_CACHE_DEVICE_SIZE));
+ Integer maxBrowserCache= new Integer(prprts.getProperty(ODDR_CACHE_BROWSER_SIZE));
+ Integer maxOsCache= new Integer(prprts.getProperty(ODDR_CACHE_OS_SIZE));
+ Integer maxUnknownCache= new Integer(prprts.getProperty(ODDR_CACHE_UNKNOWNUAS_SIZE));
+
+
+ InputStream oddrUaDeviceBuilderStream = null;
+ InputStream oddrUaDeviceDatasourceStream = null;
+ InputStream[] oddrUaDeviceBuilderPatchStreams = null;
+ InputStream[] oddrUaDeviceDatasourcePatchStreams = null;
+ InputStream oddrUaBrowserDatasourceStream = null;
+ InputStream oddrUaOperatingSystemDatasourceStream = null;
+
+ try {
+ oddrUaDeviceBuilderStream = (InputStream) prprts.get(ODDR_UA_DEVICE_BUILDER_STREAM_PROP);
+ } catch (Exception ex) {
+ oddrUaDeviceBuilderStream = null;
+ }
+ try {
+ oddrUaDeviceDatasourceStream = (InputStream) prprts.get(ODDR_UA_DEVICE_DATASOURCE_STREAM_PROP);
+ } catch (Exception ex) {
+ oddrUaDeviceDatasourceStream = null;
+ }
+ try {
+ oddrUaDeviceBuilderPatchStreams = (InputStream[]) prprts.get(ODDR_UA_DEVICE_BUILDER_PATCH_STREAMS_PROP);
+ } catch (Exception ex) {
+ oddrUaDeviceBuilderPatchStreams = null;
+ }
+ try {
+ oddrUaDeviceDatasourcePatchStreams = (InputStream[]) prprts.get(ODDR_UA_DEVICE_DATASOURCE_PATCH_STREAMS_PROP);
+ } catch (Exception ex) {
+ oddrUaDeviceDatasourcePatchStreams = null;
+ }
+ try {
+ oddrUaBrowserDatasourceStream = (InputStream) prprts.get(ODDR_UA_BROWSER_DATASOURCE_STREAM_PROP);
+ } catch (Exception ex) {
+ oddrUaBrowserDatasourceStream = null;
+ }
+ try {
+ oddrUaOperatingSystemDatasourceStream = (InputStream) prprts.get(ODDR_UA_OPERATINGSYSTEM_DATASOURCE_STREAM_PROP);
+ } catch (Exception ex) {
+ oddrUaOperatingSystemDatasourceStream = null;
+ }
+
+ String oddrThreshold = prprts.getProperty(ODDR_THRESHOLD_PROP);
+
+ if ((oddrUaDeviceBuilderPath == null || oddrUaDeviceBuilderPath.trim().length() == 0) && oddrUaDeviceBuilderStream == null) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalArgumentException("Can not find property " + ODDR_UA_DEVICE_BUILDER_PATH_PROP));
+ }
+
+ if ((oddrUaDeviceDatasourcePath == null || oddrUaDeviceDatasourcePath.trim().length() == 0) && oddrUaDeviceDatasourceStream == null) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalArgumentException("Can not find property " + ODDR_UA_DEVICE_DATASOURCE_PATH_PROP));
+ }
+
+ String oddrUaDeviceBuilderPatchPathArray[] = null;
+
+ if (oddrUaDeviceBuilderPatchPaths != null && oddrUaDeviceBuilderPatchPaths.trim().length() != 0) {
+ oddrUaDeviceBuilderPatchPathArray = oddrUaDeviceBuilderPatchPaths.split(",");
+
+ } else {
+ oddrUaDeviceBuilderPatchPathArray = new String[0];
+ }
+
+ String ooddrUaDeviceDatasourcePatchPathArray[] = null;
+
+ if (oddrUaDeviceDatasourcePatchPaths != null && oddrUaDeviceDatasourcePatchPaths.trim().length() != 0) {
+ ooddrUaDeviceDatasourcePatchPathArray = oddrUaDeviceDatasourcePatchPaths.split(",");
+
+ } else {
+ ooddrUaDeviceDatasourcePatchPathArray = new String[0];
+ }
+
+ if (oddrUaDeviceBuilderPatchStreams == null) {
+ oddrUaDeviceBuilderPatchStreams = new InputStream[0];
+ }
+
+ if (oddrUaDeviceDatasourcePatchStreams == null) {
+ oddrUaDeviceDatasourcePatchStreams = new InputStream[0];
+ }
+
+ if ((oddrUaBrowserDatasourcePaths == null || oddrUaBrowserDatasourcePaths.trim().length() == 0) && oddrUaBrowserDatasourceStream == null) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalArgumentException("Can not find property " + ODDR_UA_BROWSER_DATASOURCE_PATH_PROP));
+ }
+
+ if ((oddrUaOperatingSystemDatasourcePaths == null || oddrUaOperatingSystemDatasourcePaths.trim().length() == 0) && oddrUaOperatingSystemDatasourceStream == null) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalArgumentException("Can not find property " + ODDR_UA_OPERATINGSYSTEM_DATASOURCE_PATH_PROP));
+ }
+
+ if (oddrThreshold == null || oddrThreshold.trim().length() == 0) {
+ this.threshold = ODDR_DEFAULT_THRESHOLD;
+
+ } else {
+ try {
+ this.threshold = Integer.parseInt(oddrThreshold);
+ if (this.threshold <= 0) {
+ this.threshold = ODDR_DEFAULT_THRESHOLD;
+ }
+
+ } catch (NumberFormatException x) {
+ this.threshold = ODDR_DEFAULT_THRESHOLD;
+ }
+ }
+
+ Map devices = new HashMap();
+ DeviceDatasourceHandler deviceDatasourceHandler = new DeviceDatasourceHandler(devices, vocabularyHolder);
+
+ InputStream stream = null;
+ SAXParser parser = null;
+
+ try {
+ if (oddrUaDeviceDatasourceStream != null) {
+ stream = oddrUaDeviceDatasourceStream;
+ } else {
+ stream = new FileInputStream(new File(oddrUaDeviceDatasourcePath));
+ }
+
+ } catch (IOException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalArgumentException("Can not open " + ODDR_UA_DEVICE_DATASOURCE_PATH_PROP + " " + oddrUaDeviceDatasourcePath));
+ }
+
+ try {
+ parser = SAXParserFactory.newInstance().newSAXParser();
+
+ } catch (ParserConfigurationException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalStateException("Can not instantiate SAXParserFactory.newInstance().newSAXParser()"));
+
+ } catch (SAXException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalStateException("Can not instantiate SAXParserFactory.newInstance().newSAXParser()"));
+ }
+
+ try {
+ parser.parse(stream, deviceDatasourceHandler);
+
+ } catch (SAXException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new RuntimeException("Can not parse document: " + oddrUaDeviceDatasourcePath));
+
+ } catch (IOException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new RuntimeException("Can not open " + ODDR_UA_DEVICE_DATASOURCE_PATH_PROP + " :" + oddrUaDeviceDatasourcePath));
+ }
+
+ try {
+ stream.close();
+
+ } catch (IOException ex) {
+ logger.warn("", ex);
+ }
+
+ deviceDatasourceHandler.setPatching(true);
+
+ if (oddrUaDeviceDatasourcePatchStreams != null) {
+ for (int i = 0; i < oddrUaDeviceDatasourcePatchStreams.length; i++) {
+ stream = oddrUaDeviceDatasourcePatchStreams[i];
+
+ try {
+ parser = SAXParserFactory.newInstance().newSAXParser();
+
+ } catch (ParserConfigurationException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalStateException("Can not instantiate SAXParserFactory.newInstance().newSAXParser()"));
+
+ } catch (SAXException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalStateException("Can not instantiate SAXParserFactory.newInstance().newSAXParser()"));
+ }
+
+ try {
+ parser.parse(stream, deviceDatasourceHandler);
+
+ } catch (Exception ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new RuntimeException("Can not parse DeviceDatasource input stream " + i));
+ }
+
+ try {
+ stream.close();
+
+ } catch (IOException ex) {
+ logger.warn("", ex);
+ }
+ }
+ } else {
+ for (int i = 0; i < ooddrUaDeviceDatasourcePatchPathArray.length; i++) {
+ try {
+ stream = new FileInputStream(new File(ooddrUaDeviceDatasourcePatchPathArray[i]));
+
+ } catch (IOException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalArgumentException("Can not open " + ODDR_UA_DEVICE_DATASOURCE_PATH_PROP + " " + ooddrUaDeviceDatasourcePatchPathArray[i]));
+ }
+
+ try {
+ parser = SAXParserFactory.newInstance().newSAXParser();
+
+ } catch (ParserConfigurationException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalStateException("Can not instantiate SAXParserFactory.newInstance().newSAXParser()"));
+
+ } catch (SAXException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalStateException("Can not instantiate SAXParserFactory.newInstance().newSAXParser()"));
+ }
+
+ try {
+ parser.parse(stream, deviceDatasourceHandler);
+
+ } catch (SAXException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new RuntimeException("Can not parse document: " + ooddrUaDeviceDatasourcePatchPathArray[i]));
+
+ } catch (IOException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new RuntimeException("Can not open " + ODDR_UA_DEVICE_DATASOURCE_PATH_PROP + " :" + ooddrUaDeviceDatasourcePatchPathArray[i]));
+ }
+
+ try {
+ stream.close();
+
+ } catch (IOException ex) {
+ logger.warn("", ex);
+ }
+ }
+
+ }
+
+ List builders = new ArrayList();
+ DeviceBuilderHandler deviceBuilderHandler = new DeviceBuilderHandler(builders);
+
+ try {
+ if (oddrUaDeviceBuilderStream != null) {
+ stream = oddrUaDeviceBuilderStream;
+ } else {
+ stream = new FileInputStream(new File(oddrUaDeviceBuilderPath));
+ }
+
+ } catch (IOException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalArgumentException("Can not open " + ODDR_UA_DEVICE_BUILDER_PATH_PROP + " " + oddrUaDeviceBuilderPath));
+ }
+
+ try {
+ parser = SAXParserFactory.newInstance().newSAXParser();
+
+ } catch (ParserConfigurationException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalStateException("Can not instantiate SAXParserFactory.newInstance().newSAXParser()"));
+
+ } catch (SAXException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalStateException("Can not instantiate SAXParserFactory.newInstance().newSAXParser()"));
+ }
+
+ try {
+ parser.parse(stream, deviceBuilderHandler);
+
+ } catch (SAXException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new RuntimeException("Can not parse document: " + oddrUaDeviceBuilderPath));
+
+ } catch (IOException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new RuntimeException("Can not open " + ODDR_UA_DEVICE_DATASOURCE_PATH_PROP + " :" + oddrUaDeviceBuilderPath));
+ }
+
+ try {
+ stream.close();
+
+ } catch (IOException ex) {
+ logger.warn("", ex);
+ }
+
+ if (oddrUaDeviceBuilderPatchStreams != null) {
+ for (int i = 0; i < oddrUaDeviceBuilderPatchStreams.length; i++) {
+ stream = oddrUaDeviceBuilderPatchStreams[i];
+
+ try {
+ parser = SAXParserFactory.newInstance().newSAXParser();
+
+ } catch (ParserConfigurationException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalStateException("Can not instantiate SAXParserFactory.newInstance().newSAXParser()"));
+
+ } catch (SAXException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalStateException("Can not instantiate SAXParserFactory.newInstance().newSAXParser()"));
+ }
+
+ try {
+ parser.parse(stream, deviceBuilderHandler);
+
+ } catch (Exception ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new RuntimeException("Can not parse DeviceBuilder input stream " + i));
+ }
+
+ try {
+ stream.close();
+
+ } catch (IOException ex) {
+ logger.warn("", ex);
+ }
+ }
+
+ } else {
+ for (int i = 0; i < oddrUaDeviceBuilderPatchPathArray.length; i++) {
+ try {
+ stream = new FileInputStream(new File(oddrUaDeviceBuilderPatchPathArray[i]));
+
+ } catch (IOException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalArgumentException("Can not open " + ODDR_UA_DEVICE_BUILDER_PATCH_PATHS_PROP + " " + oddrUaDeviceBuilderPatchPathArray[i]));
+ }
+
+ try {
+ parser = SAXParserFactory.newInstance().newSAXParser();
+
+ } catch (ParserConfigurationException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalStateException("Can not instantiate SAXParserFactory.newInstance().newSAXParser()"));
+
+ } catch (SAXException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalStateException("Can not instantiate SAXParserFactory.newInstance().newSAXParser()"));
+ }
+
+ try {
+ parser.parse(stream, deviceBuilderHandler);
+
+ } catch (SAXException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new RuntimeException("Can not parse document: " + oddrUaDeviceBuilderPatchPathArray[i]));
+
+ } catch (IOException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new RuntimeException("Can not open " + ODDR_UA_DEVICE_DATASOURCE_PATH_PROP + " :" + oddrUaDeviceBuilderPatchPathArray[i]));
+ }
+
+ try {
+ stream.close();
+
+ } catch (IOException ex) {
+ logger.warn("", ex);
+ }
+ }
+ }
+
+ BrowserDatasourceHandler browserDatasourceHandler = new BrowserDatasourceHandler(vocabularyHolder);
+
+ try {
+ if (oddrUaBrowserDatasourceStream != null) {
+ stream = oddrUaBrowserDatasourceStream;
+ } else {
+ stream = new FileInputStream(new File(oddrUaBrowserDatasourcePaths));
+ }
+
+ } catch (IOException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalArgumentException("Can not open " + ODDR_UA_BROWSER_DATASOURCE_PATH_PROP + " " + oddrUaBrowserDatasourcePaths));
+ }
+
+ try {
+ parser = SAXParserFactory.newInstance().newSAXParser();
+
+ } catch (ParserConfigurationException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalStateException("Can not instantiate SAXParserFactory.newInstance().newSAXParser()"));
+
+ } catch (SAXException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalStateException("Can not instantiate SAXParserFactory.newInstance().newSAXParser()"));
+ }
+
+ try {
+ parser.parse(stream, browserDatasourceHandler);
+
+ } catch (SAXException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new RuntimeException("Can not parse document: " + oddrUaBrowserDatasourcePaths));
+
+ } catch (IOException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new RuntimeException("Can not open " + ODDR_UA_BROWSER_DATASOURCE_PATH_PROP + " :" + oddrUaBrowserDatasourcePaths));
+ }
+
+ try {
+ stream.close();
+
+ } catch (IOException ex) {
+ logger.warn("", ex);
+ }
+
+ OperatingSystemDatasourceHandler operatingSystemDatasourceHandler = new OperatingSystemDatasourceHandler(vocabularyHolder);
+
+ try {
+ if (oddrUaOperatingSystemDatasourceStream != null) {
+ stream = oddrUaOperatingSystemDatasourceStream;
+ } else {
+ stream = new FileInputStream(new File(oddrUaOperatingSystemDatasourcePaths));
+ }
+
+ } catch (IOException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalArgumentException("Can not open " + ODDR_UA_OPERATINGSYSTEM_DATASOURCE_PATH_PROP + " " + oddrUaOperatingSystemDatasourcePaths));
+ }
+
+ try {
+ parser = SAXParserFactory.newInstance().newSAXParser();
+
+ } catch (ParserConfigurationException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalStateException("Can not instantiate SAXParserFactory.newInstance().newSAXParser()"));
+
+ } catch (SAXException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalStateException("Can not instantiate SAXParserFactory.newInstance().newSAXParser()"));
+ }
+
+ try {
+ parser.parse(stream, operatingSystemDatasourceHandler);
+
+ } catch (SAXException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new RuntimeException("Can not parse document: " + oddrUaOperatingSystemDatasourcePaths));
+
+ } catch (IOException ex) {
+ throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new RuntimeException("Can not open " + ODDR_UA_OPERATINGSYSTEM_DATASOURCE_PATH_PROP + " :" + oddrUaOperatingSystemDatasourcePaths));
+ }
+
+ try {
+ stream.close();
+
+ } catch (IOException ex) {
+ logger.warn("", ex);
+ }
+
+ deviceIdentificator = new CachedDeviceIdentificator(deviceBuilderHandler.getBuilders(), deviceDatasourceHandler.getDevices(), maxDeviceCache, maxUnknownCache);
+ deviceIdentificator.completeInit();
+
+ browserIdentificator = new CachedBrowserIdentificator(new Builder[]{DefaultBrowserBuilder.getInstance()}, browserDatasourceHandler.getBrowsers(), maxBrowserCache, maxUnknownCache);
+ browserIdentificator.completeInit();
+
+ osIdentificator = new CachedOSIdentificator(new Builder[]{DefaultOSBuilder.getInstance()}, operatingSystemDatasourceHandler.getOperatingSystems(), maxOsCache, maxUnknownCache);
+ osIdentificator.completeInit();
+
+ deviceDatasourceHandler = null;
+ deviceBuilderHandler = null;
+ browserDatasourceHandler = null;
+ operatingSystemDatasourceHandler = null;
+
+ this.defaultVocabularyIRI = defaultVocabularyIRI;
+
+ parser = null;
+
+ oddrVocabularyService = null;
+
+ return;
+ }
+
+ public String getAPIVersion() {
+ return ODDR_API_VERSION;
+ }
+
+ public String getDataVersion() {
+ return ODDR_DATA_VERSION;
+ }
+
+ public PropertyRef[] listPropertyRefs() {
+ List propertyRefsList = new ArrayList();
+ Map vocabularies = vocabularyHolder.getVocabularies();
+ Set vocabularyKeys = vocabularies.keySet();
+
+ for (String vocabularyKey : vocabularyKeys) {
+ Vocabulary vocabulary = vocabularies.get(vocabularyKey);
+ Map properties = vocabulary.getProperties();
+ Set propertyKeys = properties.keySet();
+ for (String propertyKey : propertyKeys) {
+ PropertyName propertyName = new ODDRPropertyName(propertyKey, vocabularyKey);
+ for (int i = 0; i < properties.get(propertyKey).getAspects().length; i++) {
+ PropertyRef propertyRef = new ODDRPropertyRef(propertyName, properties.get(propertyKey).getAspects()[i]);
+ propertyRefsList.add(propertyRef);
+ }
+ }
+ }
+
+ PropertyRef[] propertyRefs = new PropertyRef[propertyRefsList.size()];
+ propertyRefs = propertyRefsList.toArray(propertyRefs);
+
+ return propertyRefs;
+ }
+
+ public PropertyValue getPropertyValue(Evidence evdnc, PropertyRef pr) throws NameException {
+ return getPropertyValues(evdnc, new PropertyRef[]{pr}).getValue(pr);
+ }
+
+ public PropertyValue getPropertyValue(Evidence evdnc, String localPropertyName, String localAspectName, String vocabularyIRI) throws NameException {
+ return getPropertyValue(evdnc, newPropertyRef(newPropertyName(localPropertyName, vocabularyIRI), localAspectName));
+ }
+
+ public PropertyValue getPropertyValue(Evidence evdnc, PropertyName pn) throws NameException {
+ return getPropertyValue(evdnc, newPropertyRef(pn));
+ }
+
+ public PropertyValue getPropertyValue(Evidence evdnc, String localPropertyName) throws NameException {
+ return getPropertyValue(evdnc, newPropertyName(localPropertyName));
+ }
+
+ public PropertyValues getPropertyValues(Evidence evdnc) {
+ Device deviceFound = null;
+ Browser browserFound = null;
+ OperatingSystem osFound = null;
+ boolean deviceIdentified = false;
+ boolean browserIdentified = false;
+ boolean osIdentified = false;
+ UserAgent deviceUA = null;
+ UserAgent browserUA = null;
+
+ JexlEngine jexl = new JexlEngine();
+ ODDRPropertyValues ret = new ODDRPropertyValues();
+ Map vocabularies = vocabularyHolder.getVocabularies();
+ Set vocabularyKeys = vocabularies.keySet();
+
+ for (String vocabularyKey : vocabularyKeys) {
+ Vocabulary vocabulary = vocabularies.get(vocabularyKey);
+ Map properties = vocabulary.getProperties();
+ Set propertyKeys = properties.keySet();
+ for (String propertyKey : propertyKeys) {
+ PropertyName propertyName = new ODDRPropertyName(propertyKey, vocabularyKey);
+ for (int i = 0; i < properties.get(propertyKey).getAspects().length; i++) {
+ PropertyRef propertyRef = new ODDRPropertyRef(propertyName, properties.get(propertyKey).getAspects()[i]);
+ if (ASPECT_DEVICE.equals(propertyRef.getAspectName())) {
+ if (!deviceIdentified) {
+ if (deviceUA == null) {
+ deviceUA = UserAgentFactory.newDeviceUserAgent(evdnc);
+ }
+ if (evdnc instanceof BufferedODDRHTTPEvidence) {
+ deviceFound = ((BufferedODDRHTTPEvidence) evdnc).getDeviceFound();
+ }
+ if (deviceFound == null) {
+ deviceFound = deviceIdentificator.get(deviceUA, this.threshold);
+ }
+ if (evdnc instanceof BufferedODDRHTTPEvidence) {
+ ((BufferedODDRHTTPEvidence) evdnc).setDeviceFound(deviceFound);
+ }
+ deviceIdentified = true;
+ }
+ String property = null;
+ if (deviceFound != null) {
+ property = deviceFound.get(propertyRef.getLocalPropertyName());
+ ret.addProperty(new ODDRPropertyValue(property, properties.get(propertyKey).getType(), propertyRef));
+
+ } else {
+ ret.addProperty(new ODDRPropertyValue(null, properties.get(propertyKey).getType(), propertyRef));
+ }
+ continue;
+
+ } else if (ASPECT_WEB_BROWSER.equals(propertyRef.getAspectName())) {
+ if (!browserIdentified) {
+ if (browserUA == null) {
+ browserUA = UserAgentFactory.newBrowserUserAgent(evdnc);
+ }
+ if (evdnc instanceof BufferedODDRHTTPEvidence) {
+ browserFound = ((BufferedODDRHTTPEvidence) evdnc).getBrowserFound();
+ }
+ if (browserFound == null) {
+ browserFound = browserIdentificator.get(browserUA, this.threshold);
+ }
+ if (evdnc instanceof BufferedODDRHTTPEvidence) {
+ ((BufferedODDRHTTPEvidence) evdnc).setBrowserFound(browserFound);
+ }
+
+ browserIdentified = true;
+ }
+ String property = null;
+ if (browserFound != null) {
+ property = browserFound.get(propertyRef.getLocalPropertyName());
+ ret.addProperty(new ODDRPropertyValue(property, properties.get(propertyKey).getType(), propertyRef));
+
+ } else {
+ ret.addProperty(new ODDRPropertyValue(null, properties.get(propertyKey).getType(), propertyRef));
+ }
+ continue;
+
+ } else if (ASPECT_OPERATIVE_SYSTEM.equals(propertyRef.getAspectName())) {
+ if (!osIdentified) {
+ if (deviceUA == null) {
+ deviceUA = UserAgentFactory.newDeviceUserAgent(evdnc);
+ }
+ if (evdnc instanceof BufferedODDRHTTPEvidence) {
+ osFound = ((BufferedODDRHTTPEvidence) evdnc).getOsFound();
+ }
+ if (osFound == null) {
+ osFound = osIdentificator.get(deviceUA, this.threshold);
+ }
+ if (evdnc instanceof BufferedODDRHTTPEvidence) {
+ ((BufferedODDRHTTPEvidence) evdnc).setOsFound(osFound);
+ }
+
+ osIdentified = true;
+ }
+ String property = null;
+ if (osFound != null) {
+ property = osFound.get(propertyRef.getLocalPropertyName());
+ ret.addProperty(new ODDRPropertyValue(property, properties.get(propertyKey).getType(), propertyRef));
+
+ } else {
+ ret.addProperty(new ODDRPropertyValue(null, properties.get(propertyKey).getType(), propertyRef));
+ }
+ continue;
+
+ } else if (ASPECT_GROUP.equals(propertyRef.getAspectName())) {
+ try {
+ String jexlExp = properties.get(propertyKey).getExpr();
+ Matcher m = groupRegexprPattern.matcher(jexlExp);
+ while (m.find()) {
+ String id = m.group(1);
+ String propertyValueString = null;
+ PropertyValue propertyValue = getPropertyValue(evdnc, vocabulary.getVocabularyVariables().get(id).getName(), vocabulary.getVocabularyVariables().get(id).getAspect(), vocabulary.getVocabularyVariables().get(id).getVocabulary());
+ propertyValueString = (propertyValue.exists() ? propertyValue.getString() : "-");
+ String toReplace = "$" + id;
+ jexlExp = jexlExp.replaceAll(Matcher.quoteReplacement(toReplace), "'" + propertyValueString + "'");
+ }
+ Expression e = jexl.createExpression(jexlExp);
+ JexlContext jc = new MapContext();
+ Object o = e.evaluate(jc);
+ ret.addProperty(new ODDRPropertyValue(o.toString(), properties.get(propertyKey).getType(), propertyRef));
+
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ public PropertyValues getPropertyValues(Evidence evdnc, PropertyRef[] prs) throws NameException {
+ Device deviceFound = null;
+ Browser browserFound = null;
+ OperatingSystem osFound = null;
+ boolean deviceIdentified = false;
+ boolean browserIdentified = false;
+ boolean osIdentified = false;
+ UserAgent deviceUA = null;
+ UserAgent browserUA = null;
+
+ JexlEngine jexl = new JexlEngine();
+ ODDRPropertyValues ret = new ODDRPropertyValues();
+ Map vocabularies = vocabularyHolder.getVocabularies();
+
+ for (PropertyRef propertyRef : prs) {
+ VocabularyProperty vocabularyProperty = vocabularyHolder.existProperty(propertyRef.getLocalPropertyName(), propertyRef.getAspectName(), propertyRef.getNamespace());
+ Vocabulary vocabulary = vocabularies.get(propertyRef.getNamespace());
+ if (ASPECT_DEVICE.equals(propertyRef.getAspectName())) {
+ if (!deviceIdentified) {
+ if (deviceUA == null) {
+ deviceUA = UserAgentFactory.newDeviceUserAgent(evdnc);
+ }
+ if (evdnc instanceof BufferedODDRHTTPEvidence) {
+ deviceFound = ((BufferedODDRHTTPEvidence) evdnc).getDeviceFound();
+ }
+ if (deviceFound == null) {
+ deviceFound = deviceIdentificator.get(deviceUA, this.threshold);
+ }
+ if (evdnc instanceof BufferedODDRHTTPEvidence) {
+ ((BufferedODDRHTTPEvidence) evdnc).setDeviceFound(deviceFound);
+ }
+
+ deviceIdentified = true;
+ }
+ String property = null;
+
+ if (deviceFound != null) {
+ property = deviceFound.get(propertyRef.getLocalPropertyName());
+ ret.addProperty(new ODDRPropertyValue(property, vocabularyProperty.getType(), propertyRef));
+
+ } else {
+ ret.addProperty(new ODDRPropertyValue(null, vocabularyProperty.getType(), propertyRef));
+ }
+ continue;
+
+ } else if (ASPECT_WEB_BROWSER.equals(propertyRef.getAspectName())) {
+ //TODO: evaluate ua-pixels header in evidence
+ if (!browserIdentified) {
+ if (browserUA == null) {
+ browserUA = UserAgentFactory.newBrowserUserAgent(evdnc);
+ }
+ if (evdnc instanceof BufferedODDRHTTPEvidence) {
+ browserFound = ((BufferedODDRHTTPEvidence) evdnc).getBrowserFound();
+ }
+ if (browserFound == null) {
+ browserFound = browserIdentificator.get(browserUA, this.threshold);
+ }
+ if (evdnc instanceof BufferedODDRHTTPEvidence) {
+ ((BufferedODDRHTTPEvidence) evdnc).setBrowserFound(browserFound);
+ }
+ browserIdentified = true;
+ }
+ String property = null;
+ if (browserFound != null) {
+ property = browserFound.get(propertyRef.getLocalPropertyName());
+ ret.addProperty(new ODDRPropertyValue(property, vocabularyProperty.getType(), propertyRef));
+
+ } else {
+ ret.addProperty(new ODDRPropertyValue(null, vocabularyProperty.getType(), propertyRef));
+ }
+ continue;
+
+ } else if (ASPECT_OPERATIVE_SYSTEM.equals(propertyRef.getAspectName())) {
+ //TODO: evaluate ua-os header in evidence
+ if (!osIdentified) {
+ if (deviceUA == null) {
+ deviceUA = UserAgentFactory.newDeviceUserAgent(evdnc);
+ }
+ if (evdnc instanceof BufferedODDRHTTPEvidence) {
+ osFound = ((BufferedODDRHTTPEvidence) evdnc).getOsFound();
+ }
+ if (osFound == null) {
+ osFound = osIdentificator.get(deviceUA, this.threshold);
+ }
+ if (evdnc instanceof BufferedODDRHTTPEvidence) {
+ ((BufferedODDRHTTPEvidence) evdnc).setOsFound(osFound);
+ }
+ osIdentified = true;
+ }
+ String property = null;
+ if (osFound != null) {
+ property = osFound.get(propertyRef.getLocalPropertyName());
+ ret.addProperty(new ODDRPropertyValue(property, vocabularyProperty.getType(), propertyRef));
+
+ } else {
+ ret.addProperty(new ODDRPropertyValue(null, vocabularyProperty.getType(), propertyRef));
+ }
+ continue;
+
+ } else if (ASPECT_GROUP.equals(propertyRef.getAspectName())) {
+ try {
+ String jexlExp = vocabularyProperty.getExpr();
+ Matcher m = groupRegexprPattern.matcher(jexlExp);
+ while (m.find()) {
+ String id = m.group(1);
+ String propertyValueString = null;
+ PropertyValue propertyValue = getPropertyValue(evdnc, vocabulary.getVocabularyVariables().get(id).getName(), vocabulary.getVocabularyVariables().get(id).getAspect(), vocabulary.getVocabularyVariables().get(id).getVocabulary());
+ propertyValueString = (propertyValue.exists() ? propertyValue.getString() : "-");
+ String toReplace = "$" + id;
+ jexlExp = jexlExp.replaceAll(Matcher.quoteReplacement(toReplace), "'" + propertyValueString + "'");
+ }
+ Expression e = jexl.createExpression(jexlExp);
+ JexlContext jc = new MapContext();
+ Object o = e.evaluate(jc);
+ ret.addProperty(new ODDRPropertyValue(o.toString(), vocabularyProperty.getType(), propertyRef));
+
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ public PropertyValues getPropertyValues(Evidence evdnc, String localAspectName) throws NameException {
+ return getPropertyValues(evdnc, localAspectName, defaultVocabularyIRI);
+ }
+
+ public PropertyValues getPropertyValues(Evidence evdnc, String localAspectName, String vocabularyIRI) throws NameException {
+ VocabularyProperty vocabularyProperty = vocabularyHolder.existProperty(localAspectName, null, vocabularyIRI);
+
+ PropertyName propertyName = new ODDRPropertyName(localAspectName, vocabularyIRI);
+ PropertyRef propertyRef = new ODDRPropertyRef(propertyName, vocabularyProperty.getDefaultAspect());
+
+ return getPropertyValues(evdnc, new PropertyRef[]{propertyRef});
+ }
+
+ public PropertyName newPropertyName(String localPropertyName, String vocabularyIRI) throws NameException {
+ vocabularyHolder.existProperty(localPropertyName, null, vocabularyIRI);
+ return new ODDRPropertyName(localPropertyName, vocabularyIRI);
+ }
+
+ public PropertyName newPropertyName(String localPropertyName) throws NameException {
+ return newPropertyName(localPropertyName, defaultVocabularyIRI);
+ }
+
+ public PropertyRef newPropertyRef(PropertyName pn, String localAspectName) throws NameException {
+ vocabularyHolder.existProperty(pn.getLocalPropertyName(), localAspectName, pn.getNamespace());
+ return new ODDRPropertyRef(pn, localAspectName);
+ }
+
+ public PropertyRef newPropertyRef(PropertyName pn) throws NameException {
+ VocabularyProperty vocabularyProperty = vocabularyHolder.existProperty(pn.getLocalPropertyName(), null, pn.getNamespace());
+ return newPropertyRef(pn, vocabularyProperty.getDefaultAspect());
+ }
+
+ public PropertyRef newPropertyRef(String localPropertyName) throws NameException {
+ return newPropertyRef(newPropertyName(localPropertyName));
+ }
+
+ public Evidence newHTTPEvidence() {
+ return new ODDRHTTPEvidence();
+ }
+
+ public Evidence newHTTPEvidence(Map map) {
+ return new ODDRHTTPEvidence(map);
+ }
+}
diff --git a/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/Token.java b/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/Token.java
new file mode 100644
index 0000000..7c9b574
--- /dev/null
+++ b/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/Token.java
@@ -0,0 +1,132 @@
+/**
+ * Copyright 2012 Fundación CTIC
+ * This software is distributed under the terms of the GNU Lesser General Public License.
+ *
+ *
+ * This file is part of OpenDDR Simple APIs.
+ * OpenDDR Simple APIs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * OpenDDR Simple APIs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Simple APIs. If not, see .
+ *
+ * @author José Quiroga Álvarez
+ * @author Diego Martínez Ballesteros
+ *
+ */
+package org.openddr.simpleapi.oddr.builder.vitamineddevice;
+
+import java.util.regex.Pattern;
+
+//same approach as token strings with commndly used regex patterns
+public class Token {
+
+ private String id; //token string
+ private Pattern midPattern; //case-insensitive '.*id.*' regex
+ private Pattern tailPattern; //case-insensitive '.*id' regex
+ private Pattern semicolonPattern; //case-insensitive '.*id.?;.*' regex
+ private Pattern quotePattern; //case-insensitive '.* + Pattern.quote(token) + .*'
+ private String looseId; //loose token
+ private Pattern looseMidPattern; //case-insensitive '.*looseID.*' regex
+ private Pattern looseTailPattern; //case-insensitive '.*looseID' regex
+ private Pattern looseSemicolonPattern; //case-insensitive '.*looseID.?;.*' regex
+
+ private Pattern caseSensitiveMidPattern; //case-sensitive '.*id.*' regex
+ private Pattern caseSensitiveTailPattern; //case-sensitive '.*id' regex
+ private Pattern caseSensitiveSemicolonPattern; //case-sensitive '.*id.?;.*' regex
+ private Pattern caseSensitiveQuotePattern; //case-sensitive '.* + Pattern.quote(token) + .*'
+ private Pattern caseSensitiveLooseMidPattern; //case-sensitive '.*looseID.*' regex
+ private Pattern caseSensitiveLooseTailPattern; //case-sensitive '.*looseID' regex
+ private Pattern caseSensitiveLooseSemicolonPattern; //case-sensitive '.*looseID.?;.*' regex
+
+ public Token(String id){
+ this.id = id;
+ this.midPattern = Pattern.compile("(?i).*" + this.id + ".*");
+ this.tailPattern = Pattern.compile("(?i).*" + this.id);
+ this.semicolonPattern = Pattern.compile("(?i).*" + this.id + ".?;.*");
+ this.quotePattern = Pattern.compile("(?i).*" + Pattern.quote(this.id) + ".*");
+ this.looseId = this.id.replaceAll("[ _/-]", ".?");
+ this.looseMidPattern = Pattern.compile("(?i).*" + this.looseId + ".*");
+ this.looseTailPattern = Pattern.compile("(?i).*" + this.looseId);
+ this.looseSemicolonPattern = Pattern.compile("(?i).*" + this.looseId + ".?;.*");
+
+ this.caseSensitiveMidPattern = Pattern.compile(".*" + this.id + ".*");
+ this.caseSensitiveTailPattern = Pattern.compile(".*" + this.id);
+ this.caseSensitiveSemicolonPattern = Pattern.compile(".*" + this.id + ".?;.*");
+ this.caseSensitiveQuotePattern = Pattern.compile(".*" + Pattern.quote(this.id) + ".*");
+ this.caseSensitiveLooseMidPattern = Pattern.compile(".*" + this.looseId + ".*");
+ this.caseSensitiveLooseTailPattern = Pattern.compile(".*" + this.looseId);
+ this.caseSensitiveLooseSemicolonPattern = Pattern.compile(".*" + this.looseId + ".?;.*");
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getLooseId() {
+ return looseId;
+ }
+
+ public Pattern getMidPattern() {
+ return midPattern;
+ }
+
+ public Pattern getTailPattern() {
+ return tailPattern;
+ }
+
+ public Pattern getSemicolonPattern() {
+ return semicolonPattern;
+ }
+
+ public Pattern getLooseMidPattern() {
+ return looseMidPattern;
+ }
+
+ public Pattern getLooseTailPattern() {
+ return looseTailPattern;
+ }
+
+ public Pattern getLooseSemicolonPattern() {
+ return looseSemicolonPattern;
+ }
+
+ public Pattern getQuotePattern() {
+ return quotePattern;
+ }
+
+ public Pattern getCaseSensitiveMidPattern() {
+ return caseSensitiveMidPattern;
+ }
+
+ public Pattern getCaseSensitiveTailPattern() {
+ return caseSensitiveTailPattern;
+ }
+
+ public Pattern getCaseSensitiveSemicolonPattern() {
+ return caseSensitiveSemicolonPattern;
+ }
+
+ public Pattern getCaseSensitiveQuotePattern() {
+ return caseSensitiveQuotePattern;
+ }
+
+ public Pattern getCaseSensitiveLooseMidPattern() {
+ return caseSensitiveLooseMidPattern;
+ }
+
+ public Pattern getCaseSensitiveLooseTailPattern() {
+ return caseSensitiveLooseTailPattern;
+ }
+
+ public Pattern getCaseSensitiveLooseSemicolonPattern() {
+ return caseSensitiveLooseSemicolonPattern;
+ }
+}
diff --git a/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedAndroidDeviceBuilder.java b/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedAndroidDeviceBuilder.java
new file mode 100644
index 0000000..d8e62ad
--- /dev/null
+++ b/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedAndroidDeviceBuilder.java
@@ -0,0 +1,220 @@
+/**
+ * Copyright 2012 Fundación CTIC
+ * This software is distributed under the terms of the GNU Lesser General Public License.
+ *
+ *
+ * This file is part of OpenDDR Simple APIs.
+ * OpenDDR Simple APIs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * OpenDDR Simple APIs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Simple APIs. If not, see .
+ *
+ * @author José Quiroga Álvarez
+ * @author Diego Martínez Ballesteros
+ *
+ */
+package org.openddr.simpleapi.oddr.builder.vitamineddevice;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.openddr.simpleapi.oddr.model.device.Device;
+import org.openddr.simpleapi.oddr.model.UserAgent;
+
+public class VitaminedAndroidDeviceBuilder extends VitaminedOrderedTokenDeviceBuilder {
+
+ private static final String BUILD_HASH_REGEXP = ".*Build/([^ \\)\\(]*).*";
+ private Pattern buildHashPattern = Pattern.compile(BUILD_HASH_REGEXP);
+ private Map devices;
+
+ public VitaminedAndroidDeviceBuilder() {
+ super();
+ }
+
+ public boolean canBuild(UserAgent userAgent) {
+ if (userAgent.containsAndroid()) {
+ return true;
+
+ } else {
+ return false;
+ }
+ }
+
+ public Device build(UserAgent userAgent, int confidenceTreshold) {
+ ArrayList foundDevices = new ArrayList();
+ Iterator it = orderedRules.keySet().iterator();
+ while (it.hasNext()) {
+ String tokenString = (String) it.next();
+ Token tokenInstance = tokensCache.get(tokenString); //get the token by key
+ Device d = elaborateAndroidDeviceWithToken(userAgent, tokenInstance);
+ if (d != null) {
+ if (d.getConfidence() > confidenceTreshold) {
+ return d;
+
+ } else {
+ if (d.getConfidence() > 0) {
+ foundDevices.add(d);
+ }
+ }
+ }
+ }
+ if (foundDevices.size() > 0) {
+ Collections.sort(foundDevices, Collections.reverseOrder());
+ return foundDevices.get(0);
+ }
+ return null;
+ }
+
+ public void putDevice(String deviceID, List initProperties) {
+ orderedRules.put(initProperties.get(0), deviceID);
+ }
+
+ private Device elaborateAndroidDeviceWithToken(UserAgent userAgent, Token tokenInst) {
+ if (userAgent.hasMozillaPattern() || userAgent.hasOperaPattern()) {
+ int subtract = 0;
+
+ Pattern loosePattern = tokenInst.getLooseMidPattern();
+
+ if (!loosePattern.matcher(userAgent.getCompleteUserAgent().replaceAll("Android", "")).matches()) {
+ return null;
+ }
+
+ String patternElementInsideClean = cleanPatternElementInside(userAgent.getPatternElementsInside());
+
+ Pattern currentPattern = null;
+
+ for (int i = 0; i <= 1; i++) {
+
+ if (i == 1) currentPattern = Pattern.compile("(?i).*" + tokenInst.getLooseId() + ".?Build/.*");
+ else currentPattern = Pattern.compile("(?i).*" + tokenInst.getId() + ".?Build/.*");
+ if (patternElementInsideClean != null && currentPattern.matcher(patternElementInsideClean).matches()) {//&& userAgent.getPatternElementsInside().matches(".*" + currentToken + ".?Build/.*")) {
+ String deviceId = (String) orderedRules.get(tokenInst.getId());
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(100 - subtract);
+ return retDevice;
+ }
+ }
+
+ if (i == 1) currentPattern = tokenInst.getLooseTailPattern();
+ else currentPattern = tokenInst.getTailPattern();
+ if (userAgent.getPatternElementsPre() != null && currentPattern.matcher(userAgent.getPatternElementsPre()).matches()) {//userAgent.getPatternElementsPre().matches(".*" + currentToken)) {
+ String deviceId = (String) orderedRules.get(tokenInst.getId());
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(95 - subtract);
+ return retDevice;
+ }
+ }
+
+ if (patternElementInsideClean != null && currentPattern.matcher(patternElementInsideClean).matches()) {//userAgent.getPatternElementsInside().matches(".*" + currentToken)) {
+ String deviceId = (String) orderedRules.get(tokenInst.getId());
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(90 - subtract);
+ return retDevice;
+ }
+ }
+
+ if (i == 1) currentPattern = tokenInst.getLooseSemicolonPattern();
+ else currentPattern = tokenInst.getSemicolonPattern();
+ if (patternElementInsideClean != null && currentPattern.matcher(patternElementInsideClean).matches()) {//userAgent.getPatternElementsInside().matches(".*" + currentToken + ".?;.*")) {
+ String deviceId = (String) orderedRules.get(tokenInst.getId());
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(90 - subtract);
+ return retDevice;
+ }
+ }
+
+
+ if (i == 1) currentPattern = tokenInst.getLooseMidPattern();
+ else currentPattern = tokenInst.getMidPattern();
+ if (patternElementInsideClean != null && currentPattern.matcher(patternElementInsideClean).matches()) {//userAgent.getPatternElementsInside().matches(".*" + currentToken + ".*")) {
+ String deviceId = (String) orderedRules.get(tokenInst.getId());
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(80 - subtract);
+ return retDevice;
+ }
+ }
+ if (userAgent.getPatternElementsPre() != null && currentPattern.matcher(userAgent.getPatternElementsPre()).matches()) {//userAgent.getPatternElementsPre().matches(".*" + currentToken + ".*")) {
+ String deviceId = (String) orderedRules.get(tokenInst.getId());
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(80 - subtract);
+ return retDevice;
+ }
+ }
+ if (userAgent.getPatternElementsPost() != null && currentPattern.matcher(userAgent.getPatternElementsPost()).matches()) {//userAgent.getPatternElementsPost().matches(".*" + currentToken + ".*")) {
+ String deviceId = (String) orderedRules.get(tokenInst.getId());
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(60 - subtract);
+ return retDevice;
+ }
+ }
+ if (i == 1) {
+ if (userAgent.getPatternElementsInside() != null && currentPattern.matcher(userAgent.getPatternElementsInside()).matches()) {//userAgent.getPatternElementsInside().matches(".*" + currentToken + ".*")) {
+ String deviceId = (String) orderedRules.get(tokenInst.getId());
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(40);
+ return retDevice;
+ }
+ }
+ }
+ subtract += 20;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void afterOderingCompleteInit(Map devices) {
+ this.devices = devices;
+ for (Object devIdObj : orderedRules.values()) {
+ String devId = (String) devIdObj;
+ if (!devices.containsKey(devId)) {
+ throw new IllegalStateException("unable to find device with id: " + devId + "in devices");
+ }
+ }
+ }
+
+ private String cleanPatternElementInside(String patternElementsInside) {
+ String patternElementInsideClean = patternElementsInside;
+
+ Matcher buildHashMatcher = buildHashPattern.matcher(patternElementInsideClean);
+
+ if (buildHashMatcher.find()) {
+ String build = buildHashMatcher.group(1);
+ patternElementInsideClean = patternElementInsideClean.replaceAll("Build/" + Pattern.quote(build), "Build/");
+
+ }
+ patternElementInsideClean = patternElementInsideClean.replaceAll("Android", "");
+
+ return patternElementInsideClean;
+ }
+}
diff --git a/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedDeviceBuilder.java b/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedDeviceBuilder.java
new file mode 100644
index 0000000..aebde32
--- /dev/null
+++ b/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedDeviceBuilder.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright 2012 Fundación CTIC
+ * This software is distributed under the terms of the GNU Lesser General Public License.
+ *
+ *
+ * This file is part of OpenDDR Simple APIs.
+ * OpenDDR Simple APIs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * OpenDDR Simple APIs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Simple APIs. If not, see .
+ *
+ * @author José Quiroga Álvarez
+ * @author Diego Martínez Ballesteros
+ *
+ */
+package org.openddr.simpleapi.oddr.builder.vitamineddevice;
+
+import java.util.HashMap;
+
+import org.openddr.simpleapi.oddr.builder.device.DeviceBuilder;
+
+//includes tokens as complex class instead of simple strings
+//each token includes frequently used Pattern instances
+public abstract class VitaminedDeviceBuilder implements DeviceBuilder {
+
+ protected HashMap tokensCache = new HashMap();
+
+}
diff --git a/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedIOSDeviceBuilder.java b/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedIOSDeviceBuilder.java
new file mode 100644
index 0000000..603e28d
--- /dev/null
+++ b/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedIOSDeviceBuilder.java
@@ -0,0 +1,92 @@
+/**
+ * Copyright 2012 Fundación CTIC
+ * This software is distributed under the terms of the GNU Lesser General Public License.
+ *
+ *
+ * This file is part of OpenDDR Simple APIs.
+ * OpenDDR Simple APIs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * OpenDDR Simple APIs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Simple APIs. If not, see .
+ *
+ * @author José Quiroga Álvarez
+ * @author Diego Martínez Ballesteros
+ *
+ */
+package org.openddr.simpleapi.oddr.builder.vitamineddevice;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openddr.simpleapi.oddr.builder.device.DeviceBuilder;
+import org.openddr.simpleapi.oddr.model.device.Device;
+import org.openddr.simpleapi.oddr.model.UserAgent;
+
+public class VitaminedIOSDeviceBuilder extends VitaminedDeviceBuilder {
+
+ private LinkedHashMap iOSDevices;
+ private Map devices;
+
+ public VitaminedIOSDeviceBuilder() {
+ iOSDevices = new LinkedHashMap();
+ }
+
+ public boolean canBuild(UserAgent userAgent) {
+ if (userAgent.containsIOSDevices() && (!userAgent.containsAndroid()) && (!userAgent.containsWindowsPhone())) {
+ return true;
+
+ } else {
+ return false;
+ }
+ }
+
+ public Device build(UserAgent userAgent, int confidenceTreshold) {
+ Iterator it = iOSDevices.keySet().iterator();
+ while (it.hasNext()) {
+ String tokenString = (String) it.next();
+ Token tokenInstance = tokensCache.get(tokenString); //get the token by key
+ if (tokenInstance.getCaseSensitiveMidPattern().matcher(userAgent.getCompleteUserAgent()).matches()) {
+ String iosDeviceID = iOSDevices.get(tokenString);
+ if (iosDeviceID != null) {
+ Device retDevice = (Device) devices.get(iosDeviceID).clone();
+ retDevice.setConfidence(90);
+ return retDevice;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void putDevice(String device, List initProperties) {
+ iOSDevices.put(initProperties.get(0), device);
+ }
+
+ public void completeInit(Map devices) {
+ String global = "iPhone";
+ if (iOSDevices.containsKey(global)) {
+ String iphone = iOSDevices.get(global);
+ iOSDevices.remove(global);
+ iOSDevices.put(global, iphone);
+ }
+
+ this.devices = devices;
+
+ for (String deviceID : iOSDevices.values()) {
+ if (!devices.containsKey(deviceID)) {
+ throw new IllegalStateException("unable to find device with id: " + deviceID + "in devices");
+ }
+ else
+ tokensCache.put(deviceID, new Token(deviceID));
+ }
+ }
+}
diff --git a/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedOrderedTokenDeviceBuilder.java b/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedOrderedTokenDeviceBuilder.java
new file mode 100644
index 0000000..4c6b9e2
--- /dev/null
+++ b/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedOrderedTokenDeviceBuilder.java
@@ -0,0 +1,102 @@
+/**
+ * Copyright 2012 Fundación CTIC
+ * This software is distributed under the terms of the GNU Lesser General Public License.
+ *
+ *
+ * This file is part of OpenDDR Simple APIs.
+ * OpenDDR Simple APIs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * OpenDDR Simple APIs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Simple APIs. If not, see .
+ *
+ * @author José Quiroga Álvarez
+ * @author Diego Martínez Ballesteros
+ *
+ */
+package org.openddr.simpleapi.oddr.builder.vitamineddevice;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.openddr.simpleapi.oddr.builder.device.DeviceBuilder;
+import org.openddr.simpleapi.oddr.model.device.Device;
+
+public abstract class VitaminedOrderedTokenDeviceBuilder extends VitaminedDeviceBuilder {
+
+ protected LinkedHashMap orderedRules;
+
+ public VitaminedOrderedTokenDeviceBuilder() {
+ orderedRules = new LinkedHashMap();
+ }
+
+ abstract protected void afterOderingCompleteInit(Map devices);
+
+ public final void completeInit(Map devices) {
+ LinkedHashMap tmp = new LinkedHashMap();
+ ArrayList keys = new ArrayList(orderedRules.keySet());
+ Collections.sort(keys, new Comparator() {
+
+ public int compare(String o1, String o2) {
+ return o2.length() - o1.length();
+ }
+ });
+ for (String string : keys) {
+ tmp.put(string, orderedRules.get(string));
+ }
+ ArrayList keysOrdered = new ArrayList();
+
+ orderedRules = new LinkedHashMap();
+
+ while (keys.size() > 0) {
+ boolean found = false;
+ for (String k1 : keys) {
+ for (String k2 : keys) {
+ if ((!k1.equals(k2)) && k2.matches(".*" + k1 + ".*")) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ keysOrdered.add(k1);
+ keys.remove(k1);
+ break;
+ }
+ }
+ if (!found) {
+ continue;
+ }
+ int max = 0;
+ int idx = -1;
+ for (int i = 0; i < keys.size(); i++) {
+ String string = keys.get(i);
+ if (string.length() > max) {
+ max = string.length();
+ idx = i;
+ }
+ }
+ if (idx >= 0) {
+ keysOrdered.add(keys.get(idx));
+ keys.remove(idx);
+ }
+ }
+ for (String key : keysOrdered) {
+ orderedRules.put(key, tmp.get(key));
+ tokensCache.put(key, new Token(key));
+ tmp.remove(key);
+ }
+
+ afterOderingCompleteInit(devices);
+ }
+}
diff --git a/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedSimpleDeviceBuilder.java b/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedSimpleDeviceBuilder.java
new file mode 100644
index 0000000..ebe9a2f
--- /dev/null
+++ b/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedSimpleDeviceBuilder.java
@@ -0,0 +1,90 @@
+/**
+ * Copyright 2012 Fundación CTIC
+ * This software is distributed under the terms of the GNU Lesser General Public License.
+ *
+ *
+ * This file is part of OpenDDR Simple APIs.
+ * OpenDDR Simple APIs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * OpenDDR Simple APIs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Simple APIs. If not, see .
+ *
+ * @author José Quiroga Álvarez
+ * @author Diego Martínez Ballesteros
+ *
+ */
+package org.openddr.simpleapi.oddr.builder.vitamineddevice;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.openddr.simpleapi.oddr.builder.device.DeviceBuilder;
+import org.openddr.simpleapi.oddr.model.BuiltObject;
+import org.openddr.simpleapi.oddr.model.UserAgent;
+import org.openddr.simpleapi.oddr.model.device.Device;
+
+public class VitaminedSimpleDeviceBuilder extends VitaminedDeviceBuilder {
+
+ private LinkedHashMap simpleTokenMap;
+ private Map devices;
+
+ public VitaminedSimpleDeviceBuilder() {
+ simpleTokenMap = new LinkedHashMap();
+ }
+
+ public void putDevice(String deviceId, List initProperties) {
+
+ for (String token : initProperties) {
+ simpleTokenMap.put(token, deviceId);
+ }
+ }
+
+ public void completeInit(Map devices) {
+ this.devices = devices;
+
+ for (String deviceID : simpleTokenMap.values()) {
+ if (!devices.containsKey(deviceID)) {
+ throw new IllegalStateException("unable to find device with id: " + deviceID + "in devices");
+ }
+ else
+ tokensCache.put(deviceID, new Token(deviceID));
+ }
+ }
+
+ public boolean canBuild(UserAgent userAgent) {
+ for (String token : simpleTokenMap.keySet()) {
+ Token tokenInstance = tokensCache.get(token);
+ if (tokenInstance.getQuotePattern().matcher(userAgent.getCompleteUserAgent()).matches()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public BuiltObject build(UserAgent userAgent, int confidenceTreshold) {
+ Iterator it = simpleTokenMap.keySet().iterator();
+ while (it.hasNext()) {
+ String tokenString = (String) it.next();
+ Token tokenInstance = tokensCache.get(tokenString); //get the token by key
+ if (tokenInstance.getQuotePattern().matcher(userAgent.getCompleteUserAgent()).matches()) {
+ String desktopDeviceId = simpleTokenMap.get(tokenString);
+ if (desktopDeviceId != null) {
+ Device device = devices.get(desktopDeviceId);
+ return device;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedSymbianDeviceBuilder.java b/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedSymbianDeviceBuilder.java
new file mode 100644
index 0000000..abe4bfc
--- /dev/null
+++ b/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedSymbianDeviceBuilder.java
@@ -0,0 +1,226 @@
+/**
+ * Copyright 2012 Fundación CTIC
+ * This software is distributed under the terms of the GNU Lesser General Public License.
+ *
+ *
+ * This file is part of OpenDDR Simple APIs.
+ * OpenDDR Simple APIs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * OpenDDR Simple APIs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Simple APIs. If not, see .
+ *
+ * @author José Quiroga Álvarez
+ * @author Diego Martínez Ballesteros
+ *
+ */
+package org.openddr.simpleapi.oddr.builder.vitamineddevice;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.openddr.simpleapi.oddr.model.device.Device;
+import org.openddr.simpleapi.oddr.model.UserAgent;
+
+public class VitaminedSymbianDeviceBuilder extends VitaminedOrderedTokenDeviceBuilder {
+
+ private Map devices;
+
+ public VitaminedSymbianDeviceBuilder() {
+ super();
+ }
+
+ public boolean canBuild(UserAgent userAgent) {
+ if (userAgent.containsSymbian()) {
+ return true;
+
+ } else {
+ return false;
+ }
+ }
+
+ public Device build(UserAgent userAgent, int confidenceTreshold) {
+ ArrayList foundDevices = new ArrayList();
+ Iterator it = orderedRules.keySet().iterator();
+ while (it.hasNext()) {
+ String tokenString = (String) it.next();
+ Token tokenInstance = tokensCache.get(tokenString); //get the token by key
+ Device d = elaborateSymbianDeviceWithToken(userAgent, tokenInstance);
+ if (d != null) {
+ if (d.getConfidence() > confidenceTreshold) {
+ return d;
+
+ } else {
+ if (d.getConfidence() > 0) {
+ foundDevices.add(d);
+ }
+ }
+ }
+ }
+
+ if (foundDevices.size() > 0) {
+ Collections.sort(foundDevices, Collections.reverseOrder());
+ return foundDevices.get(0);
+ }
+ return null;
+ }
+
+ public void putDevice(String device, List initProperties) {
+ orderedRules.put(initProperties.get(0), device);
+ }
+
+ private Device elaborateSymbianDeviceWithToken(UserAgent userAgent, Token tokenInst) {
+ if (userAgent.hasMozillaPattern() || userAgent.hasOperaPattern()) {
+ int subtract = 0;
+ Pattern loosePattern = tokenInst.getCaseSensitiveLooseMidPattern();
+
+ if (!loosePattern.matcher(userAgent.getCompleteUserAgent()).matches()) {
+ return null;
+ }
+
+ Pattern currentPattern = null;
+
+ if (userAgent.hasOperaPattern()) {
+ subtract += 10;
+ }
+ for (int i = 0; i <= 1; i++) {
+
+ if (i == 1) currentPattern = Pattern.compile(".*Series60.?(\\d+)\\.(\\d+).?" + tokenInst.getLooseId() + ".*");
+ else currentPattern = Pattern.compile(".*Series60.?(\\d+)\\.(\\d+).?" + tokenInst.getId() + ".*");
+ if (userAgent.getPatternElementsInside() != null && currentPattern.matcher(userAgent.getPatternElementsInside()).matches()) {// userAgent.getPatternElementsInside().matches(".*Series60.?(\\d+)\\.(\\d+).?" + currentToken + ".*")) {
+ String deviceId = (String) orderedRules.get(tokenInst.getId());
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(100 - subtract);
+ return retDevice;
+ }
+ }
+
+ if (i == 1) currentPattern = tokenInst.getCaseSensitiveLooseTailPattern();
+ else currentPattern = tokenInst.getCaseSensitiveTailPattern();
+ if (userAgent.getPatternElementsPre() != null && currentPattern.matcher(userAgent.getPatternElementsPre()).matches()) {//userAgent.getPatternElementsPre().matches(".*" + currentToken)) {
+ String deviceId = (String) orderedRules.get(tokenInst.getId());
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(95 - subtract);
+ return retDevice;
+ }
+ }
+
+ if (userAgent.getPatternElementsInside() != null && currentPattern.matcher(userAgent.getPatternElementsInside()).matches()) {//userAgent.getPatternElementsInside().matches(".*" + currentToken)) {
+ String deviceId = (String) orderedRules.get(tokenInst.getId());
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(90 - subtract);
+ return retDevice;
+ }
+ }
+
+ if (i == 1) currentPattern = tokenInst.getCaseSensitiveLooseSemicolonPattern();
+ else currentPattern = tokenInst.getCaseSensitiveSemicolonPattern();
+ if (userAgent.getPatternElementsInside() != null && currentPattern.matcher(userAgent.getPatternElementsInside()).matches()) {//userAgent.getPatternElementsInside().matches(".*" + currentToken + ".?;.*")) {
+ String deviceId = (String) orderedRules.get(tokenInst.getId());
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(90 - subtract);
+ return retDevice;
+ }
+ }
+
+ if (i == 1) currentPattern = tokenInst.getCaseSensitiveLooseMidPattern();
+ else currentPattern = tokenInst.getCaseSensitiveMidPattern();
+ if (userAgent.getPatternElementsInside() != null && currentPattern.matcher(userAgent.getPatternElementsInside()).matches()) {//userAgent.getPatternElementsInside().matches(".*" + currentToken + ".*")) {
+ String deviceId = (String) orderedRules.get(tokenInst.getId());
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(80 - subtract);
+ return retDevice;
+ }
+ }
+
+ if (userAgent.getPatternElementsPre() != null && currentPattern.matcher(userAgent.getPatternElementsPre()).matches()) {//userAgent.getPatternElementsPre().matches(".*" + currentToken + ".*")) {
+ String deviceId = (String) orderedRules.get(tokenInst.getId());
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(80 - subtract);
+ return retDevice;
+ }
+ }
+
+ if (userAgent.getPatternElementsPost() != null && currentPattern.matcher(userAgent.getPatternElementsPost()).matches()) {//userAgent.getPatternElementsPost().matches(".*" + currentToken + ".*")) {
+ String deviceId = (String) orderedRules.get(tokenInst.getId());
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(60 - subtract);
+ return retDevice;
+ }
+ }
+ subtract += 20;
+ }
+ } else {
+ String ua = userAgent.getCompleteUserAgent().replaceAll("SN[0-9]*", "");
+
+ int subtract = 0;
+ String currentToken = tokenInst.getId();
+
+ String looseToken = tokenInst.getLooseId();
+ Pattern loosePattern = tokenInst.getCaseSensitiveLooseMidPattern();
+
+ if (!loosePattern.matcher(userAgent.getCompleteUserAgent()).matches()) {
+ return null;
+ }
+
+ Pattern currentPattern = null;
+
+ for (int i = 0; i <= 1; i++) {
+ if (i == 1) {
+ currentToken = looseToken;
+ }
+
+ currentPattern = Pattern.compile(".*" + currentToken + ".*");
+ if (currentPattern.matcher(ua).matches()) {
+ String deviceId = (String) orderedRules.get(tokenInst.getId());
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(100 - subtract);
+ return retDevice;
+ }
+ }
+
+ subtract += 20;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void afterOderingCompleteInit(Map devices) {
+ this.devices = devices;
+ for (Object devIdObj : orderedRules.values()) {
+ String devId = (String) devIdObj;
+ if (!devices.containsKey(devId)) {
+ throw new IllegalStateException("unable to find device with id: " + devId + "in devices");
+ }
+ }
+ }
+}
diff --git a/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedTwoStepDeviceBuilder.java b/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedTwoStepDeviceBuilder.java
new file mode 100644
index 0000000..da56ad1
--- /dev/null
+++ b/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedTwoStepDeviceBuilder.java
@@ -0,0 +1,251 @@
+/**
+ * Copyright 2012 Fundación CTIC
+ * This software is distributed under the terms of the GNU Lesser General Public License.
+ *
+ *
+ * This file is part of OpenDDR Simple APIs.
+ * OpenDDR Simple APIs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * OpenDDR Simple APIs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Simple APIs. If not, see .
+ *
+ * @author José Quiroga Álvarez
+ * @author Diego Martínez Ballesteros
+ *
+ */
+package org.openddr.simpleapi.oddr.builder.vitamineddevice;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.openddr.simpleapi.oddr.model.device.Device;
+import org.openddr.simpleapi.oddr.model.UserAgent;
+
+public class VitaminedTwoStepDeviceBuilder extends VitaminedOrderedTokenDeviceBuilder{
+
+ private Map devices;
+
+ private Pattern anyLetter = Pattern.compile(".*[a-zA-Z].*");
+ private Pattern looseSymbols = Pattern.compile("[ _/-]?");
+
+ public VitaminedTwoStepDeviceBuilder() {
+ super();
+ }
+
+ public boolean canBuild(UserAgent userAgent) {
+ for (String step1Token : orderedRules.keySet()) {
+ if (userAgent.getCompleteUserAgent().matches("(?i).*" + step1Token + ".*")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Device build(UserAgent userAgent, int confidenceTreshold) {
+ ArrayList foundDevices = new ArrayList();
+ for (String step1Token : orderedRules.keySet()) {
+ if (userAgent.getCompleteUserAgent().matches("(?i).*" + step1Token + ".*")) {
+ Map step1Compliant = (Map) orderedRules.get(step1Token);
+ for (String step2token : step1Compliant.keySet()) {
+ Device d = elaborateTwoStepDeviceWithToken(userAgent, step1Token, step2token);
+ if (d != null) {
+ if (d.getConfidence() > confidenceTreshold) {
+ return d;
+
+ } else {
+ if (d.getConfidence() > 0) {
+ foundDevices.add(d);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (foundDevices.size() > 0) {
+ Collections.sort(foundDevices, Collections.reverseOrder());
+ return foundDevices.get(0);
+ }
+
+ return null;
+ }
+
+ public void putDevice(String device, List initProperties) {
+ String step1TokenString = initProperties.get(0);
+ String step2TokenString = initProperties.get(1);
+
+ if (step2TokenString.matches(".*" + step1TokenString + ".*")) {
+ step2TokenString = step2TokenString.replaceAll(step1TokenString + "[^\\p{Alnum}]?", "");
+ }
+
+ Map step1Token = (Map) orderedRules.get(step1TokenString);
+ if (step1Token == null) {
+ step1Token = new LinkedHashMap();
+ orderedRules.put(initProperties.get(0), step1Token);
+ }
+ step1Token.put(step2TokenString, device);
+ }
+
+ private Device elaborateTwoStepDeviceWithToken(UserAgent userAgent, String step1Token, String step2Token) {
+ int maxLittleTokensDistance = 4;
+ int maxBigTokensDistance = 8;
+ int confidence;
+
+ String originalToken = step2Token;
+ String looseToken = step2Token.replaceAll("[ _/-]", ".?");
+
+ if (userAgent.getCompleteUserAgent().matches("(?i).*" + step2Token + ".*")) {
+ confidence = 100;
+ } else if (userAgent.getCompleteUserAgent().matches("(?i).*" + looseToken + ".*")) {
+ step2Token = looseToken;
+ confidence = 90;
+
+ } else {
+ return null;
+ }
+
+ Matcher result = Pattern.compile("(?i)(?:(?:.*" + step1Token + "(.*?)" + step2Token + ".*)|(?:.*" + step2Token + "(.*?)" + step1Token + ".*))").matcher(userAgent.getCompleteUserAgent());
+ if (result.matches()) {
+ //test for case sensitive match
+ Matcher bestResult = Pattern.compile("(?:(?:.*" + step1Token + "(.*?)" + step2Token + ".*)|(?:.*" + step2Token + "(.*?)" + step1Token + ".*))").matcher(userAgent.getCompleteUserAgent());
+ if (bestResult.matches()) {
+ result = bestResult;
+
+ } else {
+ confidence -= 20;
+ }
+
+ String betweenTokens = result.group(1);
+ String s2 = result.group(2);
+ if (s2 != null && (betweenTokens == null || betweenTokens.length() > s2.length())) {
+ betweenTokens = s2;
+ }
+ int betweenTokensLength = betweenTokens.length();
+
+ if (step2Token.length() > 3) {
+ if (betweenTokensLength > maxBigTokensDistance) {
+ confidence -= 10;
+ }
+
+ String deviceId = ((Map) orderedRules.get(step1Token)).get(originalToken);
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(confidence);
+ return retDevice;
+ }
+ }
+
+
+ if ((betweenTokensLength < maxLittleTokensDistance) || (betweenTokensLength < maxBigTokensDistance && (step2Token.length() < 6 || !anyLetter.matcher(step2Token).matches()))) {
+ if (betweenTokensLength <= 1) {
+ if (!looseSymbols.matcher(betweenTokens).matches()) {
+ confidence -= 20;
+ }
+
+ } else {
+ confidence -= 40;
+ }
+
+ confidence -= (betweenTokensLength * 4);
+
+ String deviceId = ((Map) orderedRules.get(step1Token)).get(originalToken);
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(confidence);
+ return retDevice;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void afterOderingCompleteInit(Map devices) {
+ for (String step1Token : orderedRules.keySet()) {
+ sortElement(step1Token);
+ }
+
+ this.devices = devices;
+ for (Object devIdsMapObj : orderedRules.values()) {
+ Map devIdsMap = (Map) devIdsMapObj;
+ for (Object devIdObj : devIdsMap.values()) {
+ String devId = (String) devIdObj;
+ if (!devices.containsKey(devId)) {
+ throw new IllegalStateException("unable to find device with id: " + devId + "in devices");
+ }
+ }
+ }
+ }
+
+ private void sortElement(String step1Token) {
+ Map currentStep1Map = (Map) orderedRules.get(step1Token);
+
+ LinkedHashMap tmp = new LinkedHashMap();
+ ArrayList keys = new ArrayList(currentStep1Map.keySet());
+ Collections.sort(keys, new Comparator() {
+
+ public int compare(String o1, String o2) {
+ return o2.length() - o1.length();
+ }
+ });
+ for (String string : keys) {
+ tmp.put(string, currentStep1Map.get(string));
+ }
+ ArrayList keysOrdered = new ArrayList();
+ currentStep1Map = new LinkedHashMap();
+ while (keys.size() > 0) {
+ boolean found = false;
+ for (String k1 : keys) {
+ for (String k2 : keys) {
+ if ((!k1.equals(k2)) && k2.matches(".*" + k1 + ".*")) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ keysOrdered.add(k1);
+ keys.remove(k1);
+ break;
+ }
+ }
+ if (!found) {
+ continue;
+ }
+ int max = 0;
+ int idx = -1;
+ for (int i = 0; i < keys.size(); i++) {
+ String string = keys.get(i);
+ if (string.length() > max) {
+ max = string.length();
+ idx = i;
+ }
+ }
+ if (idx >= 0) {
+ keysOrdered.add(keys.get(idx));
+ keys.remove(idx);
+ }
+ }
+ for (String key : keysOrdered) {
+ currentStep1Map.put(key, tmp.get(key));
+ tmp.remove(key);
+ }
+
+ orderedRules.put(step1Token, currentStep1Map);
+ }
+}
diff --git a/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedWinPhoneDeviceBuilder.java b/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedWinPhoneDeviceBuilder.java
new file mode 100644
index 0000000..5e78531
--- /dev/null
+++ b/src/org/openddr/simpleapi/oddr/builder/vitamineddevice/VitaminedWinPhoneDeviceBuilder.java
@@ -0,0 +1,178 @@
+/**
+ * Copyright 2012 Fundación CTIC
+ * This software is distributed under the terms of the GNU Lesser General Public License.
+ *
+ *
+ * This file is part of OpenDDR Simple APIs.
+ * OpenDDR Simple APIs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * OpenDDR Simple APIs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Simple APIs. If not, see .
+ *
+ * @author José Quiroga Álvarez
+ * @author Diego Martínez Ballesteros
+ *
+ */
+package org.openddr.simpleapi.oddr.builder.vitamineddevice;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.openddr.simpleapi.oddr.model.device.Device;
+import org.openddr.simpleapi.oddr.model.UserAgent;
+
+public class VitaminedWinPhoneDeviceBuilder extends VitaminedOrderedTokenDeviceBuilder {
+
+ private Map devices;
+
+ public VitaminedWinPhoneDeviceBuilder() {
+ super();
+ }
+
+ public boolean canBuild(UserAgent userAgent) {
+ if (userAgent.containsWindowsPhone()) {
+ return true;
+
+ } else {
+ return false;
+ }
+ }
+
+ public Device build(UserAgent userAgent, int confidenceTreshold) {
+ ArrayList foundDevices = new ArrayList();
+ Iterator it = orderedRules.keySet().iterator();
+ while (it.hasNext()) {
+ String tokenString = (String) it.next();
+ Token tokenInstance = tokensCache.get(tokenString); //get the token by key
+ Device d = elaborateWinPhoneDeviceWithToken(userAgent, tokenInstance);
+ if (d != null) {
+ if (d.getConfidence() > confidenceTreshold) {
+ return d;
+
+ } else {
+ if (d.getConfidence() > 0) {
+ foundDevices.add(d);
+ }
+ }
+ }
+ }
+ if (foundDevices.size() > 0) {
+ Collections.sort(foundDevices, Collections.reverseOrder());
+ return foundDevices.get(0);
+ }
+ return null;
+ }
+
+ public void putDevice(String device, List initProperties) {
+ orderedRules.put(initProperties.get(0), device);
+ }
+
+ private Device elaborateWinPhoneDeviceWithToken(UserAgent userAgent, Token tokenInst) {
+ if (userAgent.hasMozillaPattern() || userAgent.hasOperaPattern()) {
+ int subtract = 0;
+ Pattern loosePattern = tokenInst.getLooseMidPattern();
+
+ if (!loosePattern.matcher(userAgent.getCompleteUserAgent()).matches()) {
+ return null;
+ }
+
+ Pattern currentPattern = null;
+
+ if (userAgent.hasOperaPattern()) {
+ subtract += 10;
+ }
+
+ for (int i = 0; i <= 1; i++) {
+
+ if (i == 1) currentPattern = tokenInst.getLooseTailPattern();
+ else currentPattern = tokenInst.getTailPattern();
+ if (userAgent.getPatternElementsInside() != null && currentPattern.matcher(userAgent.getPatternElementsInside()).matches()) {
+ String deviceId = (String) orderedRules.get(tokenInst.getId());
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(100 - subtract);
+ return retDevice;
+ }
+ }
+ if (userAgent.getPatternElementsPre() != null && currentPattern.matcher(userAgent.getPatternElementsPre()).matches()) {
+ String deviceId = (String) orderedRules.get(tokenInst.getId());
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(95 - subtract);
+ return retDevice;
+ }
+ }
+
+ if (i == 1) currentPattern = tokenInst.getLooseSemicolonPattern();
+ else currentPattern = tokenInst.getSemicolonPattern();
+ if (userAgent.getPatternElementsInside() != null && currentPattern.matcher(userAgent.getPatternElementsInside()).matches()) {
+ String deviceId = (String) orderedRules.get(tokenInst.getId());
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(90 - subtract);
+ return retDevice;
+ }
+ }
+
+ if (i == 1) currentPattern = tokenInst.getLooseMidPattern();
+ else currentPattern = tokenInst.getMidPattern();
+ if (userAgent.getPatternElementsInside() != null && currentPattern.matcher(userAgent.getPatternElementsInside()).matches()) {
+ String deviceId = (String) orderedRules.get(tokenInst.getId());
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(80 - subtract);
+ return retDevice;
+ }
+ }
+ if (userAgent.getPatternElementsPre() != null && currentPattern.matcher(userAgent.getPatternElementsPre()).matches()) {
+ String deviceId = (String) orderedRules.get(tokenInst.getId());
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(80 - subtract);
+ return retDevice;
+ }
+ }
+ if (userAgent.getPatternElementsPost() != null && currentPattern.matcher(userAgent.getPatternElementsPost()).matches()) {
+ String deviceId = (String) orderedRules.get(tokenInst.getId());
+
+ if (devices.containsKey(deviceId)) {
+ Device retDevice = (Device) devices.get(deviceId).clone();
+ retDevice.setConfidence(60 - subtract);
+ return retDevice;
+ }
+ }
+ subtract += 20;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void afterOderingCompleteInit(Map devices) {
+ this.devices = devices;
+ for (Object devIdObj : orderedRules.values()) {
+ String devId = (String) devIdObj;
+ if (!devices.containsKey(devId)) {
+ throw new IllegalStateException("unable to find device with id: " + devId + "in devices");
+ }
+ }
+ }
+}
diff --git a/src/org/openddr/simpleapi/oddr/identificator/BrowserIdentificator.java b/src/org/openddr/simpleapi/oddr/identificator/BrowserIdentificator.java
index a6fd945..940539d 100644
--- a/src/org/openddr/simpleapi/oddr/identificator/BrowserIdentificator.java
+++ b/src/org/openddr/simpleapi/oddr/identificator/BrowserIdentificator.java
@@ -31,8 +31,8 @@
public class BrowserIdentificator implements Identificator {
- private Builder[] builders;
- private Map browserCapabilities;
+ protected Builder[] builders;
+ protected Map browserCapabilities;
protected final Logger logger = LoggerFactory.getLogger(getClass());
public BrowserIdentificator(Builder[] builders, Map browserCapabilities) {
@@ -77,7 +77,7 @@ public Browser get(UserAgent userAgent, int confidenceTreshold) {
return null;
}
- private String getClosestKnownBrowserID(String actualBrowserID) {
+ protected String getClosestKnownBrowserID(String actualBrowserID) {
if (actualBrowserID == null) {
return null;
}
diff --git a/src/org/openddr/simpleapi/oddr/identificator/CachedBrowserIdentificator.java b/src/org/openddr/simpleapi/oddr/identificator/CachedBrowserIdentificator.java
new file mode 100644
index 0000000..142dfa8
--- /dev/null
+++ b/src/org/openddr/simpleapi/oddr/identificator/CachedBrowserIdentificator.java
@@ -0,0 +1,131 @@
+/**
+ * Copyright 2012 Fundación CTIC
+ * This software is distributed under the terms of the GNU Lesser General Public License.
+ *
+ *
+ * This file is part of OpenDDR Simple APIs.
+ * OpenDDR Simple APIs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * OpenDDR Simple APIs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Simple APIs. If not, see .
+ *
+ * @author José Quiroga Álvarez
+ * @author Diego Martínez Ballesteros
+ *
+ */
+package org.openddr.simpleapi.oddr.identificator;
+
+import java.util.Map;
+
+import org.apache.commons.collections.map.LRUMap;
+import org.openddr.simpleapi.oddr.builder.Builder;
+import org.openddr.simpleapi.oddr.model.UserAgent;
+import org.openddr.simpleapi.oddr.model.UserAgentFactory;
+import org.openddr.simpleapi.oddr.model.browser.Browser;
+import org.w3c.ddr.simple.Evidence;
+
+public class CachedBrowserIdentificator extends BrowserIdentificator implements
+ CachedIdentificator {
+
+ // caches
+ private LRUMap browserCache;
+ private LRUMap notFoundUA;
+
+ public CachedBrowserIdentificator(Builder[] builders,
+ Map browserCapabilities, Integer maxCacheSize, Integer maxNotFoundUASize) {
+ super(builders, browserCapabilities);
+ browserCache = new LRUMap(maxCacheSize);
+ notFoundUA = new LRUMap(maxNotFoundUASize);
+ }
+
+ public Browser get(String userAgent, int confidenceTreshold) {
+ return get(UserAgentFactory.newUserAgent(userAgent), confidenceTreshold);
+ }
+
+ // XXX to be refined, this should NOT be the main entry point, we should use
+ // a set of evidence derivation
+ public Browser get(Evidence evdnc, int threshold) {
+ UserAgent ua = UserAgentFactory.newBrowserUserAgent(evdnc);
+
+ if (ua != null) {
+ return get(ua, threshold);
+ }
+
+ return null;
+ }
+
+ @Override
+ public Browser get(UserAgent userAgent, int confidenceTreshold) {
+ // check if the device is in the cache
+ Browser foundBrowser = (Browser) getFromCache(userAgent);
+
+ if (foundBrowser == null) {
+ if (isUaNotFound(userAgent)) {
+ return null;
+ }
+ for (Builder builder : builders) {
+ if (builder.canBuild(userAgent)) {
+ Browser browser = (Browser) builder.build(userAgent,
+ confidenceTreshold);
+ if (browser != null) {
+ if (browserCapabilities != null) {
+ String bestID = getClosestKnownBrowserID(browser
+ .getId());
+ if (bestID != null) {
+ browser.putPropertiesMap(browserCapabilities
+ .get(bestID).getPropertiesMap());
+ if (!bestID.equals(browser.getId())) {
+ browser.setConfidence(browser
+ .getConfidence() - 15);
+ }
+ }
+ }
+ // add browser to the cache
+ addToCache(userAgent, browser);
+ return browser;
+ }
+ }
+ }
+ } else {
+ return foundBrowser;
+ }
+ addNotFoundUa(userAgent);
+ return null;
+ }
+
+ public Boolean isUaNotFound(UserAgent userAgent) {
+ if (notFoundUA.get(userAgent.getCompleteUserAgent()) != null) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void addNotFoundUa(UserAgent userAgent) {
+ notFoundUA.put(userAgent.getCompleteUserAgent(), false);
+ }
+
+ @Override
+ public Object getFromCache(UserAgent userAgent) {
+ Browser cachedBrowser = (Browser) browserCache.get(userAgent
+ .getCompleteUserAgent());
+ if (cachedBrowser == null) {
+ return null;
+ }
+ return cachedBrowser;
+ }
+
+ @Override
+ public void addToCache(UserAgent userAgent, Object browser) {
+ browserCache.put(userAgent.getCompleteUserAgent(), browser);
+
+ }
+}
diff --git a/src/org/openddr/simpleapi/oddr/identificator/CachedDeviceIdentificator.java b/src/org/openddr/simpleapi/oddr/identificator/CachedDeviceIdentificator.java
new file mode 100644
index 0000000..93a02d2
--- /dev/null
+++ b/src/org/openddr/simpleapi/oddr/identificator/CachedDeviceIdentificator.java
@@ -0,0 +1,141 @@
+/**
+ * Copyright 2012 Fundación CTIC
+ * This software is distributed under the terms of the GNU Lesser General Public License.
+ *
+ *
+ * This file is part of OpenDDR Simple APIs.
+ * OpenDDR Simple APIs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * OpenDDR Simple APIs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Simple APIs. If not, see .
+ *
+ * @author José Quiroga Álvarez
+ * @author Diego Martínez Ballesteros
+ *
+ */
+package org.openddr.simpleapi.oddr.identificator;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.collections.map.LRUMap;
+import org.openddr.simpleapi.oddr.builder.device.DeviceBuilder;
+import org.openddr.simpleapi.oddr.model.UserAgent;
+import org.openddr.simpleapi.oddr.model.device.Device;
+
+public class CachedDeviceIdentificator extends DeviceIdentificator implements
+ CachedIdentificator {
+
+ // caches
+ private LRUMap deviceCache;
+ private LRUMap notFoundUA;
+
+ public CachedDeviceIdentificator(DeviceBuilder[] builders,
+ Map devices, Integer maxCacheSize, Integer maxNotFoundUASize) {
+ super(builders, devices);
+ deviceCache = new LRUMap(maxCacheSize);
+ notFoundUA = new LRUMap(maxNotFoundUASize);
+ }
+
+ @Override
+ public Device get(UserAgent userAgent, int confidenceTreshold) {
+ List foundDevices = new ArrayList();
+ // check if the device is in the cache
+ Device foundDevice = (Device) getFromCache(userAgent);
+
+ // if not, build the device
+ if (foundDevice == null) {
+ if (isUaNotFound(userAgent)) {
+ return null;
+ }
+ for (DeviceBuilder deviceBuilder : builders) {
+ if (deviceBuilder.canBuild(userAgent)) {
+ Device device = (Device) deviceBuilder.build(userAgent,
+ confidenceTreshold);
+ if (device != null) {
+ String parentId = device.getParentId();
+ Device parentDevice = null;
+ Set propertiesSet = null;
+ Iterator it = null;
+ while (!"root".equals(parentId)) {
+ parentDevice = (Device) devices.get(parentId);
+ propertiesSet = parentDevice.getPropertiesMap()
+ .entrySet();
+ it = propertiesSet.iterator();
+ while (it.hasNext()) {
+ Map.Entry entry = (Map.Entry) it.next();
+ if (!device.containsProperty((String) entry
+ .getKey())) {
+ device.putProperty((String) entry.getKey(),
+ (String) entry.getValue());
+ }
+ }
+ parentId = parentDevice.getParentId();
+ }
+ foundDevices.add(device);
+ if (device.getConfidence() >= confidenceTreshold) {
+ foundDevice = device;
+ break;
+ }
+ }
+ }
+ }
+
+ if (foundDevice != null) {
+ // add the device to the cache
+ addToCache(userAgent, foundDevice);
+ } else {
+ if (foundDevices.isEmpty()) {
+ addNotFoundUa(userAgent);
+ return null;
+ }
+
+ Collections.sort(foundDevices, Collections.reverseOrder());
+ foundDevice = foundDevices.get(0);
+ // add the device to the cache
+ addToCache(userAgent, foundDevice);
+ }
+ }
+ return foundDevice;
+ }
+
+ @Override
+ public Boolean isUaNotFound(UserAgent userAgent) {
+ if (notFoundUA.get(userAgent.getCompleteUserAgent()) != null) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void addNotFoundUa(UserAgent userAgent) {
+ notFoundUA.put(userAgent.getCompleteUserAgent(), false);
+ }
+
+ @Override
+ public Object getFromCache(UserAgent userAgent) {
+ Device cachedDevice = (Device) deviceCache.get(userAgent
+ .getCompleteUserAgent());
+ if (cachedDevice == null) {
+ return null;
+ }
+ return cachedDevice;
+ }
+
+ @Override
+ public void addToCache(UserAgent userAgent, Object device) {
+ deviceCache.put(userAgent.getCompleteUserAgent(), device);
+ }
+}
diff --git a/src/org/openddr/simpleapi/oddr/identificator/CachedIdentificator.java b/src/org/openddr/simpleapi/oddr/identificator/CachedIdentificator.java
new file mode 100644
index 0000000..74f5771
--- /dev/null
+++ b/src/org/openddr/simpleapi/oddr/identificator/CachedIdentificator.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright 2012 Fundación CTIC
+ * This software is distributed under the terms of the GNU Lesser General Public License.
+ *
+ *
+ * This file is part of OpenDDR Simple APIs.
+ * OpenDDR Simple APIs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * OpenDDR Simple APIs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Simple APIs. If not, see .
+ *
+ * @author José Quiroga Álvarez
+ * @author Diego Martínez Ballesteros
+ *
+ */
+package org.openddr.simpleapi.oddr.identificator;
+
+import org.openddr.simpleapi.oddr.model.UserAgent;
+
+public interface CachedIdentificator {
+
+ /**
+ * Check if the request come from an unknown device.
+ *
+ * @param userAgent
+ * UserAgent of the device
+ * @return true
if the UA is in the cache of not found devices
+ * or false
in other case
+ */
+ public Boolean isUaNotFound(UserAgent userAgent);
+
+ /**
+ * Add a device to the cache of not found devices
+ *
+ * @param userAgent
+ * UserAgent of the device
+ */
+ public void addNotFoundUa(UserAgent userAgent);
+
+ /**
+ * Get from the cache
+ *
+ * @param userAgent
+ * UserAgent of the device
+ * @return a Object
if the UA is in the cache or
+ * null
in other case
+ */
+ public Object getFromCache(UserAgent userAgent);
+
+ /**
+ * Add to the cache
+ *
+ * @param userAgent
+ * UserAgent of the device
+ * @param object
+ *
+ */
+ public void addToCache(UserAgent userAgent, Object object);
+}
diff --git a/src/org/openddr/simpleapi/oddr/identificator/CachedOSIdentificator.java b/src/org/openddr/simpleapi/oddr/identificator/CachedOSIdentificator.java
new file mode 100644
index 0000000..36e1993
--- /dev/null
+++ b/src/org/openddr/simpleapi/oddr/identificator/CachedOSIdentificator.java
@@ -0,0 +1,115 @@
+/**
+ * Copyright 2012 Fundación CTIC
+ * This software is distributed under the terms of the GNU Lesser General Public License.
+ *
+ *
+ * This file is part of OpenDDR Simple APIs.
+ * OpenDDR Simple APIs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * OpenDDR Simple APIs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Simple APIs. If not, see .
+ *
+ * @author José Quiroga Álvarez
+ * @author Diego Martínez Ballesteros
+ *
+ */
+package org.openddr.simpleapi.oddr.identificator;
+
+import java.util.Map;
+
+import org.apache.commons.collections.map.LRUMap;
+import org.openddr.simpleapi.oddr.builder.Builder;
+import org.openddr.simpleapi.oddr.model.UserAgent;
+import org.openddr.simpleapi.oddr.model.os.OperatingSystem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CachedOSIdentificator extends OSIdentificator implements
+ CachedIdentificator {
+
+ // caches
+ private LRUMap osCache;
+ private LRUMap notFoundUA;
+
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ public CachedOSIdentificator(Builder[] builders,
+ Map operatingSystemCapabilities, Integer maxCacheSize, Integer maxNotFoundUASize) {
+ super(builders, operatingSystemCapabilities);
+ osCache = new LRUMap(maxCacheSize);
+ notFoundUA = new LRUMap(maxNotFoundUASize);
+ }
+
+ @Override
+ public OperatingSystem get(UserAgent userAgent, int confidenceTreshold) {
+ // check if the device is in the cache
+ OperatingSystem foundOs = (OperatingSystem) getFromCache(userAgent);
+
+ if (foundOs == null) {
+ if (isUaNotFound(userAgent)) {
+ return null;
+ }
+ for (Builder builder : builders) {
+ if (builder.canBuild(userAgent)) {
+ OperatingSystem os = (OperatingSystem) builder.build(
+ userAgent, confidenceTreshold);
+ if (os != null) {
+ if (operatingSystemCapabilities != null) {
+ String bestID = getClosestKnownBrowserID(os.getId());
+ if (bestID != null) {
+ os.putPropertiesMap(operatingSystemCapabilities
+ .get(bestID).getPropertiesMap());
+ if (!bestID.equals(os.getId())) {
+ os.setConfidence(os.getConfidence() - 15);
+ }
+ }
+ }
+ // add browser to the cache
+ addToCache(userAgent, os);
+ return os;
+ }
+ }
+ }
+ } else {
+ return foundOs;
+ }
+ addNotFoundUa(userAgent);
+ return null;
+ }
+
+ public Boolean isUaNotFound(UserAgent userAgent) {
+ if (notFoundUA.get(userAgent.getCompleteUserAgent()) != null) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void addNotFoundUa(UserAgent userAgent) {
+ notFoundUA.put(userAgent.getCompleteUserAgent(), false);
+ }
+
+ @Override
+ public Object getFromCache(UserAgent userAgent) {
+ OperatingSystem cachedOs = (OperatingSystem) osCache.get(userAgent
+ .getCompleteUserAgent());
+ if (cachedOs == null) {
+ return null;
+ }
+ return cachedOs;
+ }
+
+ @Override
+ public void addToCache(UserAgent userAgent, Object os) {
+ osCache.put(userAgent.getCompleteUserAgent(), os);
+
+ }
+}
diff --git a/src/org/openddr/simpleapi/oddr/identificator/DeviceIdentificator.java b/src/org/openddr/simpleapi/oddr/identificator/DeviceIdentificator.java
index 35f7bf4..77d41f9 100644
--- a/src/org/openddr/simpleapi/oddr/identificator/DeviceIdentificator.java
+++ b/src/org/openddr/simpleapi/oddr/identificator/DeviceIdentificator.java
@@ -34,8 +34,8 @@
public class DeviceIdentificator implements Identificator {
- private DeviceBuilder[] builders;
- private Map devices;
+ protected DeviceBuilder[] builders;
+ protected Map devices;
public DeviceIdentificator(DeviceBuilder[] builders, Map devices) {
this.builders = builders;
diff --git a/src/org/openddr/simpleapi/oddr/identificator/OSIdentificator.java b/src/org/openddr/simpleapi/oddr/identificator/OSIdentificator.java
index ab1dc1b..fee305a 100644
--- a/src/org/openddr/simpleapi/oddr/identificator/OSIdentificator.java
+++ b/src/org/openddr/simpleapi/oddr/identificator/OSIdentificator.java
@@ -31,8 +31,8 @@
public class OSIdentificator implements Identificator {
- private Builder[] builders;
- private Map operatingSystemCapabilities;
+ protected Builder[] builders;
+ protected Map operatingSystemCapabilities;
protected final Logger logger = LoggerFactory.getLogger(getClass());
public OSIdentificator(Builder[] builders, Map operatingSystemCapabilities) {
@@ -74,7 +74,7 @@ public OperatingSystem get(UserAgent userAgent, int confidenceTreshold) {
return null;
}
- private String getClosestKnownBrowserID(String actualOperatingSystemID) {
+ protected String getClosestKnownBrowserID(String actualOperatingSystemID) {
if (actualOperatingSystemID == null) {
return null;
}