pull #14 implemented pull request by rripken for containsMatch and firstMatch
This commit is contained in:
parent
4633b1ba2a
commit
e2c5334234
@ -85,11 +85,11 @@ public class Trie {
|
|||||||
return collectedEmits;
|
return collectedEmits;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean matches(String text)
|
public boolean containsMatch(CharSequence text) {
|
||||||
{
|
|
||||||
Emit firstMatch = firstMatch(text);
|
Emit firstMatch = firstMatch(text);
|
||||||
return firstMatch != null;
|
return firstMatch != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void parseText(CharSequence text, EmitHandler emitHandler) {
|
public void parseText(CharSequence text, EmitHandler emitHandler) {
|
||||||
State currentState = this.rootState;
|
State currentState = this.rootState;
|
||||||
for (int position = 0; position < text.length(); position++) {
|
for (int position = 0; position < text.length(); position++) {
|
||||||
@ -105,21 +105,7 @@ public class Trie {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removePartialMatches(CharSequence searchText, List<Emit> collectedEmits) {
|
public Emit firstMatch(CharSequence text) {
|
||||||
long size = searchText.length();
|
|
||||||
List<Emit> removeEmits = new ArrayList<Emit>();
|
|
||||||
for (Emit emit : collectedEmits) {
|
|
||||||
if ((emit.getStart() == 0 ||
|
|
||||||
!Character.isAlphabetic(searchText.charAt(emit.getStart() - 1))) &&
|
|
||||||
(emit.getEnd() + 1 == size ||
|
|
||||||
!Character.isAlphabetic(searchText.charAt(emit.getEnd() + 1)))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
removeEmits.add(emit);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Emit firstMatch(String text)
|
|
||||||
{
|
|
||||||
if (!trieConfig.isAllowOverlaps()) {
|
if (!trieConfig.isAllowOverlaps()) {
|
||||||
// Slow path. Needs to find all the matches to detect overlaps.
|
// Slow path. Needs to find all the matches to detect overlaps.
|
||||||
Collection<Emit> parseText = parseText(text);
|
Collection<Emit> parseText = parseText(text);
|
||||||
@ -127,11 +113,10 @@ public class Trie {
|
|||||||
return parseText.iterator().next();
|
return parseText.iterator().next();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Fast path. Returs first match found.
|
// Fast path. Returns first match found.
|
||||||
checkForConstructedFailureStates();
|
|
||||||
int position = 0;
|
|
||||||
State currentState = this.rootState;
|
State currentState = this.rootState;
|
||||||
for (Character character : text.toCharArray()) {
|
for (int position = 0; position < text.length(); position++) {
|
||||||
|
Character character = text.charAt(position);
|
||||||
if (trieConfig.isCaseInsensitive()) {
|
if (trieConfig.isCaseInsensitive()) {
|
||||||
character = Character.toLowerCase(character);
|
character = Character.toLowerCase(character);
|
||||||
}
|
}
|
||||||
@ -149,23 +134,20 @@ public class Trie {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
position++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isPartialMatch(String searchText, Emit emit)
|
private boolean isPartialMatch(CharSequence searchText, Emit emit) {
|
||||||
{
|
|
||||||
return (emit.getStart() != 0 &&
|
return (emit.getStart() != 0 &&
|
||||||
Character.isAlphabetic(searchText.charAt(emit.getStart() - 1))) ||
|
Character.isAlphabetic(searchText.charAt(emit.getStart() - 1))) ||
|
||||||
(emit.getEnd() + 1 != searchText.length() &&
|
(emit.getEnd() + 1 != searchText.length() &&
|
||||||
Character.isAlphabetic(searchText.charAt(emit.getEnd() + 1)));
|
Character.isAlphabetic(searchText.charAt(emit.getEnd() + 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removePartialMatches(String searchText, List<Emit> collectedEmits)
|
private void removePartialMatches(CharSequence searchText, List<Emit> collectedEmits) {
|
||||||
{
|
List<Emit> removeEmits = new ArrayList<>();
|
||||||
List<Emit> removeEmits = new ArrayList<Emit>();
|
|
||||||
for (Emit emit : collectedEmits) {
|
for (Emit emit : collectedEmits) {
|
||||||
if (isPartialMatch(searchText, emit)) {
|
if (isPartialMatch(searchText, emit)) {
|
||||||
removeEmits.add(emit);
|
removeEmits.add(emit);
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import java.util.Iterator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static junit.framework.Assert.assertEquals;
|
import static junit.framework.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class TrieTest {
|
public class TrieTest {
|
||||||
|
|
||||||
@ -24,8 +25,9 @@ public class TrieTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void keywordAndTextAreTheSameFirstMatch() {
|
public void keywordAndTextAreTheSameFirstMatch() {
|
||||||
Trie trie = new Trie();
|
Trie trie = Trie.builder()
|
||||||
trie.addKeyword("abc");
|
.addKeyword("abc")
|
||||||
|
.build();
|
||||||
Emit firstMatch = trie.firstMatch("abc");
|
Emit firstMatch = trie.firstMatch("abc");
|
||||||
checkEmit(firstMatch, 0, 2, "abc");
|
checkEmit(firstMatch, 0, 2, "abc");
|
||||||
}
|
}
|
||||||
@ -42,8 +44,9 @@ public class TrieTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void textIsLongerThanKeywordFirstMatch() {
|
public void textIsLongerThanKeywordFirstMatch() {
|
||||||
Trie trie = new Trie();
|
Trie trie = Trie.builder()
|
||||||
trie.addKeyword("abc");
|
.addKeyword("abc")
|
||||||
|
.build();
|
||||||
Emit firstMatch = trie.firstMatch(" abc");
|
Emit firstMatch = trie.firstMatch(" abc");
|
||||||
checkEmit(firstMatch, 1, 3, "abc");
|
checkEmit(firstMatch, 1, 3, "abc");
|
||||||
}
|
}
|
||||||
@ -62,10 +65,11 @@ public class TrieTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void variousKeywordsFirstMatch() {
|
public void variousKeywordsFirstMatch() {
|
||||||
Trie trie = new Trie();
|
Trie trie = Trie.builder()
|
||||||
trie.addKeyword("abc");
|
.addKeyword("abc")
|
||||||
trie.addKeyword("bcd");
|
.addKeyword("bcd")
|
||||||
trie.addKeyword("cde");
|
.addKeyword("cde")
|
||||||
|
.build();
|
||||||
Emit firstMatch = trie.firstMatch("bcd");
|
Emit firstMatch = trie.firstMatch("bcd");
|
||||||
checkEmit(firstMatch, 0, 2, "bcd");
|
checkEmit(firstMatch, 0, 2, "bcd");
|
||||||
}
|
}
|
||||||
@ -104,11 +108,12 @@ public class TrieTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void ushersTestFirstMatch() {
|
public void ushersTestFirstMatch() {
|
||||||
Trie trie = new Trie();
|
Trie trie = Trie.builder()
|
||||||
trie.addKeyword("hers");
|
.addKeyword("hers")
|
||||||
trie.addKeyword("his");
|
.addKeyword("his")
|
||||||
trie.addKeyword("she");
|
.addKeyword("she")
|
||||||
trie.addKeyword("he");
|
.addKeyword("he")
|
||||||
|
.build();
|
||||||
Emit firstMatch = trie.firstMatch("ushers");
|
Emit firstMatch = trie.firstMatch("ushers");
|
||||||
checkEmit(firstMatch, 2, 3, "he");
|
checkEmit(firstMatch, 2, 3, "he");
|
||||||
}
|
}
|
||||||
@ -150,8 +155,9 @@ public class TrieTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void misleadingTestFirstMatch() {
|
public void misleadingTestFirstMatch() {
|
||||||
Trie trie = new Trie();
|
Trie trie = Trie.builder()
|
||||||
trie.addKeyword("hers");
|
.addKeyword("hers")
|
||||||
|
.build();
|
||||||
Emit firstMatch = trie.firstMatch("h he her hers");
|
Emit firstMatch = trie.firstMatch("h he her hers");
|
||||||
checkEmit(firstMatch, 9, 12, "hers");
|
checkEmit(firstMatch, 9, 12, "hers");
|
||||||
}
|
}
|
||||||
@ -174,11 +180,12 @@ public class TrieTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void recipesFirstMatch() {
|
public void recipesFirstMatch() {
|
||||||
Trie trie = new Trie();
|
Trie trie = Trie.builder()
|
||||||
trie.addKeyword("veal");
|
.addKeyword("veal")
|
||||||
trie.addKeyword("cauliflower");
|
.addKeyword("cauliflower")
|
||||||
trie.addKeyword("broccoli");
|
.addKeyword("broccoli")
|
||||||
trie.addKeyword("tomatoes");
|
.addKeyword("tomatoes")
|
||||||
|
.build();
|
||||||
Emit firstMatch = trie.firstMatch("2 cauliflowers, 3 tomatoes, 4 slices of veal, 100g broccoli");
|
Emit firstMatch = trie.firstMatch("2 cauliflowers, 3 tomatoes, 4 slices of veal, 100g broccoli");
|
||||||
|
|
||||||
checkEmit(firstMatch, 2, 12, "cauliflower");
|
checkEmit(firstMatch, 2, 12, "cauliflower");
|
||||||
@ -186,9 +193,10 @@ public class TrieTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void longAndShortOverlappingMatch() {
|
public void longAndShortOverlappingMatch() {
|
||||||
Trie trie = new Trie();
|
Trie trie = Trie.builder()
|
||||||
trie.addKeyword("he");
|
.addKeyword("he")
|
||||||
trie.addKeyword("hehehehe");
|
.addKeyword("hehehehe")
|
||||||
|
.build();
|
||||||
Collection<Emit> emits = trie.parseText("hehehehehe");
|
Collection<Emit> emits = trie.parseText("hehehehehe");
|
||||||
Iterator<Emit> iterator = emits.iterator();
|
Iterator<Emit> iterator = emits.iterator();
|
||||||
checkEmit(iterator.next(), 0, 1, "he");
|
checkEmit(iterator.next(), 0, 1, "he");
|
||||||
@ -215,17 +223,28 @@ public class TrieTest {
|
|||||||
checkEmit(iterator.next(), 6, 7, "ab");
|
checkEmit(iterator.next(), 6, 7, "ab");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void nonOverlappingFirstMatch() {
|
public void nonOverlappingFirstMatch() {
|
||||||
Trie trie = new Trie().removeOverlaps();
|
Trie trie = Trie.builder().removeOverlaps()
|
||||||
trie.addKeyword("ab");
|
.addKeyword("ab")
|
||||||
trie.addKeyword("cba");
|
.addKeyword("cba")
|
||||||
trie.addKeyword("ababc");
|
.addKeyword("ababc")
|
||||||
|
.build();
|
||||||
Emit firstMatch = trie.firstMatch("ababcbab");
|
Emit firstMatch = trie.firstMatch("ababcbab");
|
||||||
|
|
||||||
checkEmit(firstMatch, 0, 4, "ababc");
|
checkEmit(firstMatch, 0, 4, "ababc");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void containsMatch() {
|
||||||
|
Trie trie = Trie.builder().removeOverlaps()
|
||||||
|
.addKeyword("ab")
|
||||||
|
.addKeyword("cba")
|
||||||
|
.addKeyword("ababc")
|
||||||
|
.build();
|
||||||
|
assertTrue(trie.containsMatch("ababcbab"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void startOfChurchillSpeech() {
|
public void startOfChurchillSpeech() {
|
||||||
Trie trie = Trie.builder().removeOverlaps()
|
Trie trie = Trie.builder().removeOverlaps()
|
||||||
@ -246,7 +265,8 @@ public class TrieTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void partialMatch() {
|
public void partialMatch() {
|
||||||
Trie trie = Trie.builder().onlyWholeWords()
|
Trie trie = Trie.builder()
|
||||||
|
.onlyWholeWords()
|
||||||
.addKeyword("sugar")
|
.addKeyword("sugar")
|
||||||
.build();
|
.build();
|
||||||
Collection<Emit> emits = trie.parseText("sugarcane sugarcane sugar canesugar"); // left, middle, right test
|
Collection<Emit> emits = trie.parseText("sugarcane sugarcane sugar canesugar"); // left, middle, right test
|
||||||
@ -256,8 +276,10 @@ public class TrieTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void partialMatchFirstMatch() {
|
public void partialMatchFirstMatch() {
|
||||||
Trie trie = new Trie().onlyWholeWords();
|
Trie trie = Trie.builder()
|
||||||
trie.addKeyword("sugar");
|
.onlyWholeWords()
|
||||||
|
.addKeyword("sugar")
|
||||||
|
.build();
|
||||||
Emit firstMatch = trie.firstMatch("sugarcane sugarcane sugar canesugar"); // left, middle, right test
|
Emit firstMatch = trie.firstMatch("sugarcane sugarcane sugar canesugar"); // left, middle, right test
|
||||||
|
|
||||||
checkEmit(firstMatch, 20, 24, "sugar");
|
checkEmit(firstMatch, 20, 24, "sugar");
|
||||||
@ -318,11 +340,12 @@ public class TrieTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void caseInsensitiveFirstMatch() {
|
public void caseInsensitiveFirstMatch() {
|
||||||
Trie trie = new Trie().caseInsensitive();
|
Trie trie = Trie.builder().caseInsensitive()
|
||||||
trie.addKeyword("turning");
|
.addKeyword("turning")
|
||||||
trie.addKeyword("once");
|
.addKeyword("once")
|
||||||
trie.addKeyword("again");
|
.addKeyword("again")
|
||||||
trie.addKeyword("börkü");
|
.addKeyword("börkü")
|
||||||
|
.build();
|
||||||
Emit firstMatch = trie.firstMatch("TurninG OnCe AgAiN BÖRKÜ");
|
Emit firstMatch = trie.firstMatch("TurninG OnCe AgAiN BÖRKÜ");
|
||||||
|
|
||||||
checkEmit(firstMatch, 0, 6, "turning");
|
checkEmit(firstMatch, 0, 6, "turning");
|
||||||
@ -365,9 +388,12 @@ public class TrieTest {
|
|||||||
@Test
|
@Test
|
||||||
public void unicodeIssueBug8ReportedByDwyerkFirstMatch() {
|
public void unicodeIssueBug8ReportedByDwyerkFirstMatch() {
|
||||||
String target = "LİKE THIS"; // The second character ('İ') is Unicode, which was read by AC as a 2-byte char
|
String target = "LİKE THIS"; // The second character ('İ') is Unicode, which was read by AC as a 2-byte char
|
||||||
Trie trie = new Trie().caseInsensitive().onlyWholeWords();
|
Trie trie = Trie.builder()
|
||||||
assertEquals("THIS", target.substring(5,9)); // Java does it the right way
|
.caseInsensitive()
|
||||||
trie.addKeyword("this");
|
.onlyWholeWords()
|
||||||
|
.addKeyword("this")
|
||||||
|
.build();
|
||||||
|
assertEquals("THIS", target.substring(5, 9)); // Java does it the right way
|
||||||
Emit firstMatch = trie.firstMatch(target);
|
Emit firstMatch = trie.firstMatch(target);
|
||||||
checkEmit(firstMatch, 5, 8, "this");
|
checkEmit(firstMatch, 5, 8, "this");
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user