Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: move YAML parsing to compile time #83

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 10 additions & 12 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<groupId>com.github.ua-parser</groupId>
<artifactId>uap-java</artifactId>
<packaging>jar</packaging>
<version>1.5.5-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
<name>User Agent Parser for Java</name>

<description>
Expand Down Expand Up @@ -56,17 +56,14 @@
</properties>

<build>
<resources>
<resource>
<testResources>
<testResource>
<targetPath>ua_parser</targetPath>
<directory>${basedir}/uap-core</directory>
<includes>
<include>regexes.yaml</include>
</includes>
</resource>
</resources>

<testResources>
</testResource>
<testResource>
<targetPath>ua_parser</targetPath>
<directory>${basedir}/uap-core/test_resources</directory>
Expand Down Expand Up @@ -152,16 +149,17 @@
</profiles>

<dependencies>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.33</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
Expand Down
12 changes: 0 additions & 12 deletions src/main/java/ua_parser/CachingParser.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package ua_parser;

import java.io.InputStream;
import java.util.Map;

import org.apache.commons.collections4.map.LRUMap;
Expand Down Expand Up @@ -38,23 +37,12 @@ public CachingParser() {
super();
}

public CachingParser(InputStream regexYaml) {
super(regexYaml);
}

public CachingParser(int cacheSize) {
super();
assert cacheSize > 0: INVALID_CACHE_SIZE_ERROR_MESSAGE;
this.cacheSize = cacheSize;
}

public CachingParser(InputStream regexYaml, int cacheSize) {
super(regexYaml);
assert cacheSize > 0: INVALID_CACHE_SIZE_ERROR_MESSAGE;
this.cacheSize = cacheSize;
}


// ------------------------------------------

@Override
Expand Down
29 changes: 2 additions & 27 deletions src/main/java/ua_parser/DeviceParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand All @@ -32,8 +30,8 @@ public class DeviceParser {

private final List<DevicePattern> patterns;

public DeviceParser(List<DevicePattern> patterns) {
this.patterns = patterns;
public DeviceParser() {
this.patterns = Regexes.getDevicePatterns();
}

public Device parse(String agentString) {
Expand All @@ -50,29 +48,6 @@ public Device parse(String agentString) {
return Device.OTHER;
}

/**
* Constructs a thread-safe DeviceParser.
* @param configList configure a device parser from a list of regexp hashmaps
* @return a device parser
*/
public static DeviceParser fromList(List<Map<String,String>> configList) {
List<DevicePattern> configPatterns = new ArrayList<>();
for (Map<String,String> configMap : configList) {
configPatterns.add(DeviceParser.patternFromMap(configMap));
}
return new DeviceParser(new CopyOnWriteArrayList<>(configPatterns));
}

protected static DevicePattern patternFromMap(Map<String, String> configMap) {
String regex = configMap.get("regex");
if (regex == null) {
throw new IllegalArgumentException("Device is missing regex");
}
Pattern pattern = "i".equals(configMap.get("regex_flag")) // no ohter flags used (by now)
? Pattern.compile(regex, Pattern.CASE_INSENSITIVE) : Pattern.compile(regex);
return new DevicePattern(pattern, configMap.get("device_replacement"));
}

protected static class DevicePattern {
private static final Pattern SUBSTITUTIONS_PATTERN = Pattern.compile("\\$\\d");
private final Pattern pattern;
Expand Down
34 changes: 2 additions & 32 deletions src/main/java/ua_parser/OSParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@

package ua_parser;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand All @@ -32,22 +29,8 @@ public class OSParser {

private final List<OSPattern> patterns;

public OSParser(List<OSPattern> patterns) {
this.patterns = patterns;
}

/**
* Constructs a thread-safe OSParser.
* @param configList configure an operating system parser from a list of regexp hashmaps
* @return operating system parser
*/
public static OSParser fromList(List<Map<String,String>> configList) {
List<OSPattern> configPatterns = new ArrayList<>();

for (Map<String,String> configMap : configList) {
configPatterns.add(OSParser.patternFromMap(configMap));
}
return new OSParser(new CopyOnWriteArrayList<>(configPatterns));
public OSParser() {
this.patterns = Regexes.getOSPatterns();
}

public OS parse(String agentString) {
Expand All @@ -64,19 +47,6 @@ public OS parse(String agentString) {
return OS.OTHER;
}

protected static OSPattern patternFromMap(Map<String, String> configMap) {
String regex = configMap.get("regex");
if (regex == null) {
throw new IllegalArgumentException("OS is missing regex");
}

return(new OSPattern(Pattern.compile(regex),
configMap.get("os_replacement"),
configMap.get("os_v1_replacement"),
configMap.get("os_v2_replacement"),
configMap.get("os_v3_replacement")));
}

protected static class OSPattern {
private final Pattern pattern;
private final String osReplacement, v1Replacement, v2Replacement, v3Replacement;
Expand Down
82 changes: 6 additions & 76 deletions src/main/java/ua_parser/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,67 +16,22 @@

package ua_parser;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;

import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;

/**
* Java implementation of <a href="https://github.com/ua-parser">UA Parser</a>
*
* @author Steve Jiang (@sjiang) &lt;gh at iamsteve com&gt;
*/
public class Parser {

private static final String REGEX_YAML_PATH = "/ua_parser/regexes.yaml";

public static final int CODE_POINT_LIMIT = 3455764;
private UserAgentParser uaParser;
private OSParser osParser;
private DeviceParser deviceParser;

/**
* Creates a parser using the regular expression yaml file bundled in the jar.
* @throws RuntimeException if there's a problem reading the file from the classpath
* Creates a parser
*/
public Parser() {
this(getDefaultLoaderOptions());
}

/**
* Creates a parser using the regular expression yaml file bundled in the jar.
*
* @param loaderOptions configuration for loading parser safe limits.
* @throws RuntimeException if there's a problem reading the file from the classpath.
*/
public Parser(LoaderOptions loaderOptions) {
try (InputStream is = Parser.class.getResourceAsStream(REGEX_YAML_PATH)) {
initialize(is, loaderOptions);
} catch (IOException e) {
throw new RuntimeException("failed to initialize parser from regexes.yaml bundled in jar", e);
}
}

/**
* Creates a parser using the supplied regular expression yaml file.
* It is the responsibility of the caller to close the InputStream after construction.
* @param regexYaml the yaml file containing the regular expressions
*/
public Parser(InputStream regexYaml) {
this(regexYaml, getDefaultLoaderOptions());
}
/**
* Creates a parser using the supplied regular expression yaml file.
* It is the responsibility of the caller to close the InputStream after construction.
* @param regexYaml the yaml file containing the regular expressions
* @param loaderOptions configuration for loading parser safe limits.
*/
public Parser(InputStream regexYaml, LoaderOptions loaderOptions) {
initialize(regexYaml, loaderOptions);
initialize();
}

public Client parse(String agentString) {
Expand All @@ -98,34 +53,9 @@ public OS parseOS(String agentString) {
return osParser.parse(agentString);
}

public static LoaderOptions getDefaultLoaderOptions(){
LoaderOptions options = new LoaderOptions();
options.setCodePointLimit(CODE_POINT_LIMIT);
return options;
}

private void initialize(InputStream regexYaml, LoaderOptions loaderOptions) {
Yaml yaml = new Yaml(new SafeConstructor(loaderOptions));

@SuppressWarnings("unchecked")
Map<String,List<Map<String,String>>> regexConfig = (Map<String,List<Map<String,String>>>) yaml.load(regexYaml);

List<Map<String,String>> uaParserConfigs = regexConfig.get("user_agent_parsers");
if (uaParserConfigs == null) {
throw new IllegalArgumentException("user_agent_parsers is missing from yaml");
}
uaParser = UserAgentParser.fromList(uaParserConfigs);

List<Map<String,String>> osParserConfigs = regexConfig.get("os_parsers");
if (osParserConfigs == null) {
throw new IllegalArgumentException("os_parsers is missing from yaml");
}
osParser = OSParser.fromList(osParserConfigs);

List<Map<String,String>> deviceParserConfigs = regexConfig.get("device_parsers");
if (deviceParserConfigs == null) {
throw new IllegalArgumentException("device_parsers is missing from yaml");
}
deviceParser = DeviceParser.fromList(deviceParserConfigs);
private void initialize() {
uaParser = new UserAgentParser();
osParser = new OSParser();
deviceParser = new DeviceParser();
}
}
Loading