add --shared
[pylucene.git] / lucene-java-3.4.0 / lucene / src / java / org / apache / lucene / util / AttributeSource.java
1 package org.apache.lucene.util;
2
3 /**
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
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
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.
18  */
19
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;
27 import java.util.Map;
28 import java.util.Map.Entry;
29
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;
33
34 /**
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.
42  */
43 public class AttributeSource {
44   /**
45    * An AttributeFactory creates instances of {@link AttributeImpl}s.
46    */
47   public static abstract class AttributeFactory {
48     /**
49      * returns an {@link AttributeImpl} for the supplied {@link Attribute} interface class.
50      */
51     public abstract AttributeImpl createAttributeInstance(Class<? extends Attribute> attClass);
52     
53     /**
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.
56      */
57     public static final AttributeFactory DEFAULT_ATTRIBUTE_FACTORY = new DefaultAttributeFactory();
58     
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>>>();
62       
63       private DefaultAttributeFactory() {}
64     
65       @Override
66       public AttributeImpl createAttributeInstance(Class<? extends Attribute> attClass) {
67         try {
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());
73         }
74       }
75       
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();
80           if (clazz == null) {
81             try {
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;
87               } else {
88                 clazz = Class.forName(attClass.getName() + "Impl", true, attClass.getClassLoader())
89                   .asSubclass(AttributeImpl.class);
90               }
91               attClassImplMap.put(attClass,
92                 new WeakReference<Class<? extends AttributeImpl>>(clazz)
93               );
94             } catch (ClassNotFoundException e) {
95               throw new IllegalArgumentException("Could not find implementing class for " + attClass.getName());
96             }
97           }
98           return clazz;
99         }
100       }
101     }
102   }
103       
104   /**
105    * This class holds the state of an AttributeSource.
106    * @see #captureState
107    * @see #restoreState
108    */
109   public static final class State implements Cloneable {
110     AttributeImpl attribute;
111     State next;
112     
113     @Override
114     public Object clone() {
115       State clone = new State();
116       clone.attribute = (AttributeImpl) attribute.clone();
117       
118       if (next != null) {
119         clone.next = (State) next.clone();
120       }
121       
122       return clone;
123     }
124   }
125     
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;
131
132   private AttributeFactory factory;
133   
134   /**
135    * An AttributeSource using the default attribute factory {@link AttributeSource.AttributeFactory#DEFAULT_ATTRIBUTE_FACTORY}.
136    */
137   public AttributeSource() {
138     this(AttributeFactory.DEFAULT_ATTRIBUTE_FACTORY);
139   }
140   
141   /**
142    * An AttributeSource that uses the same attributes as the supplied one.
143    */
144   public AttributeSource(AttributeSource input) {
145     if (input == null) {
146       throw new IllegalArgumentException("input AttributeSource must not be null");
147     }
148     this.attributes = input.attributes;
149     this.attributeImpls = input.attributeImpls;
150     this.currentState = input.currentState;
151     this.factory = input.factory;
152   }
153   
154   /**
155    * An AttributeSource using the supplied {@link AttributeFactory} for creating new {@link Attribute} instances.
156    */
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;
162   }
163   
164   /**
165    * returns the used AttributeFactory.
166    */
167   public AttributeFactory getAttributeFactory() {
168     return this.factory;
169   }
170   
171   /** Returns a new iterator that iterates the attribute classes
172    * in the same order they were added in.
173    */
174   public Iterator<Class<? extends Attribute>> getAttributeClassesIterator() {
175     return Collections.unmodifiableSet(attributes.keySet()).iterator();
176   }
177   
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.
181    */
182   public Iterator<AttributeImpl> getAttributeImplsIterator() {
183     final State initState = getCurrentState();
184     if (initState != null) {
185       return new Iterator<AttributeImpl>() {
186         private State state = initState;
187       
188         public void remove() {
189           throw new UnsupportedOperationException();
190         }
191         
192         public AttributeImpl next() {
193           if (state == null)
194             throw new NoSuchElementException();
195           final AttributeImpl att = state.attribute;
196           state = state.next;
197           return att;
198         }
199         
200         public boolean hasNext() {
201           return state != null;
202         }
203       };
204     } else {
205       return Collections.<AttributeImpl>emptySet().iterator();
206     }
207   }
208   
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>>>>();
212   
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;
223         do {
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)));
227             }
228           }
229           actClazz = actClazz.getSuperclass();
230         } while (actClazz != null);
231       }
232       return foundInterfaces;
233     }
234   }
235   
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}.
242    * </font></p>
243    */
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);
249     
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);
261       }
262     }
263   }
264   
265   /**
266    * The caller must pass in a Class&lt;? extends Attribute&gt; 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. 
270    */
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."
278         );
279       }
280       addAttributeImpl(attImpl = this.factory.createAttributeInstance(attClass));
281     }
282     return attClass.cast(attImpl);
283   }
284   
285   /** Returns true, iff this AttributeSource has any attributes */
286   public boolean hasAttributes() {
287     return !this.attributes.isEmpty();
288   }
289
290   /**
291    * The caller must pass in a Class&lt;? extends Attribute&gt; value. 
292    * Returns true, iff this AttributeSource contains the passed-in Attribute.
293    */
294   public boolean hasAttribute(Class<? extends Attribute> attClass) {
295     return this.attributes.containsKey(attClass);
296   }
297
298   /**
299    * The caller must pass in a Class&lt;? extends Attribute&gt; value. 
300    * Returns the instance of the passed in Attribute contained in this AttributeSource
301    * 
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}.
308    */
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() + "'.");
313     }
314     return attClass.cast(attImpl);
315   }
316     
317   private State getCurrentState() {
318     State s  = currentState[0];
319     if (s != null || !hasAttributes()) {
320       return s;
321     }
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();
327       c = c.next;
328       c.attribute = it.next();
329     }
330     return s;
331   }
332   
333   /**
334    * Resets all Attributes in this AttributeSource by calling
335    * {@link AttributeImpl#clear()} on each Attribute implementation.
336    */
337   public void clearAttributes() {
338     for (State state = getCurrentState(); state != null; state = state.next) {
339       state.attribute.clear();
340     }
341   }
342   
343   /**
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.
346    */
347   public State captureState() {
348     final State state = this.getCurrentState();
349     return (state == null) ? null : (State) state.clone();
350   }
351   
352   /**
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).
359    * <p>
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.   
366    */
367   public void restoreState(State state) {
368     if (state == null)  return;
369     
370     do {
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");
375       }
376       state.attribute.copyTo(targetImpl);
377       state = state.next;
378     } while (state != null);
379   }
380
381   @Override
382   public int hashCode() {
383     int code = 0;
384     for (State state = getCurrentState(); state != null; state = state.next) {
385       code = code * 31 + state.attribute.hashCode();
386     }
387     return code;
388   }
389   
390   @Override
391   public boolean equals(Object obj) {
392     if (obj == this) {
393       return true;
394     }
395
396     if (obj instanceof AttributeSource) {
397       AttributeSource other = (AttributeSource) obj;  
398     
399       if (hasAttributes()) {
400         if (!other.hasAttributes()) {
401           return false;
402         }
403         
404         if (this.attributeImpls.size() != other.attributeImpls.size()) {
405           return false;
406         }
407   
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)) {
413             return false;
414           }
415           thisState = thisState.next;
416           otherState = otherState.next;
417         }
418         return true;
419       } else {
420         return !other.hasAttributes();
421       }
422     } else
423       return false;
424   }
425   
426   /**
427    * Returns a string representation of the object. In general, the {@code toString} method
428    * returns a string that &quot;textually represents&quot; this object.
429    *
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
432    * will be removed.
433    *
434    * <p>It is recommeneded to use {@link #reflectAsString} or {@link #reflectWith}
435    * to get a well-defined output of AttributeSource's internals.
436    */
437   // TODO: @deprecated remove this method in 4.0
438   @Override
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());
445       }
446     }
447     return sb.append(')').toString();
448   }
449   
450   /**
451    * This method returns the current attribute values as a string in the following format
452    * by calling the {@link #reflectWith(AttributeReflector)} method:
453    * 
454    * <ul>
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"}
457    * </ul>
458    *
459    * @see #reflectWith(AttributeReflector)
460    */
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) {
466           buffer.append(',');
467         }
468         if (prependAttClass) {
469           buffer.append(attClass.getName()).append('#');
470         }
471         buffer.append(key).append('=').append((value == null) ? "null" : value);
472       }
473     });
474     return buffer.toString();
475   }
476   
477   /**
478    * This method is for introspection of attributes, it should simply
479    * add the key/values this AttributeSource holds to the given {@link AttributeReflector}.
480    *
481    * <p>This method iterates over all Attribute implementations and calls the
482    * corresponding {@link AttributeImpl#reflectWith} method.</p>
483    *
484    * @see AttributeImpl#reflectWith
485    */
486   public final void reflectWith(AttributeReflector reflector) {
487     for (State state = getCurrentState(); state != null; state = state.next) {
488       state.attribute.reflectWith(reflector);
489     }
490   }
491
492   /**
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.
498    */
499   public AttributeSource cloneAttributes() {
500     final AttributeSource clone = new AttributeSource(this.factory);
501     
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());
506       }
507       
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()));
511       }
512     }
513     
514     return clone;
515   }
516   
517   /**
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}.
524    */
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");
531       }
532       state.attribute.copyTo(targetImpl);
533     }
534   }
535
536 }