Skip to content

Commit

Permalink
current state brand model parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
commenthol committed Nov 7, 2014
1 parent bf928bb commit 868f929
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 32 deletions.
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"
```

Author:
Expand Down
27 changes: 19 additions & 8 deletions src/main/java/ua_parser/Device.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,17 @@
*/
public class Device {
public final String family;
public final String brand;
public final String model;

public Device(String 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
Expand All @@ -40,17 +44,24 @@ public boolean equals(Object other) {
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 + '"');
}
}
86 changes: 68 additions & 18 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,41 +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");
}
return new DevicePattern(Pattern.compile(regex),
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 final Pattern pattern;
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 familyReplacement) {
public DevicePattern(Pattern pattern,
String familyReplacement,
String brandReplacement,
String modelReplacement) {
this.pattern = pattern;
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;
}

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

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);
}

String family = null;
if (familyReplacement != null) {
if (familyReplacement.contains("$1") && matcher.groupCount() >= 1 && matcher.group(1) != null) {
family = familyReplacement.replaceFirst("\\$1", Matcher.quoteReplacement(matcher.group(1)));
} else {
family = familyReplacement;
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];
}
} else if (matcher.groupCount() >= 1) {
family = matcher.group(1);
}
return family;
return output.trim();
}
}

}
}
1 change: 0 additions & 1 deletion src/test/java/ua_parser/DataTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

import org.junit.Test;
import org.junit.Before;
import org.yaml.snakeyaml.Yaml;

import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
Expand Down
9 changes: 6 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,15 @@ 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);
}
}
7 changes: 5 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 Expand Up @@ -114,6 +114,7 @@ public void testInvalidConfigThrows() throws Exception {
parserFromStringConfig("user_agent_parsers:\n - family_replacement: 'a'");
}

@SuppressWarnings({ "rawtypes", "unchecked" })
void testUserAgentFromYaml(String filename) {
InputStream yamlStream = this.getClass().getResourceAsStream(TEST_RESOURCE_PATH + filename);

Expand All @@ -127,6 +128,7 @@ void testUserAgentFromYaml(String filename) {
}
}

@SuppressWarnings({ "rawtypes", "unchecked" })
void testOSFromYaml(String filename) {
InputStream yamlStream = this.getClass().getResourceAsStream(TEST_RESOURCE_PATH + filename);

Expand All @@ -140,6 +142,7 @@ void testOSFromYaml(String filename) {
}
}

@SuppressWarnings({ "rawtypes", "unchecked" })
void testDeviceFromYaml(String filename) {
InputStream yamlStream = this.getClass().getResourceAsStream(TEST_RESOURCE_PATH + filename);

Expand Down

0 comments on commit 868f929

Please sign in to comment.