put text positions on the transitions and track in the token stream

This commit is contained in:
Douglas Lovell 2015-10-30 10:58:15 -06:00
parent dd5f9b25fa
commit b7bb0cbf5b
4 changed files with 50 additions and 35 deletions

View File

@ -21,13 +21,12 @@ package org.ahocorasick.trie;
*/ */
class CharacterTransition extends Transition<Character> { class CharacterTransition extends Transition<Character> {
public CharacterTransition(Character c) { public CharacterTransition(Character c, int start) {
super(c); super(c, start, 1);
} }
@Override public CharacterTransition(Character c) {
public void updateMatch(StringBuilder match) { this(c, 0);
match.append(token);
} }
@Override @Override

View File

@ -25,16 +25,27 @@ import java.util.Objects;
*/ */
public abstract class Transition<T> { public abstract class Transition<T> {
protected final T token; protected final T token;
protected final int start;
protected final int length;
public Transition(T token) { public Transition(T token, int start, int length) {
this.token = token; this.token = token;
this.start = start;
this.length = length;
} }
public T transitionToken() { public T transitionToken() {
return token; return token;
} }
public abstract void updateMatch(StringBuilder match); public int getStart() {
return start;
}
public int getLength() {
return length;
}
public abstract boolean isWordSeparator(); public abstract boolean isWordSeparator();
@Override @Override

View File

@ -31,11 +31,15 @@ public class Trie {
this.rootState = new State(); this.rootState = new State();
} }
private interface KeywordTokenizer { private abstract class KeywordTokenizer {
public Transition nextTransition(); protected int position = 0;
public abstract Transition nextTransition();
public int getPosition() {
return position;
}
} }
private class WordTokenizer implements KeywordTokenizer { private class WordTokenizer extends KeywordTokenizer {
private final Iterator<String> st; private final Iterator<String> st;
public WordTokenizer(String keyword) { public WordTokenizer(String keyword) {
String[] tokens = keyword.split("\\s"); String[] tokens = keyword.split("\\s");
@ -45,13 +49,18 @@ public class Trie {
public Transition<String> nextTransition() { public Transition<String> nextTransition() {
WordTransition t = null; WordTransition t = null;
if (st.hasNext()) { if (st.hasNext()) {
t = new WordTransition(st.next()); String word = st.next();
t = new WordTransition(word, position);
if (0 < position) {
position += 1; // a space
}
position += word.length();
} }
return t; return t;
} }
} }
private class CharacterTokenizer implements KeywordTokenizer { private class CharacterTokenizer extends KeywordTokenizer {
private final java.text.StringCharacterIterator ct; private final java.text.StringCharacterIterator ct;
private char cur; private char cur;
public CharacterTokenizer(String keyword) { public CharacterTokenizer(String keyword) {
@ -62,9 +71,10 @@ public class Trie {
public Transition<Character> nextTransition() { public Transition<Character> nextTransition() {
CharacterTransition t = null; CharacterTransition t = null;
if (cur != CharacterIterator.DONE) { if (cur != CharacterIterator.DONE) {
t = new CharacterTransition(cur); t = new CharacterTransition(cur, position);
cur = ct.next(); cur = ct.next();
} }
position += 1;
return t; return t;
} }
} }
@ -82,11 +92,18 @@ public class Trie {
private class TokenStream { private class TokenStream {
private final KeywordTokenizer kwt; private final KeywordTokenizer kwt;
private final String input;
private Transition lookahead; private Transition lookahead;
private final StringBuilder match = new StringBuilder();
public TokenStream(KeywordTokenizer kwt) { public TokenStream(String input) {
this.kwt = kwt; this.input = input;
if (trieConfig.hasWordTransitions()) {
kwt = new WordTokenizer(input);
}
else {
kwt = new CharacterTokenizer(input);
}
lookahead = null;
} }
public Transition nextTransition() { public Transition nextTransition() {
@ -97,22 +114,15 @@ public class Trie {
else { else {
lookahead = null; lookahead = null;
} }
if (next != null) {
next.updateMatch(match);
}
return next; return next;
} }
public int position() {
return match.length() - 1;
}
public boolean isWholeWord(int start) { public boolean isWholeWord(int start) {
if (lookahead == null) { if (lookahead == null) {
lookahead = kwt.nextTransition(); lookahead = kwt.nextTransition();
} }
return ((start == 0 || return ((start == 0 ||
Character.isSpaceChar(match.codePointAt(start-1))) && Character.isSpaceChar(input.codePointAt(start-1))) &&
(lookahead == null || lookahead.isWordSeparator())); (lookahead == null || lookahead.isWordSeparator()));
} }
} }
@ -169,7 +179,7 @@ public class Trie {
if (trieConfig.isCaseInsensitive()) { if (trieConfig.isCaseInsensitive()) {
input = input.toLowerCase(); input = input.toLowerCase();
} }
TokenStream tknz = new TokenStream(keywordTokenizer(input)); TokenStream tknz = new TokenStream(input);
State currentState = this.rootState; State currentState = this.rootState;
Transition tn = tknz.nextTransition(); Transition tn = tknz.nextTransition();
@ -180,7 +190,7 @@ public class Trie {
currentState = getState(currentState, tn, flushHandler); currentState = getState(currentState, tn, flushHandler);
Collection<String> emits = currentState.emit(); Collection<String> emits = currentState.emit();
for (String emit : emits) { for (String emit : emits) {
int position = tknz.position(); int position = tn.getStart() + tn.getLength() - 1;
int start = position - emit.length() + 1; int start = position - emit.length() + 1;
boolean isWholeWord = tknz.isWholeWord(start); boolean isWholeWord = tknz.isWholeWord(start);
if (isWholeWord || !trieConfig.isOnlyWholeWords()) { if (isWholeWord || !trieConfig.isOnlyWholeWords()) {

View File

@ -21,21 +21,16 @@ package org.ahocorasick.trie;
*/ */
public class WordTransition extends Transition<String> { public class WordTransition extends Transition<String> {
public WordTransition(String s) { public WordTransition(String s, int start) {
super(s); super(s, start, s.length());
} }
@Override public WordTransition(String s) {
public void updateMatch(StringBuilder match) { this(s, 0);
if (0 < match.length()) {
match.append(' ');
}
match.append(token);
} }
@Override @Override
public boolean isWordSeparator() { public boolean isWordSeparator() {
return true; return true;
} }
} }