Index: lucene/src/java/org/apache/lucene/util/AttributeSource.java =================================================================== --- lucene/src/java/org/apache/lucene/util/AttributeSource.java (revision 1096005) +++ lucene/src/java/org/apache/lucene/util/AttributeSource.java (working copy) @@ -93,10 +93,33 @@ } } + /** + * This class holds the state of an AttributeSource. + * @see #captureState + * @see #restoreState + */ + public static final class State implements Cloneable { + AttributeImpl attribute; + State next; + + @Override + public Object clone() { + State clone = new State(); + clone.attribute = (AttributeImpl) attribute.clone(); + + if (next != null) { + clone.next = (State) next.clone(); + } + + return clone; + } + } + // These two maps must always be in sync!!! // So they are private, final and read-only from the outside (read-only iterators) private final Map, AttributeImpl> attributes; private final Map, AttributeImpl> attributeImpls; + private final State[] currentState; private AttributeFactory factory; @@ -116,6 +139,7 @@ } this.attributes = input.attributes; this.attributeImpls = input.attributeImpls; + this.currentState = input.currentState; this.factory = input.factory; } @@ -125,6 +149,7 @@ public AttributeSource(AttributeFactory factory) { this.attributes = new LinkedHashMap, AttributeImpl>(); this.attributeImpls = new LinkedHashMap, AttributeImpl>(); + this.currentState = new State[1]; this.factory = factory; } @@ -148,10 +173,10 @@ */ public final Iterator getAttributeImplsIterator() { if (hasAttributes()) { - if (currentState == null) { + if (currentState[0] == null) { computeCurrentState(); } - final State initState = currentState; + final State initState = currentState[0]; return new Iterator() { private State state = initState; @@ -225,7 +250,7 @@ // Attribute is a superclass of this interface if (!attributes.containsKey(curInterface)) { // invalidate state to force recomputation in captureState() - this.currentState = null; + this.currentState[0] = null; attributes.put(curInterface, att); attributeImpls.put(clazz, att); } @@ -283,34 +308,9 @@ } return attClass.cast(attImpl); } - - /** - * This class holds the state of an AttributeSource. - * @see #captureState - * @see #restoreState - */ - public static final class State implements Cloneable { - AttributeImpl attribute; - State next; - @Override - public Object clone() { - State clone = new State(); - clone.attribute = (AttributeImpl) attribute.clone(); - - if (next != null) { - clone.next = (State) next.clone(); - } - - return clone; - } - } - - private State currentState = null; - private void computeCurrentState() { - currentState = new State(); - State c = currentState; + State c = currentState[0] = new State(); final Iterator it = attributeImpls.values().iterator(); c.attribute = it.next(); while (it.hasNext()) { @@ -326,10 +326,10 @@ */ public final void clearAttributes() { if (hasAttributes()) { - if (currentState == null) { + if (currentState[0] == null) { computeCurrentState(); } - for (State state = currentState; state != null; state = state.next) { + for (State state = currentState[0]; state != null; state = state.next) { state.attribute.clear(); } } @@ -344,10 +344,10 @@ return null; } - if (currentState == null) { + if (currentState[0] == null) { computeCurrentState(); } - return (State) this.currentState.clone(); + return (State) this.currentState[0].clone(); } /** @@ -383,10 +383,10 @@ public int hashCode() { int code = 0; if (hasAttributes()) { - if (currentState == null) { + if (currentState[0] == null) { computeCurrentState(); } - for (State state = currentState; state != null; state = state.next) { + for (State state = currentState[0]; state != null; state = state.next) { code = code * 31 + state.attribute.hashCode(); } } @@ -413,14 +413,14 @@ } // it is only equal if all attribute impls are the same in the same order - if (this.currentState == null) { + if (this.currentState[0] == null) { this.computeCurrentState(); } - State thisState = this.currentState; - if (other.currentState == null) { + State thisState = this.currentState[0]; + if (other.currentState[0] == null) { other.computeCurrentState(); } - State otherState = other.currentState; + State otherState = other.currentState[0]; while (thisState != null && otherState != null) { if (otherState.attribute.getClass() != thisState.attribute.getClass() || !otherState.attribute.equals(thisState.attribute)) { return false; @@ -474,10 +474,10 @@ */ public final void reflectWith(AttributeReflector reflector) { if (hasAttributes()) { - if (currentState == null) { + if (currentState[0] == null) { computeCurrentState(); } - for (State state = currentState; state != null; state = state.next) { + for (State state = currentState[0]; state != null; state = state.next) { state.attribute.reflectWith(reflector); } } @@ -495,10 +495,10 @@ if (hasAttributes()) { // first clone the impls - if (currentState == null) { + if (currentState[0] == null) { computeCurrentState(); } - for (State state = currentState; state != null; state = state.next) { + for (State state = currentState[0]; state != null; state = state.next) { clone.attributeImpls.put(state.attribute.getClass(), (AttributeImpl) state.attribute.clone()); } @@ -521,10 +521,10 @@ */ public final void copyTo(AttributeSource target) { if (hasAttributes()) { - if (currentState == null) { + if (currentState[0] == null) { computeCurrentState(); } - for (State state = currentState; state != null; state = state.next) { + for (State state = currentState[0]; state != null; state = state.next) { final AttributeImpl targetImpl = target.attributeImpls.get(state.attribute.getClass()); if (targetImpl == null) { throw new IllegalArgumentException("This AttributeSource contains AttributeImpl of type " + Index: lucene/src/test/org/apache/lucene/analysis/TestMockAnalyzer.java =================================================================== --- lucene/src/test/org/apache/lucene/analysis/TestMockAnalyzer.java (revision 1096005) +++ lucene/src/test/org/apache/lucene/analysis/TestMockAnalyzer.java (working copy) @@ -1,5 +1,6 @@ package org.apache.lucene.analysis; +import java.io.StringReader; import java.util.Arrays; import org.apache.lucene.util.automaton.Automaton; @@ -95,4 +96,19 @@ new String[] { "ok", "fine" }, new int[] { 1, 2 }); } + + public void testLUCENE_3042() throws Exception { + String testString = "t"; + + Analyzer analyzer = new MockAnalyzer(random); + TokenStream stream = analyzer.reusableTokenStream("dummy", new StringReader(testString)); + stream.reset(); + while (stream.incrementToken()) { + // consume + } + stream.end(); + + assertAnalyzesToReuse(analyzer, testString, new String[] { "t" }); + } + }