1 package org.apache.lucene.util;
4 * Licensed to the Apache Software Foundation (ASF) under one or more
5 * contributor license agreements. See the NOTICE file distributed with
6 * this work for additional information regarding copyright ownership.
7 * The ASF licenses this file to You under the Apache License, Version 2.0
8 * (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
20 import java.lang.ref.WeakReference;
21 import java.util.Collections;
22 import java.util.NoSuchElementException;
23 import java.util.Iterator;
24 import java.util.LinkedHashMap;
25 import java.util.WeakHashMap;
26 import java.util.LinkedList;
28 import java.util.Map.Entry;
30 import org.apache.lucene.analysis.TokenStream; // for javadocs
31 import org.apache.lucene.analysis.tokenattributes.TermAttribute;
32 import org.apache.lucene.analysis.tokenattributes.CharTermAttributeImpl;
35 * An AttributeSource contains a list of different {@link AttributeImpl}s,
36 * and methods to add and get them. There can only be a single instance
37 * of an attribute in the same AttributeSource instance. This is ensured
38 * by passing in the actual type of the Attribute (Class<Attribute>) to
39 * the {@link #addAttribute(Class)}, which then checks if an instance of
40 * that type is already present. If yes, it returns the instance, otherwise
41 * it creates a new instance and returns it.
43 public class AttributeSource {
45 * An AttributeFactory creates instances of {@link AttributeImpl}s.
47 public static abstract class AttributeFactory {
49 * returns an {@link AttributeImpl} for the supplied {@link Attribute} interface class.
51 public abstract AttributeImpl createAttributeInstance(Class<? extends Attribute> attClass);
54 * This is the default factory that creates {@link AttributeImpl}s using the
55 * class name of the supplied {@link Attribute} interface class by appending <code>Impl</code> to it.
57 public static final AttributeFactory DEFAULT_ATTRIBUTE_FACTORY = new DefaultAttributeFactory();
59 private static final class DefaultAttributeFactory extends AttributeFactory {
60 private static final WeakHashMap<Class<? extends Attribute>, WeakReference<Class<? extends AttributeImpl>>> attClassImplMap =
61 new WeakHashMap<Class<? extends Attribute>, WeakReference<Class<? extends AttributeImpl>>>();
63 private DefaultAttributeFactory() {}
66 public AttributeImpl createAttributeInstance(Class<? extends Attribute> attClass) {
68 return getClassForInterface(attClass).newInstance();
69 } catch (InstantiationException e) {
70 throw new IllegalArgumentException("Could not instantiate implementing class for " + attClass.getName());
71 } catch (IllegalAccessException e) {
72 throw new IllegalArgumentException("Could not instantiate implementing class for " + attClass.getName());
76 private static Class<? extends AttributeImpl> getClassForInterface(Class<? extends Attribute> attClass) {
77 synchronized(attClassImplMap) {
78 final WeakReference<Class<? extends AttributeImpl>> ref = attClassImplMap.get(attClass);
79 Class<? extends AttributeImpl> clazz = (ref == null) ? null : ref.get();
82 // TODO: Remove when TermAttribute is removed!
83 // This is a "sophisticated backwards compatibility hack"
84 // (enforce new impl for this deprecated att):
85 if (TermAttribute.class.equals(attClass)) {
86 clazz = CharTermAttributeImpl.class;
88 clazz = Class.forName(attClass.getName() + "Impl", true, attClass.getClassLoader())
89 .asSubclass(AttributeImpl.class);
91 attClassImplMap.put(attClass,
92 new WeakReference<Class<? extends AttributeImpl>>(clazz)
94 } catch (ClassNotFoundException e) {
95 throw new IllegalArgumentException("Could not find implementing class for " + attClass.getName());
105 * This class holds the state of an AttributeSource.
109 public static final class State implements Cloneable {
110 AttributeImpl attribute;
114 public Object clone() {
115 State clone = new State();
116 clone.attribute = (AttributeImpl) attribute.clone();
119 clone.next = (State) next.clone();
126 // These two maps must always be in sync!!!
127 // So they are private, final and read-only from the outside (read-only iterators)
128 private final Map<Class<? extends Attribute>, AttributeImpl> attributes;
129 private final Map<Class<? extends AttributeImpl>, AttributeImpl> attributeImpls;
130 private final State[] currentState;
132 private AttributeFactory factory;
135 * An AttributeSource using the default attribute factory {@link AttributeSource.AttributeFactory#DEFAULT_ATTRIBUTE_FACTORY}.
137 public AttributeSource() {
138 this(AttributeFactory.DEFAULT_ATTRIBUTE_FACTORY);
142 * An AttributeSource that uses the same attributes as the supplied one.
144 public AttributeSource(AttributeSource input) {
146 throw new IllegalArgumentException("input AttributeSource must not be null");
148 this.attributes = input.attributes;
149 this.attributeImpls = input.attributeImpls;
150 this.currentState = input.currentState;
151 this.factory = input.factory;
155 * An AttributeSource using the supplied {@link AttributeFactory} for creating new {@link Attribute} instances.
157 public AttributeSource(AttributeFactory factory) {
158 this.attributes = new LinkedHashMap<Class<? extends Attribute>, AttributeImpl>();
159 this.attributeImpls = new LinkedHashMap<Class<? extends AttributeImpl>, AttributeImpl>();
160 this.currentState = new State[1];
161 this.factory = factory;
165 * returns the used AttributeFactory.
167 public AttributeFactory getAttributeFactory() {
171 /** Returns a new iterator that iterates the attribute classes
172 * in the same order they were added in.
174 public Iterator<Class<? extends Attribute>> getAttributeClassesIterator() {
175 return Collections.unmodifiableSet(attributes.keySet()).iterator();
178 /** Returns a new iterator that iterates all unique Attribute implementations.
179 * This iterator may contain less entries that {@link #getAttributeClassesIterator},
180 * if one instance implements more than one Attribute interface.
182 public Iterator<AttributeImpl> getAttributeImplsIterator() {
183 final State initState = getCurrentState();
184 if (initState != null) {
185 return new Iterator<AttributeImpl>() {
186 private State state = initState;
188 public void remove() {
189 throw new UnsupportedOperationException();
192 public AttributeImpl next() {
194 throw new NoSuchElementException();
195 final AttributeImpl att = state.attribute;
200 public boolean hasNext() {
201 return state != null;
205 return Collections.<AttributeImpl>emptySet().iterator();
209 /** a cache that stores all interfaces for known implementation classes for performance (slow reflection) */
210 private static final WeakHashMap<Class<? extends AttributeImpl>,LinkedList<WeakReference<Class<? extends Attribute>>>> knownImplClasses =
211 new WeakHashMap<Class<? extends AttributeImpl>,LinkedList<WeakReference<Class<? extends Attribute>>>>();
213 static LinkedList<WeakReference<Class<? extends Attribute>>> getAttributeInterfaces(final Class<? extends AttributeImpl> clazz) {
214 synchronized(knownImplClasses) {
215 LinkedList<WeakReference<Class<? extends Attribute>>> foundInterfaces = knownImplClasses.get(clazz);
216 if (foundInterfaces == null) {
217 // we have a strong reference to the class instance holding all interfaces in the list (parameter "att"),
218 // so all WeakReferences are never evicted by GC
219 knownImplClasses.put(clazz, foundInterfaces = new LinkedList<WeakReference<Class<? extends Attribute>>>());
220 // find all interfaces that this attribute instance implements
221 // and that extend the Attribute interface
222 Class<?> actClazz = clazz;
224 for (Class<?> curInterface : actClazz.getInterfaces()) {
225 if (curInterface != Attribute.class && Attribute.class.isAssignableFrom(curInterface)) {
226 foundInterfaces.add(new WeakReference<Class<? extends Attribute>>(curInterface.asSubclass(Attribute.class)));
229 actClazz = actClazz.getSuperclass();
230 } while (actClazz != null);
232 return foundInterfaces;
236 /** <b>Expert:</b> Adds a custom AttributeImpl instance with one or more Attribute interfaces.
237 * <p><font color="red"><b>Please note:</b> It is not guaranteed, that <code>att</code> is added to
238 * the <code>AttributeSource</code>, because the provided attributes may already exist.
239 * You should always retrieve the wanted attributes using {@link #getAttribute} after adding
240 * with this method and cast to your class.
241 * The recommended way to use custom implementations is using an {@link AttributeFactory}.
244 public void addAttributeImpl(final AttributeImpl att) {
245 final Class<? extends AttributeImpl> clazz = att.getClass();
246 if (attributeImpls.containsKey(clazz)) return;
247 final LinkedList<WeakReference<Class<? extends Attribute>>> foundInterfaces =
248 getAttributeInterfaces(clazz);
250 // add all interfaces of this AttributeImpl to the maps
251 for (WeakReference<Class<? extends Attribute>> curInterfaceRef : foundInterfaces) {
252 final Class<? extends Attribute> curInterface = curInterfaceRef.get();
253 assert (curInterface != null) :
254 "We have a strong reference on the class holding the interfaces, so they should never get evicted";
255 // Attribute is a superclass of this interface
256 if (!attributes.containsKey(curInterface)) {
257 // invalidate state to force recomputation in captureState()
258 this.currentState[0] = null;
259 attributes.put(curInterface, att);
260 attributeImpls.put(clazz, att);
266 * The caller must pass in a Class<? extends Attribute> value.
267 * This method first checks if an instance of that class is
268 * already in this AttributeSource and returns it. Otherwise a
269 * new instance is created, added to this AttributeSource and returned.
271 public <A extends Attribute> A addAttribute(Class<A> attClass) {
272 AttributeImpl attImpl = attributes.get(attClass);
273 if (attImpl == null) {
274 if (!(attClass.isInterface() && Attribute.class.isAssignableFrom(attClass))) {
275 throw new IllegalArgumentException(
276 "addAttribute() only accepts an interface that extends Attribute, but " +
277 attClass.getName() + " does not fulfil this contract."
280 addAttributeImpl(attImpl = this.factory.createAttributeInstance(attClass));
282 return attClass.cast(attImpl);
285 /** Returns true, iff this AttributeSource has any attributes */
286 public boolean hasAttributes() {
287 return !this.attributes.isEmpty();
291 * The caller must pass in a Class<? extends Attribute> value.
292 * Returns true, iff this AttributeSource contains the passed-in Attribute.
294 public boolean hasAttribute(Class<? extends Attribute> attClass) {
295 return this.attributes.containsKey(attClass);
299 * The caller must pass in a Class<? extends Attribute> value.
300 * Returns the instance of the passed in Attribute contained in this AttributeSource
302 * @throws IllegalArgumentException if this AttributeSource does not contain the
303 * Attribute. It is recommended to always use {@link #addAttribute} even in consumers
304 * of TokenStreams, because you cannot know if a specific TokenStream really uses
305 * a specific Attribute. {@link #addAttribute} will automatically make the attribute
306 * available. If you want to only use the attribute, if it is available (to optimize
307 * consuming), use {@link #hasAttribute}.
309 public <A extends Attribute> A getAttribute(Class<A> attClass) {
310 AttributeImpl attImpl = attributes.get(attClass);
311 if (attImpl == null) {
312 throw new IllegalArgumentException("This AttributeSource does not have the attribute '" + attClass.getName() + "'.");
314 return attClass.cast(attImpl);
317 private State getCurrentState() {
318 State s = currentState[0];
319 if (s != null || !hasAttributes()) {
322 State c = s = currentState[0] = new State();
323 final Iterator<AttributeImpl> it = attributeImpls.values().iterator();
324 c.attribute = it.next();
325 while (it.hasNext()) {
326 c.next = new State();
328 c.attribute = it.next();
334 * Resets all Attributes in this AttributeSource by calling
335 * {@link AttributeImpl#clear()} on each Attribute implementation.
337 public void clearAttributes() {
338 for (State state = getCurrentState(); state != null; state = state.next) {
339 state.attribute.clear();
344 * Captures the state of all Attributes. The return value can be passed to
345 * {@link #restoreState} to restore the state of this or another AttributeSource.
347 public State captureState() {
348 final State state = this.getCurrentState();
349 return (state == null) ? null : (State) state.clone();
353 * Restores this state by copying the values of all attribute implementations
354 * that this state contains into the attributes implementations of the targetStream.
355 * The targetStream must contain a corresponding instance for each argument
356 * contained in this state (e.g. it is not possible to restore the state of
357 * an AttributeSource containing a TermAttribute into a AttributeSource using
358 * a Token instance as implementation).
360 * Note that this method does not affect attributes of the targetStream
361 * that are not contained in this state. In other words, if for example
362 * the targetStream contains an OffsetAttribute, but this state doesn't, then
363 * the value of the OffsetAttribute remains unchanged. It might be desirable to
364 * reset its value to the default, in which case the caller should first
365 * call {@link TokenStream#clearAttributes()} on the targetStream.
367 public void restoreState(State state) {
368 if (state == null) return;
371 AttributeImpl targetImpl = attributeImpls.get(state.attribute.getClass());
372 if (targetImpl == null) {
373 throw new IllegalArgumentException("State contains AttributeImpl of type " +
374 state.attribute.getClass().getName() + " that is not in in this AttributeSource");
376 state.attribute.copyTo(targetImpl);
378 } while (state != null);
382 public int hashCode() {
384 for (State state = getCurrentState(); state != null; state = state.next) {
385 code = code * 31 + state.attribute.hashCode();
391 public boolean equals(Object obj) {
396 if (obj instanceof AttributeSource) {
397 AttributeSource other = (AttributeSource) obj;
399 if (hasAttributes()) {
400 if (!other.hasAttributes()) {
404 if (this.attributeImpls.size() != other.attributeImpls.size()) {
408 // it is only equal if all attribute impls are the same in the same order
409 State thisState = this.getCurrentState();
410 State otherState = other.getCurrentState();
411 while (thisState != null && otherState != null) {
412 if (otherState.attribute.getClass() != thisState.attribute.getClass() || !otherState.attribute.equals(thisState.attribute)) {
415 thisState = thisState.next;
416 otherState = otherState.next;
420 return !other.hasAttributes();
427 * Returns a string representation of the object. In general, the {@code toString} method
428 * returns a string that "textually represents" this object.
430 * <p><b>WARNING:</b> For backwards compatibility this method is implemented as
431 * in Lucene 2.9/3.0. In Lucene 4.0 this default implementation
434 * <p>It is recommeneded to use {@link #reflectAsString} or {@link #reflectWith}
435 * to get a well-defined output of AttributeSource's internals.
437 // TODO: @deprecated remove this method in 4.0
439 public String toString() {
440 final StringBuilder sb = new StringBuilder().append('(');
441 if (hasAttributes()) {
442 for (State state = getCurrentState(); state != null; state = state.next) {
443 if (sb.length() > 1) sb.append(',');
444 sb.append(state.attribute.toString());
447 return sb.append(')').toString();
451 * This method returns the current attribute values as a string in the following format
452 * by calling the {@link #reflectWith(AttributeReflector)} method:
455 * <li><em>iff {@code prependAttClass=true}:</em> {@code "AttributeClass#key=value,AttributeClass#key=value"}
456 * <li><em>iff {@code prependAttClass=false}:</em> {@code "key=value,key=value"}
459 * @see #reflectWith(AttributeReflector)
461 public final String reflectAsString(final boolean prependAttClass) {
462 final StringBuilder buffer = new StringBuilder();
463 reflectWith(new AttributeReflector() {
464 public void reflect(Class<? extends Attribute> attClass, String key, Object value) {
465 if (buffer.length() > 0) {
468 if (prependAttClass) {
469 buffer.append(attClass.getName()).append('#');
471 buffer.append(key).append('=').append((value == null) ? "null" : value);
474 return buffer.toString();
478 * This method is for introspection of attributes, it should simply
479 * add the key/values this AttributeSource holds to the given {@link AttributeReflector}.
481 * <p>This method iterates over all Attribute implementations and calls the
482 * corresponding {@link AttributeImpl#reflectWith} method.</p>
484 * @see AttributeImpl#reflectWith
486 public final void reflectWith(AttributeReflector reflector) {
487 for (State state = getCurrentState(); state != null; state = state.next) {
488 state.attribute.reflectWith(reflector);
493 * Performs a clone of all {@link AttributeImpl} instances returned in a new
494 * {@code AttributeSource} instance. This method can be used to e.g. create another TokenStream
495 * with exactly the same attributes (using {@link #AttributeSource(AttributeSource)}).
496 * You can also use it as a (non-performant) replacement for {@link #captureState}, if you need to look
497 * into / modify the captured state.
499 public AttributeSource cloneAttributes() {
500 final AttributeSource clone = new AttributeSource(this.factory);
502 if (hasAttributes()) {
503 // first clone the impls
504 for (State state = getCurrentState(); state != null; state = state.next) {
505 clone.attributeImpls.put(state.attribute.getClass(), (AttributeImpl) state.attribute.clone());
508 // now the interfaces
509 for (Entry<Class<? extends Attribute>, AttributeImpl> entry : this.attributes.entrySet()) {
510 clone.attributes.put(entry.getKey(), clone.attributeImpls.get(entry.getValue().getClass()));
518 * Copies the contents of this {@code AttributeSource} to the given target {@code AttributeSource}.
519 * The given instance has to provide all {@link Attribute}s this instance contains.
520 * The actual attribute implementations must be identical in both {@code AttributeSource} instances;
521 * ideally both AttributeSource instances should use the same {@link AttributeFactory}.
522 * You can use this method as a replacement for {@link #restoreState}, if you use
523 * {@link #cloneAttributes} instead of {@link #captureState}.
525 public final void copyTo(AttributeSource target) {
526 for (State state = getCurrentState(); state != null; state = state.next) {
527 final AttributeImpl targetImpl = target.attributeImpls.get(state.attribute.getClass());
528 if (targetImpl == null) {
529 throw new IllegalArgumentException("This AttributeSource contains AttributeImpl of type " +
530 state.attribute.getClass().getName() + " that is not in the target");
532 state.attribute.copyTo(targetImpl);