package org.apache.lucene.analysis.pattern;
import java.io.IOException;
import org.apache.lucene.analysis.Tokenizer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
import org.apache.lucene.util.ArrayUtil;
import org.apache.lucene.util.AttributeFactory;
import org.apache.lucene.util.automaton.Automaton;
import org.apache.lucene.util.automaton.CharacterRunAutomaton;
import org.apache.lucene.util.automaton.Operations;
import org.apache.lucene.util.automaton.RegExp;
public final class SimplePatternSplitTokenizer extends Tokenizer {
private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class);
private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class);
private final CharacterRunAutomaton runDFA;
private char[] pendingChars = new char[8];
private int tokenUpto;
private int pendingLimit;
private int pendingUpto;
private int offset;
private int sepUpto;
private final char[] buffer = new char[1024];
private int bufferLimit;
private int bufferNextRead;
public SimplePatternSplitTokenizer(String regexp) {
this(DEFAULT_TOKEN_ATTRIBUTE_FACTORY, regexp, Operations.DEFAULT_MAX_DETERMINIZED_STATES);
}
public SimplePatternSplitTokenizer(Automaton dfa) {
this(DEFAULT_TOKEN_ATTRIBUTE_FACTORY, dfa);
}
public SimplePatternSplitTokenizer(AttributeFactory factory, String regexp, int maxDeterminizedStates) {
this(factory, new RegExp(regexp).toAutomaton());
}
public SimplePatternSplitTokenizer(AttributeFactory factory, Automaton dfa) {
super(factory);
if (dfa.isDeterministic() == false) {
throw new IllegalArgumentException("please determinize the incoming automaton first");
}
runDFA = new CharacterRunAutomaton(dfa, Operations.DEFAULT_MAX_DETERMINIZED_STATES);
}
private void fillToken(int offsetStart) {
termAtt.setLength(tokenUpto);
offsetAtt.setOffset(correctOffset(offsetStart), correctOffset(offsetStart+tokenUpto));
}
@Override
public boolean incrementToken() throws IOException {
int offsetStart = offset;
clearAttributes();
tokenUpto = 0;
while (true) {
sepUpto = 0;
int ch = nextCodePoint();
if (ch == -1) {
if (tokenUpto > 0) {
fillToken(offsetStart);
return true;
} else {
return false;
}
}
int state = runDFA.step(0, ch);
if (state != -1) {
int lastAcceptLength = -1;
do {
if (runDFA.isAccept(state)) {
lastAcceptLength = sepUpto;
}
ch = nextCodePoint();
if (ch == -1) {
break;
}
state = runDFA.step(state, ch);
} while (state != -1);
if (lastAcceptLength != -1) {
int extra = sepUpto - lastAcceptLength;
if (extra != 0) {
pushBack(extra);
}
tokenUpto -= lastAcceptLength;
if (tokenUpto > 0) {
fillToken(offsetStart);
return true;
} else {
offsetStart = offset;
}
} else if (ch == -1) {
if (tokenUpto > 0) {
fillToken(offsetStart);
return true;
} else {
return false;
}
} else {
pushBack(sepUpto-1);
}
}
}
}
@Override
public void end() throws IOException {
super.end();
final int ofs = correctOffset(offset + pendingLimit - pendingUpto);
offsetAtt.setOffset(ofs, ofs);
}
@Override
public void reset() throws IOException {
super.reset();
offset = 0;
pendingUpto = 0;
pendingLimit = 0;
sepUpto = 0;
bufferNextRead = 0;
bufferLimit = 0;
}
private void pushBack(int count) {
tokenUpto -= count;
assert tokenUpto >= 0;
if (pendingLimit == 0) {
if (bufferLimit != -1 && bufferNextRead >= count) {
bufferNextRead -= count;
} else {
if (count > pendingChars.length) {
pendingChars = ArrayUtil.grow(pendingChars, count);
}
System.arraycopy(termAtt.buffer(), tokenUpto, pendingChars, 0, count);
pendingLimit = count;
}
} else {
pendingUpto -= count;
assert pendingUpto >= 0;
}
offset -= count;
}
private void appendToToken(char ch) {
char[] buffer = termAtt.buffer();
if (tokenUpto == buffer.length) {
buffer = termAtt.resizeBuffer(tokenUpto + 1);
}
buffer[tokenUpto++] = ch;
sepUpto++;
}
private int nextCodeUnit() throws IOException {
int result;
if (pendingUpto < pendingLimit) {
result = pendingChars[pendingUpto++];
if (pendingUpto == pendingLimit) {
pendingUpto = 0;
pendingLimit = 0;
}
appendToToken((char) result);
offset++;
} else if (bufferLimit == -1) {
return -1;
} else {
assert bufferNextRead <= bufferLimit: "bufferNextRead=" + bufferNextRead + " bufferLimit=" + bufferLimit;
if (bufferNextRead == bufferLimit) {
bufferLimit = input.read(buffer, 0, buffer.length);
if (bufferLimit == -1) {
return -1;
}
bufferNextRead = 0;
}
result = buffer[bufferNextRead++];
offset++;
appendToToken((char) result);
}
return result;
}
private int nextCodePoint() throws IOException {
int ch = nextCodeUnit();
if (ch == -1) {
return ch;
}
if (Character.isHighSurrogate((char) ch)) {
return Character.toCodePoint((char) ch, (char) nextCodeUnit());
} else {
return ch;
}
}
}