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

Add Brand & Model Parser in DeviceParser #37

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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import ua_parser.Client;
System.out.println(c.os.minor); // => "1"

System.out.println(c.device.family); // => "iPhone"
System.out.println(c.device.brand); // => "Apple"
System.out.println(c.device.model); // => "iPhone"
```

## Publish to Sonatype OSSRH and Maven Central Repository
Expand Down
27 changes: 24 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
<packaging>jar</packaging>
<version>1.4.1-SNAPSHOT</version>
<name>User Agent Parser for Java</name>

<description>
Java implementation of the UA Parser library. Derives operating system, browser and device
metadata from a user-agent string. This library consumes the regular expression
Expand Down Expand Up @@ -93,9 +92,31 @@
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.allen.capturewebdata.Main</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>



<profiles>
<profile>
<id>ossrh</id>
Expand All @@ -118,7 +139,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.0.0</version>
<version>3.0.1</version>
<executions>
<execution>
<id>attach-javadocs</id>
Expand Down
28 changes: 19 additions & 9 deletions src/main/java/ua_parser/Device.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,32 +25,42 @@
*/
public class Device {
public final String family;
public final String brand;
public final String model;

public Device(String family) {
this.family = family;
}
public Device(String family, String brand, String model) {
this.family = family;
this.brand = brand;
this.model = model;
}

public static Device fromMap(Map<String, String> m) {
return new Device((String) m.get("family"));
return new Device(m.get("family"), m.get("brand"), m.get("model"));
}

@Override
public boolean equals(Object other) {
if (other == this) return true;
if (!(other instanceof Device)) return false;

Device o = (Device) other;
return (this.family != null && this.family.equals(o.family)) || this.family == o.family;
return (((this.family != null && this.family.equals(o.family)) || this.family == o.family) &&
((this.brand != null && this.brand.equals(o.brand)) || this.brand == o.brand ) &&
((this.model != null && this.model.equals(o.model)) || this.model == o.model ));
}

@Override
public int hashCode() {
return family == null ? 0 : family.hashCode();
int h = family == null ? 0 : family.hashCode();
h += brand == null ? 0 : brand.hashCode();
h += model == null ? 0 : model.hashCode();
return h;
}

@Override
public String toString() {
return String.format("{\"family\": %s}",
family == null ? Constants.EMPTY_STRING : '"' + family + '"');
return String.format("{\"family\": %s, \"brand\": %s, \"model\": %s}",
family == null ? Constants.EMPTY_STRING : '"' + family + '"',
brand == null ? Constants.EMPTY_STRING : '"' + brand + '"',
model == null ? Constants.EMPTY_STRING : '"' + model + '"');
}
}
108 changes: 70 additions & 38 deletions src/main/java/ua_parser/DeviceParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,17 @@ public Device parse(String agentString) {
return null;
}

String device = null;
Device device = null;
for (DevicePattern p : patterns) {
if ((device = p.match(agentString)) != null) {
break;
}
}
if (device == null) device = "Other";
if (device != null) {
return device;
}

return new Device(device);
return new Device("Other", null, null);
}

public static DeviceParser fromList(List<Map<String,String>> configList) {
Expand All @@ -60,59 +62,89 @@ public static DeviceParser fromList(List<Map<String,String>> configList) {

protected static DevicePattern patternFromMap(Map<String, String> configMap) {
String regex = configMap.get("regex");
String regex_flag = configMap.get("regex_flag");
Pattern pattern;
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"));
}
if (null != regex_flag && regex_flag.equals("i")) {
pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
}
else {
pattern = Pattern.compile(regex);
}

return new DevicePattern(pattern,
configMap.get("device_replacement"),
configMap.get("brand_replacement"),
configMap.get("model_replacement"));
}

protected static class DevicePattern {
private static final Pattern SUBSTITUTIONS_PATTERN = Pattern.compile("\\$\\d");
private final Pattern pattern;
private final String deviceReplacement;
private final String familyReplacement;
private final String brandReplacement;
private final String modelReplacement;
private final Pattern patternCut = Pattern.compile("(?=\\$([0-9]))");
private final Pattern patternNum = Pattern.compile(".*\\$([0-9]).*");

public DevicePattern(Pattern pattern, String deviceReplacement) {
public DevicePattern(Pattern pattern,
String familyReplacement,
String brandReplacement,
String modelReplacement) {
this.pattern = pattern;
this.deviceReplacement = deviceReplacement;
this.familyReplacement = familyReplacement;
this.brandReplacement = brandReplacement;
this.modelReplacement = modelReplacement;
}

public String match(String agentString) {
public Device match(String agentString) {
String family = null, brand = null, model = null;
Matcher matcher = pattern.matcher(agentString);

if (!matcher.find()) {
return null;
}
String device = null;
if (deviceReplacement != null) {
if (deviceReplacement.contains("$")) {
device = deviceReplacement;
for (String substitution : getSubstitutions(deviceReplacement)) {
int i = Integer.valueOf(substitution.substring(1));
String replacement = matcher.groupCount() >= i && matcher.group(i) != null
? Matcher.quoteReplacement(matcher.group(i)) : "";
device = device.replaceFirst("\\" + substitution, replacement);
}
device = device.trim();
} else {
device = deviceReplacement;
}
} else if (matcher.groupCount() >= 1) {
device = matcher.group(1);

family = multiReplace(familyReplacement, matcher);
family = "".equals(family) ? null : family;
if (familyReplacement == null && matcher.groupCount() > 0 && matcher.group(1) != null){
family = matcher.group(1);
}

return device;
brand = multiReplace(brandReplacement, matcher);
brand = "".equals(brand) ? null : brand;

model = multiReplace(modelReplacement, matcher);
model = "".equals(model) ? null : model;
if (modelReplacement == null && matcher.groupCount() > 0 && matcher.group(1) != null){
model = matcher.group(1);
}

//System.out.println("MATCHED:\"" + agentString + "\" WITH " + pattern + " RESULTING IN" + new Device(family, brand, model));
return new Device(family, brand, model);
}

private List<String> getSubstitutions(String deviceReplacement) {
Matcher matcher = SUBSTITUTIONS_PATTERN.matcher(deviceReplacement);
List<String> substitutions = new ArrayList<String>();
while (matcher.find()) {
substitutions.add(matcher.group());

private String multiReplace(String input, Matcher matcher){
String output = "";

if (null != input) {
String[] cut = patternCut.split(input);
for (int i=0; i<cut.length; i++) {
Matcher m = patternNum.matcher(cut[i]);
if (m.find()) {
int markerPos = Integer.parseInt(m.group(1));
if (matcher.find(0) && matcher.groupCount() >= markerPos && matcher.group(markerPos) != null) {
cut[i] = cut[i].replaceFirst("\\$"+markerPos, Matcher.quoteReplacement(matcher.group(markerPos)));
}
else {
cut[i] = cut[i].replaceFirst("\\$"+markerPos, "");
}
}
output += cut[i];
}
}
return substitutions;
return output.trim();
}

}

}
1 change: 0 additions & 1 deletion src/main/java/ua_parser/UserAgentParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
*/
public class UserAgentParser {
private final List<UAPattern> patterns;

public UserAgentParser(List<UAPattern> patterns) {
this.patterns = patterns;
}
Expand Down
8 changes: 5 additions & 3 deletions src/test/java/ua_parser/DeviceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ public class DeviceTest extends DataTest<Device> {
@Override
protected Device getRandomInstance(long seed, StringGenerator g) {
random.setSeed(seed);
String family = g.getString(256);
return new Device(family);
String family = g.getString(256),
brand = (random.nextBoolean() ? g.getString(8): null),
model = (random.nextBoolean() ? g.getString(8): null);
return new Device(family,brand,model);
}

@Override
protected Device getBlankInstance() {
return new Device(null);
return new Device(null,null,null);
}
}
4 changes: 2 additions & 2 deletions src/test/java/ua_parser/ParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,10 @@ public void testParseAll() {

Client expected1 = new Client(new UserAgent("Firefox", "3", "5", "5"),
new OS("Mac OS X", "10", "4", null, null),
new Device("Other"));
new Device("Other",null,null));
Client expected2 = new Client(new UserAgent("Mobile Safari", "5", "1", null),
new OS("iOS", "5", "1", "1", null),
new Device("iPhone"));
new Device("iPhone","Apple","iPhone"));

assertThat(parser.parse(agentString1), is(expected1));
assertThat(parser.parse(agentString2), is(expected2));
Expand Down