Index: CHANGES.txt =================================================================== --- CHANGES.txt (revision 922602) +++ CHANGES.txt (working copy) @@ -167,6 +167,11 @@ * LUCENE-2247: Added a CharArrayMap for performance improvements in some stemmers and synonym filters. (Uwe Schindler) +* LUCENE-2314: Added AttributeSource.copyTo(AttributeSource) that + allows to use cloneAttributes() and this method as a replacement + for captureState()/restoreState(), if the state itsself + needs to be inspected/modified. (Uwe Schindler) + Optimizations * LUCENE-2075: Terms dict cache is now shared across threads instead Index: src/java/org/apache/lucene/util/AttributeSource.java =================================================================== --- src/java/org/apache/lucene/util/AttributeSource.java (revision 922602) +++ src/java/org/apache/lucene/util/AttributeSource.java (working copy) @@ -365,8 +365,10 @@ do { AttributeImpl targetImpl = attributeImpls.get(state.attribute.getClass()); - if (targetImpl == null) - throw new IllegalArgumentException("State contains an AttributeImpl that is not in this AttributeSource"); + if (targetImpl == null) { + throw new IllegalArgumentException("State contains AttributeImpl of type " + + state.attribute.getClass() + " that is not in in this AttributeSource"); + } state.attribute.copyTo(targetImpl); state = state.next; } while (state != null); @@ -446,28 +448,54 @@ /** * Performs a clone of all {@link AttributeImpl} instances returned in a new - * AttributeSource instance. This method can be used to e.g. create another TokenStream - * with exactly the same attributes (using {@link #AttributeSource(AttributeSource)}) + * {@code AttributeSource} instance. This method can be used to e.g. create another TokenStream + * with exactly the same attributes (using {@link #AttributeSource(AttributeSource)}). + * You can also use it as a (non-performant) replacement for {@link #captureState}, if you need to look + * into / modify the captured state. */ public AttributeSource cloneAttributes() { - AttributeSource clone = new AttributeSource(this.factory); + final AttributeSource clone = new AttributeSource(this.factory); - // first clone the impls if (hasAttributes()) { + // first clone the impls if (currentState == null) { computeCurrentState(); } for (State state = currentState; state != null; state = state.next) { clone.attributeImpls.put(state.attribute.getClass(), (AttributeImpl) state.attribute.clone()); } + + // now the interfaces + for (Entry, AttributeImpl> entry : this.attributes.entrySet()) { + clone.attributes.put(entry.getKey(), clone.attributeImpls.get(entry.getValue().getClass())); + } } - // now the interfaces - for (Entry, AttributeImpl> entry : this.attributes.entrySet()) { - clone.attributes.put(entry.getKey(), clone.attributeImpls.get(entry.getValue().getClass())); - } - return clone; } + + /** + * Copies the contents of this {@code AttributeSource} to the given target {@code AttributeSource}. + * The given instance has to provide all {@link Attribute}s this instance contains. + * The actual attribute implementations must be identical in both {@code AttributeSource} instances; + * ideally both AttributeSource instances should use the same {@link AttributeFactory}. + * You can use this method as a replacement for {@link #restoreState}, if you use + * {@link #cloneAttributes} instead of {@link #captureState}. + */ + public final void copyTo(AttributeSource target) { + if (hasAttributes()) { + if (currentState == null) { + computeCurrentState(); + } + for (State state = currentState; 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 " + + state.attribute.getClass() + " that is not in the target"); + } + state.attribute.copyTo(targetImpl); + } + } + } } Index: src/test/org/apache/lucene/util/TestAttributeSource.java =================================================================== --- src/test/org/apache/lucene/util/TestAttributeSource.java (revision 922602) +++ src/test/org/apache/lucene/util/TestAttributeSource.java (working copy) @@ -95,6 +95,18 @@ assertNotSame("TypeAttribute of original and clone must be different instances", typeAtt2, typeAtt); assertEquals("TermAttribute of original and clone must be equal", termAtt2, termAtt); assertEquals("TypeAttribute of original and clone must be equal", typeAtt2, typeAtt); + + // test copy back + termAtt2.setTermBuffer("OtherTerm"); + typeAtt2.setType("OtherType"); + clone.copyTo(src); + assertEquals("TermAttribute of original must now contain updated term", "OtherTerm", termAtt.term()); + assertEquals("TypeAttribute of original must now contain updated type", "OtherType", typeAtt.type()); + // verify again: + assertNotSame("TermAttribute of original and clone must be different instances", termAtt2, termAtt); + assertNotSame("TypeAttribute of original and clone must be different instances", typeAtt2, typeAtt); + assertEquals("TermAttribute of original and clone must be equal", termAtt2, termAtt); + assertEquals("TypeAttribute of original and clone must be equal", typeAtt2, typeAtt); } public void testToStringAndMultiAttributeImplementations() {