pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / src / java / org / apache / lucene / util / AttributeImpl.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.io.Serializable;
21 import java.lang.reflect.Field;
22 import java.lang.reflect.Modifier;
23 import java.lang.ref.WeakReference;
24 import java.util.LinkedList;
25
26 import org.apache.lucene.analysis.tokenattributes.CharTermAttributeImpl; // deprecated
27
28 /**
29  * Base class for Attributes that can be added to a 
30  * {@link org.apache.lucene.util.AttributeSource}.
31  * <p>
32  * Attributes are used to add data in a dynamic, yet type-safe way to a source
33  * of usually streamed objects, e. g. a {@link org.apache.lucene.analysis.TokenStream}.
34  */
35 public abstract class AttributeImpl implements Cloneable, Serializable, Attribute {  
36   /**
37    * Clears the values in this AttributeImpl and resets it to its 
38    * default value. If this implementation implements more than one Attribute interface
39    * it clears all.
40    */
41   public abstract void clear();
42   
43   /**
44    * Returns a string representation of the object. In general, the {@code toString} method
45    * returns a string that &quot;textually represents&quot; this object.
46    *
47    * <p><b>WARNING:</b> For backwards compatibility this method is implemented as
48    * {@code return reflectAsString(false)}. In Lucene 4.0 this default implementation
49    * will be removed. The reason for this is the problem of
50    * {@link CharTermAttributeImpl#toString} that must return a string representation
51    * of the term's char sequence.
52    *
53    * <p>It is recommeneded to use {@link #reflectAsString} or {@link #reflectWith}
54    * to get a well-defined output of AttributeImpl's internals.
55    */
56   // TODO: @deprecated remove this method in 4.0
57   @Override
58   public String toString() {
59     return reflectAsString(false);
60   }
61   
62   /**
63    * This method returns the current attribute values as a string in the following format
64    * by calling the {@link #reflectWith(AttributeReflector)} method:
65    * 
66    * <ul>
67    * <li><em>iff {@code prependAttClass=true}:</em> {@code "AttributeClass#key=value,AttributeClass#key=value"}
68    * <li><em>iff {@code prependAttClass=false}:</em> {@code "key=value,key=value"}
69    * </ul>
70    *
71    * @see #reflectWith(AttributeReflector)
72    * @see #toString()
73    */
74   public final String reflectAsString(final boolean prependAttClass) {
75     final StringBuilder buffer = new StringBuilder();
76     reflectWith(new AttributeReflector() {
77       public void reflect(Class<? extends Attribute> attClass, String key, Object value) {
78         if (buffer.length() > 0) {
79           buffer.append(',');
80         }
81         if (prependAttClass) {
82           buffer.append(attClass.getName()).append('#');
83         }
84         buffer.append(key).append('=').append((value == null) ? "null" : value);
85       }
86     });
87     return buffer.toString();
88   }
89   
90   /**
91    * @deprecated this will be removed in Lucene 4.0
92    */
93   @Deprecated
94   private static final VirtualMethod<AttributeImpl> toStringMethod =
95     new VirtualMethod<AttributeImpl>(AttributeImpl.class, "toString");
96     
97   /**
98    * When {@code true} (default), if a subclass overrides {@link #toString()},
99    * its output is parsed by {@link #reflectWith} and used for attribute reflection.
100    * This is added to enable attribute implementations from Lucene 2.9 or 3.0 to
101    * work correctly with reflection.
102    * @deprecated this will be removed in Lucene 4.0.
103    */
104   @Deprecated
105   protected boolean enableBackwards = true;
106
107   /**
108    * @deprecated this will be removed in Lucene 4.0
109    */
110   @Deprecated
111   private boolean assertExternalClass(Class<? extends AttributeImpl> clazz) {
112     final String name = clazz.getName();
113     return (!name.startsWith("org.apache.lucene.") && !name.startsWith("org.apache.solr."))
114       || name.equals("org.apache.lucene.util.TestAttributeSource$TestAttributeImpl");
115   }
116
117   /**
118    * This method is for introspection of attributes, it should simply
119    * add the key/values this attribute holds to the given {@link AttributeReflector}.
120    *
121    * <p>The default implementation calls {@link AttributeReflector#reflect} for all
122    * non-static fields from the implementing class, using the field name as key
123    * and the field value as value. The Attribute class is also determined by reflection.
124    * Please note that the default implementation can only handle single-Attribute
125    * implementations.
126    *
127    * <p>Custom implementations look like this (e.g. for a combined attribute implementation):
128    * <pre>
129    *   public void reflectWith(AttributeReflector reflector) {
130    *     reflector.reflect(CharTermAttribute.class, "term", term());
131    *     reflector.reflect(PositionIncrementAttribute.class, "positionIncrement", getPositionIncrement());
132    *   }
133    * </pre>
134    *
135    * <p>If you implement this method, make sure that for each invocation, the same set of {@link Attribute}
136    * interfaces and keys are passed to {@link AttributeReflector#reflect} in the same order, but possibly
137    * different values. So don't automatically exclude e.g. {@code null} properties!
138    *
139    * @see #reflectAsString(boolean)
140    */
141   public void reflectWith(AttributeReflector reflector) {
142     final Class<? extends AttributeImpl> clazz = this.getClass();
143     final LinkedList<WeakReference<Class<? extends Attribute>>> interfaces = AttributeSource.getAttributeInterfaces(clazz);
144     if (interfaces.size() != 1) {
145       throw new UnsupportedOperationException(clazz.getName() +
146         " implements more than one Attribute interface, the default reflectWith() implementation cannot handle this.");
147     }
148     final Class<? extends Attribute> interf = interfaces.getFirst().get();
149
150     // TODO: @deprecated sophisticated(TM) backwards
151     if (enableBackwards && toStringMethod.isOverriddenAsOf(clazz)) {
152       assert assertExternalClass(clazz) : "no Lucene/Solr classes should fallback to toString() parsing";
153       // this class overrides toString and for backwards compatibility we try to parse the string returned by this method:
154       for (String part : toString().split(",")) {
155         final int pos = part.indexOf('=');
156         if (pos < 0) {
157           throw new UnsupportedOperationException("The backwards compatibility layer to support reflectWith() " +
158             "on old AtributeImpls expects the toString() implementation to return a correct format as specified for method reflectAsString(false)");
159         }
160         reflector.reflect(interf, part.substring(0, pos).trim(), part.substring(pos + 1));
161       }
162       return;
163     }
164     // end sophisticated(TM) backwards
165
166     final Field[] fields = clazz.getDeclaredFields();
167     try {
168       for (int i = 0; i < fields.length; i++) {
169         final Field f = fields[i];
170         if (Modifier.isStatic(f.getModifiers())) continue;
171         f.setAccessible(true);
172         reflector.reflect(interf, f.getName(), f.get(this));
173       }
174     } catch (IllegalAccessException e) {
175       // this should never happen, because we're just accessing fields
176       // from 'this'
177       throw new RuntimeException(e);
178     }
179   }
180   
181   /**
182    * Copies the values from this Attribute into the passed-in
183    * target attribute. The target implementation must support all the
184    * Attributes this implementation supports.
185    */
186   public abstract void copyTo(AttributeImpl target);
187     
188   /**
189    * Shallow clone. Subclasses must override this if they 
190    * need to clone any members deeply,
191    */
192   @Override
193   public Object clone() {
194     Object clone = null;
195     try {
196       clone = super.clone();
197     } catch (CloneNotSupportedException e) {
198       throw new RuntimeException(e);  // shouldn't happen
199     }
200     return clone;
201   }
202 }