Skip to content

Commit b2607b0

Browse files
committed
#22 literal indexes increasing when reload
1 parent d7b3769 commit b2607b0

File tree

9 files changed

+92
-44
lines changed

9 files changed

+92
-44
lines changed

CHANGELOG.txt

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
1.2.11 - 12 July 2019
2+
- #22 literal indexes increasing when reload
3+
14
1.2.10 - 8 July 2019
25
- Updated to browscap 6000034
36
- Updated unit tests accordingly
@@ -43,7 +46,7 @@
4346
Performance updates for memory footprint and startup time.
4447

4548
1.1.0 - 24 Nov 2017
46-
Intregrated PR 2; the browscap fields are now configurable in the constructor of the parser.
49+
Integrated PR 2; the browscap fields are now configurable in the constructor of the parser.
4750
Updated documentation and unit tests.
4851

4952
1.0.4 - 5 Oct 2017

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ Add this to the dependencies in your pom.xml.
4242
<dependency>
4343
<groupId>com.blueconic</groupId>
4444
<artifactId>browscap-java</artifactId>
45-
<version>1.2.10</version>
45+
<version>1.2.11</version>
4646
</dependency>
4747
```
4848

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<groupId>com.blueconic</groupId>
55
<artifactId>browscap-java</artifactId>
66
<packaging>jar</packaging>
7-
<version>1.2.10</version>
7+
<version>1.2.11</version>
88
<name>browscap-java</name>
99
<description>A blazingly fast and memory efficient Java client on top of the BrowsCap CSV source files.</description>
1010

src/main/java/com/blueconic/browscap/impl/SearchableString.java

+22-16
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ class SearchableString {
2323
/**
2424
* Creates a new instance for the specified string value.
2525
* @param stringValue The user agent string
26+
* @param maxIndex The number of unique literals
2627
*/
27-
SearchableString(final String stringValue) {
28+
SearchableString(final String stringValue, final int maxIndex) {
2829
myChars = stringValue.toCharArray();
29-
final int max = Literal.getNumberOfInstances() + 1;
30-
myIndices = new int[max][];
30+
myIndices = new int[maxIndex][];
3131
myBuffer = new int[myChars.length];
3232
}
3333

@@ -206,9 +206,6 @@ void set(final int index, final boolean flag) {
206206
*/
207207
class Literal {
208208

209-
// Keep track of the total number of instances
210-
private static final AtomicInteger NUMBER_OF_INSTANCES = new AtomicInteger();
211-
212209
// The actual string data
213210
private final char[] myCharacters;
214211

@@ -218,10 +215,11 @@ class Literal {
218215
/**
219216
* Creates a new instance with the specified non-empty value.
220217
* @param value The String value
218+
* @param index The unique index for this instance
221219
*/
222-
Literal(final String value) {
220+
Literal(final String value, final int index) {
223221
myCharacters = value.toCharArray();
224-
myIndex = NUMBER_OF_INSTANCES.getAndAdd(1);
222+
myIndex = index;
225223
}
226224

227225
/**
@@ -273,14 +271,6 @@ final int getIndex() {
273271
return myIndex;
274272
}
275273

276-
/**
277-
* Returns the total number of literals.
278-
* @return the total number of literals.
279-
*/
280-
static int getNumberOfInstances() {
281-
return NUMBER_OF_INSTANCES.get();
282-
}
283-
284274
private static boolean contains(final char[] characters, final char value) {
285275

286276
for (final char c : characters) {
@@ -312,3 +302,19 @@ public String toString() {
312302
return new String(myCharacters);
313303
}
314304
}
305+
306+
class LiteralDomain {
307+
308+
// Keep track of the total number of instances
309+
private final AtomicInteger myNrOfInstances = new AtomicInteger();
310+
311+
Literal createLiteral(final String contents) {
312+
return new Literal(contents, myNrOfInstances.getAndAdd(1));
313+
}
314+
315+
SearchableString getSearchableString(final String contents) {
316+
final int maxIndex = myNrOfInstances.get() + 1;
317+
318+
return new SearchableString(contents, maxIndex);
319+
}
320+
}

src/main/java/com/blueconic/browscap/impl/UserAgentFileParser.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ public class UserAgentFileParser {
4040

4141
private final Set<BrowsCapField> myFields;
4242

43+
private final LiteralDomain myDomain = new LiteralDomain();
44+
4345
UserAgentFileParser(final Collection<BrowsCapField> fields) {
4446
myFields = new HashSet<>(fields);
4547
myMapper = new Mapper(myFields);
@@ -70,7 +72,7 @@ private UserAgentParser parse(final Reader input) throws IOException, ParseExcep
7072
}
7173
}
7274
}
73-
return new UserAgentParserImpl(rules.toArray(new Rule[0]), getDefaultCapabilities());
75+
return new UserAgentParserImpl(rules.toArray(new Rule[0]), myDomain, getDefaultCapabilities());
7476
}
7577

7678
Capabilities getDefaultCapabilities() {
@@ -152,7 +154,11 @@ Capabilities getCapabilities(final Map<BrowsCapField, String> values) {
152154
}
153155

154156
Literal getLiteral(final String value) {
155-
return myUniqueLiterals.computeIfAbsent(value, Literal::new);
157+
return myUniqueLiterals.computeIfAbsent(value, myDomain::createLiteral);
158+
}
159+
160+
LiteralDomain getDomain() {
161+
return myDomain;
156162
}
157163

158164
Rule createRule(final String pattern, final Capabilities capabilities) {

src/main/java/com/blueconic/browscap/impl/UserAgentParserImpl.java

+10-4
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,17 @@ class UserAgentParserImpl implements UserAgentParser {
4343
// The default Capabilities
4444
private final Capabilities myDefaultCapabilities;
4545

46+
// The domain of literals for this parser
47+
private final LiteralDomain myDomain;
48+
4649
/**
4750
* Creates a new parser based on a collection of rules.
4851
* @param rules The rules, ordered by priority
52+
* @param domain The domain of literals
53+
* @param defaultCapabilities The default capabilities
4954
*/
50-
UserAgentParserImpl(final Rule[] rules, final Capabilities defaultCapabilities) {
55+
UserAgentParserImpl(final Rule[] rules, final LiteralDomain domain, final Capabilities defaultCapabilities) {
56+
myDomain = domain;
5157
myRules = getOrderedRules(rules);
5258
myFilters = buildFilters();
5359
myDefaultCapabilities = defaultCapabilities;
@@ -59,7 +65,7 @@ class UserAgentParserImpl implements UserAgentParser {
5965
@Override
6066
public Capabilities parse(final String userAgent) {
6167

62-
final SearchableString searchString = new SearchableString(userAgent.toLowerCase());
68+
final SearchableString searchString = myDomain.getSearchableString(userAgent.toLowerCase());
6369

6470
final BitSet includes = getIncludeRules(searchString, myFilters);
6571

@@ -113,7 +119,7 @@ Filter[] buildFilters() {
113119
}
114120

115121
Filter createContainsFilter(final String pattern) {
116-
final Literal literal = new Literal(pattern);
122+
final Literal literal = myDomain.createLiteral(pattern);
117123

118124
final Predicate<SearchableString> pred = c -> c.getIndices(literal).length > 0;
119125

@@ -123,7 +129,7 @@ Filter createContainsFilter(final String pattern) {
123129
}
124130

125131
Filter createPrefixFilter(final String pattern) {
126-
final Literal literal = new Literal(pattern);
132+
final Literal literal = myDomain.createLiteral(pattern);
127133

128134
final Predicate<SearchableString> pred = s -> s.startsWith(literal);
129135

src/test/java/com/blueconic/browscap/impl/RuleTest.java

+11-3
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,18 @@
77
import static org.junit.Assert.assertFalse;
88
import static org.junit.Assert.assertTrue;
99

10+
import org.junit.Before;
1011
import org.junit.Test;
1112

1213
public class RuleTest {
1314

15+
private UserAgentFileParser myParser;
16+
17+
@Before
18+
public void setup() {
19+
myParser = new UserAgentFileParser(singleton(BROWSER));
20+
}
21+
1422
@Test
1523
public void testLiteralExpression() {
1624
final Rule rule = getRule("a");
@@ -131,13 +139,13 @@ public void testRequires() {
131139
}
132140

133141
private Rule getRule(final String pattern) {
134-
final UserAgentFileParser parser = new UserAgentFileParser(singleton(BROWSER));
135-
final Rule rule = parser.createRule(pattern, DEFAULT);
142+
final Rule rule = myParser.createRule(pattern, DEFAULT);
136143
assertEquals(pattern, rule.getPattern());
137144
return rule;
138145
}
139146

140147
private boolean matches(final Rule rule, final String useragent) {
141-
return rule.matches(new SearchableString(useragent));
148+
final SearchableString searchableString = myParser.getDomain().getSearchableString(useragent);
149+
return rule.matches(searchableString);
142150
}
143151
}

src/test/java/com/blueconic/browscap/impl/SearchableStringTest.java

+22-11
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,14 @@ public class SearchableStringTest {
1515

1616
@Test
1717
public void testSearchableString() {
18-
final Literal abc = new Literal("abc");
19-
final Literal ab = new Literal("ab");
18+
19+
final LiteralDomain domain = new LiteralDomain();
20+
21+
final Literal abc = domain.createLiteral("abc");
22+
final Literal ab = domain.createLiteral("ab");
2023

2124
final String stringValue = "abababc";
22-
final SearchableString cache = new SearchableString(stringValue);
25+
final SearchableString cache = domain.getSearchableString(stringValue);
2326

2427
assertTrue(cache.startsWith(ab));
2528
assertFalse(cache.startsWith(abc));
@@ -38,12 +41,14 @@ public void testSearchableString() {
3841

3942
@Test
4043
public void testGetIndices() {
41-
final Literal abc = new Literal("abc");
42-
final Literal ab = new Literal("ab");
43-
final Literal anyChar = new Literal("?ab");
44-
final Literal noMatch = new Literal("aaaaaaaaaaaaaaaaaa");
44+
final LiteralDomain domain = new LiteralDomain();
4545

46-
final SearchableString cache = new SearchableString("abababc");
46+
final Literal abc = domain.createLiteral("abc");
47+
final Literal ab = domain.createLiteral("ab");
48+
final Literal anyChar = domain.createLiteral("?ab");
49+
final Literal noMatch = domain.createLiteral("aaaaaaaaaaaaaaaaaa");
50+
51+
final SearchableString cache = domain.getSearchableString("abababc");
4752

4853
assertArrayEquals(new int[]{4}, cache.getIndices(abc));
4954
assertArrayEquals(new int[]{0, 2, 4}, cache.getIndices(ab));
@@ -72,17 +77,23 @@ public void testCache() {
7277

7378
@Test
7479
public void testLiteralBasics() {
80+
81+
final LiteralDomain domain = new LiteralDomain();
82+
7583
final String input = "abcdef";
76-
final Literal literal = new Literal(input);
84+
final Literal literal = domain.createLiteral(input);
7785
assertEquals(input.length(), literal.getLength());
7886
assertEquals('a', literal.getFirstChar());
7987
assertEquals(input, literal.toString());
8088
}
8189

8290
@Test
8391
public void testLiteralMatches() {
92+
93+
final LiteralDomain domain = new LiteralDomain();
94+
8495
final String input = "def";
85-
final Literal literal = new Literal(input);
96+
final Literal literal = domain.createLiteral(input);
8697

8798
// Test for matches also with invalid bounds
8899
final char[] search = "abcdef".toCharArray();
@@ -92,7 +103,7 @@ public void testLiteralMatches() {
92103
assertFalse(literal.matches(search, -10));
93104
assertFalse(literal.matches(search, 100));
94105

95-
final Literal joker = new Literal("d?f");
106+
final Literal joker = domain.createLiteral("d?f");
96107
assertTrue(joker.matches(search, 3));
97108
assertFalse(literal.matches(search, 0));
98109
assertFalse(literal.matches(search, 5));

src/test/java/com/blueconic/browscap/impl/UserAgentParserTest.java

+13-5
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,20 @@
1919

2020
import java.util.BitSet;
2121

22+
import org.junit.Before;
2223
import org.junit.Test;
2324

2425
import com.blueconic.browscap.impl.UserAgentParserImpl.Filter;
2526

2627
public class UserAgentParserTest {
2728

29+
private UserAgentFileParser myParser;
30+
31+
@Before
32+
public void setup() {
33+
myParser = new UserAgentFileParser(singleton(BROWSER));
34+
}
35+
2836
@Test
2937
public void testExcludes() {
3038

@@ -64,22 +72,23 @@ public void testGetIncludes() {
6472
final Rule d = getRule("*123*");
6573
final Rule[] rules = {a, b, c, d};
6674

67-
final UserAgentParserImpl parser = new UserAgentParserImpl(rules, DEFAULT);
75+
final LiteralDomain domain = myParser.getDomain();
76+
final UserAgentParserImpl parser = new UserAgentParserImpl(rules, domain, DEFAULT);
6877

6978
final Filter startsWithTest = parser.createPrefixFilter("test");
7079
final Filter containsTest = parser.createContainsFilter("test");
7180
final Filter containsNumbers = parser.createContainsFilter("123");
7281
final Filter[] filters = {startsWithTest, containsTest, containsNumbers};
7382

74-
final SearchableString useragent = new SearchableString("useragent_test_string");
83+
final SearchableString useragent = domain.getSearchableString("useragent_test_string");
7584
final BitSet includeRules = parser.getIncludeRules(useragent, filters);
7685

7786
// b should be checked
7887
assertEquals(1, includeRules.nextSetBit(0));
7988
// No further rules to check
8089
assertEquals(-1, includeRules.nextSetBit(2));
8190

82-
final SearchableString numberString = new SearchableString("123456");
91+
final SearchableString numberString = domain.getSearchableString("123456");
8392
final BitSet numberIncludes = parser.getIncludeRules(numberString, filters);
8493

8594
// Only d should be checked
@@ -106,8 +115,7 @@ public void testGetOrderedRules() {
106115
}
107116

108117
private Rule getRule(final String pattern) {
109-
final UserAgentFileParser parser = new UserAgentFileParser(singleton(BROWSER));
110-
final Rule rule = parser.createRule(pattern, DEFAULT);
118+
final Rule rule = myParser.createRule(pattern, DEFAULT);
111119
assertEquals(pattern, rule.getPattern());
112120
return rule;
113121
}

0 commit comments

Comments
 (0)