Lucene.Net  3.0.3
Lucene.Net is a port of the Lucene search engine library, written in C# and targeted at .NET runtime users.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Properties Pages
AttributeSource.cs
Go to the documentation of this file.
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 using System;
19 using System.Collections.Generic;
20 using System.Linq;
21 using System.Reflection;
22 using Lucene.Net.Support;
23 using TokenStream = Lucene.Net.Analysis.TokenStream;
24 
25 namespace Lucene.Net.Util
26 {
27 
28  /// <summary> An AttributeSource contains a list of different <see cref="Attribute" />s,
29  /// and methods to add and get them. There can only be a single instance
30  /// of an attribute in the same AttributeSource instance. This is ensured
31  /// by passing in the actual type of the Attribute (Class&lt;Attribute&gt;) to
32  /// the <see cref="AddAttribute{T}()" />, which then checks if an instance of
33  /// that type is already present. If yes, it returns the instance, otherwise
34  /// it creates a new instance and returns it.
35  /// </summary>
36  public class AttributeSource
37  {
38  /// <summary> An AttributeFactory creates instances of <see cref="Attribute" />s.</summary>
39  public abstract class AttributeFactory
40  {
41  /// <summary> returns an <see cref="Attribute" /> for the supplied <see cref="IAttribute" /> interface class.</summary>
42  public abstract Attribute CreateAttributeInstance<T>() where T : IAttribute;
43 
44  /// <summary> This is the default factory that creates <see cref="Attribute" />s using the
45  /// class name of the supplied <see cref="IAttribute" /> interface class by appending <c>Impl</c> to it.
46  /// </summary>
47  public static readonly AttributeFactory DEFAULT_ATTRIBUTE_FACTORY = new DefaultAttributeFactory();
48 
49  private sealed class DefaultAttributeFactory:AttributeFactory
50  {
51  // This should be WeakDictionary<T, WeakReference<TImpl>> where typeof(T) is Attribute and TImpl is typeof(AttributeImpl)
52  private static readonly WeakDictionary<Type, WeakReference> attClassImplMap =
53  new WeakDictionary<Type, WeakReference>();
54 
55  internal DefaultAttributeFactory()
56  {
57  }
58 
59  public override Attribute CreateAttributeInstance<TAttImpl>()
60  {
61  try
62  {
63  return (Attribute)System.Activator.CreateInstance(GetClassForInterface<TAttImpl>());
64  }
65  catch (System.UnauthorizedAccessException)
66  {
67  throw new System.ArgumentException("Could not instantiate implementing class for " + typeof(TAttImpl).FullName);
68  }
69  //catch (System.Exception e)
70  //{
71  // throw new System.ArgumentException("Could not instantiate implementing class for " + typeof(TAttImpl).FullName);
72  //}
73  }
74 
75  private static System.Type GetClassForInterface<T>() where T : IAttribute
76  {
77  lock (attClassImplMap)
78  {
79  var attClass = typeof (T);
80  WeakReference refz = attClassImplMap[attClass];
81  System.Type clazz = (refz == null) ? null : ((System.Type) refz.Target);
82  if (clazz == null)
83  {
84  try
85  {
86  string name = attClass.FullName.Replace(attClass.Name, attClass.Name.Substring(1)) + ", " + attClass.Assembly.FullName;
87  attClassImplMap.Add(attClass, new WeakReference( clazz = System.Type.GetType(name, true))); //OK
88  }
89  catch (System.TypeLoadException) // was System.Exception
90  {
91  throw new System.ArgumentException("Could not find implementing class for " + attClass.FullName);
92  }
93  }
94  return clazz;
95  }
96  }
97  }
98  }
99 
100  // These two maps must always be in sync!!!
101  // So they are private, final and read-only from the outside (read-only iterators)
102  private GeneralKeyedCollection<Type, AttributeImplItem> attributes;
103  private GeneralKeyedCollection<Type, AttributeImplItem> attributeImpls;
104 
105  private State[] currentState = null;
106  private AttributeFactory factory;
107 
108  /// <summary> An AttributeSource using the default attribute factory <see cref="AttributeSource.AttributeFactory.DEFAULT_ATTRIBUTE_FACTORY" />.</summary>
109  public AttributeSource():this(AttributeFactory.DEFAULT_ATTRIBUTE_FACTORY)
110  {
111  }
112 
113  /// <summary> An AttributeSource that uses the same attributes as the supplied one.</summary>
115  {
116  if (input == null)
117  {
118  throw new System.ArgumentException("input AttributeSource must not be null");
119  }
120  this.attributes = input.attributes;
121  this.attributeImpls = input.attributeImpls;
122  this.currentState = input.currentState;
123  this.factory = input.factory;
124  }
125 
126  /// <summary> An AttributeSource using the supplied <see cref="AttributeFactory" /> for creating new <see cref="IAttribute" /> instances.</summary>
128  {
129  this.attributes = new GeneralKeyedCollection<Type, AttributeImplItem>(att => att.Key);
130  this.attributeImpls = new GeneralKeyedCollection<Type, AttributeImplItem>(att => att.Key);
131  this.currentState = new State[1];
132  this.factory = factory;
133  }
134 
135  /// <summary>Returns the used AttributeFactory.</summary>
136  public virtual AttributeFactory Factory
137  {
138  get { return factory; }
139  }
140 
141  /// <summary>Returns a new iterator that iterates the attribute classes
142  /// in the same order they were added in.
143  /// Signature for Java 1.5: <c>public Iterator&lt;Class&lt;? extends Attribute&gt;&gt; getAttributeClassesIterator()</c>
144  ///
145  /// Note that this return value is different from Java in that it enumerates over the values
146  /// and not the keys
147  /// </summary>
148  [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
149  public virtual IEnumerable<Type> GetAttributeTypesIterator()
150  {
151  return this.attributes.Select(item => item.Key);
152  }
153 
154  /// <summary>Returns a new iterator that iterates all unique Attribute implementations.
155  /// This iterator may contain less entries that <see cref="GetAttributeTypesIterator" />,
156  /// if one instance implements more than one Attribute interface.
157  /// Signature for Java 1.5: <c>public Iterator&lt;AttributeImpl&gt; getAttributeImplsIterator()</c>
158  /// </summary>
159  [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
160  public virtual IEnumerable<Attribute> GetAttributeImplsIterator()
161  {
162  var initState = GetCurrentState();
163  while (initState != null)
164  {
165  var att = initState.attribute;
166  initState = initState.next;
167  yield return att;
168  }
169  }
170 
171  /// <summary>a cache that stores all interfaces for known implementation classes for performance (slow reflection) </summary>
172  private static readonly WeakDictionary<Type, System.Collections.Generic.LinkedList<WeakReference>>
173  knownImplClasses = new WeakDictionary<Type, System.Collections.Generic.LinkedList<WeakReference>>();
174 
175  /// <summary>
176  /// <b>Expert:</b> Adds a custom AttributeImpl instance with one or more Attribute interfaces.
177  /// <p><font color="red"><b>Please note:</b> It is not guaranteed, that <c>att</c> is added to
178  /// the <c>AttributeSource</c>, because the provided attributes may already exist.
179  /// You should always retrieve the wanted attributes using <see cref="GetAttribute{T}"/> after adding
180  /// with this method and cast to your class.
181  /// The recommended way to use custom implementations is using an <see cref="AttributeFactory"/>
182  /// </font></p>
183  /// </summary>
184  public virtual void AddAttributeImpl(Attribute att)
185  {
186  System.Type clazz = att.GetType();
187  if (attributeImpls.Contains(clazz))
188  return ;
189  System.Collections.Generic.LinkedList<WeakReference> foundInterfaces;
190  lock (knownImplClasses)
191  {
192  foundInterfaces = knownImplClasses[clazz];
193  if (foundInterfaces == null)
194  {
195  // we have a strong reference to the class instance holding all interfaces in the list (parameter "att"),
196  // so all WeakReferences are never evicted by GC
197  knownImplClasses.Add(clazz, foundInterfaces = new LinkedList<WeakReference>());
198  // find all interfaces that this attribute instance implements
199  // and that extend the Attribute interface
200  System.Type actClazz = clazz;
201  do
202  {
203  System.Type[] interfaces = actClazz.GetInterfaces();
204  for (int i = 0; i < interfaces.Length; i++)
205  {
206  System.Type curInterface = interfaces[i];
207  if (curInterface != typeof(IAttribute) && typeof(IAttribute).IsAssignableFrom(curInterface))
208  {
209  foundInterfaces.AddLast(new WeakReference(curInterface));
210  }
211  }
212  actClazz = actClazz.BaseType;
213  }
214  while (actClazz != null);
215  }
216  }
217 
218  // add all interfaces of this AttributeImpl to the maps
219  foreach(var curInterfaceRef in foundInterfaces)
220  {
221  System.Type curInterface = (System.Type) curInterfaceRef.Target;
222  System.Diagnostics.Debug.Assert(curInterface != null,
223  "We have a strong reference on the class holding the interfaces, so they should never get evicted");
224  // Attribute is a superclass of this interface
225  if (!attributes.ContainsKey(curInterface))
226  {
227  // invalidate state to force recomputation in captureState()
228  this.currentState[0] = null;
229  attributes.Add(new AttributeImplItem(curInterface, att));
230  if (!attributeImpls.ContainsKey(clazz))
231  {
232  attributeImpls.Add(new AttributeImplItem(clazz, att));
233  }
234  }
235  }
236  }
237 
238  /// <summary> The caller must pass in a Class&lt;? extends Attribute&gt; value.
239  /// This method first checks if an instance of that class is
240  /// already in this AttributeSource and returns it. Otherwise a
241  /// new instance is created, added to this AttributeSource and returned.
242  /// </summary>
243  // NOTE: Java has Class<T>, .NET has no Type<T>, this is not a perfect port
244  public virtual T AddAttribute<T>() where T : IAttribute
245  {
246  var attClass = typeof (T);
247  if (!attributes.ContainsKey(attClass))
248  {
249  if (!(attClass.IsInterface && typeof(IAttribute).IsAssignableFrom(attClass)))
250  {
251  throw new ArgumentException(
252  "AddAttribute() only accepts an interface that extends Attribute, but " +
253  attClass.FullName + " does not fulfil this contract."
254  );
255  }
256 
257  AddAttributeImpl(this.factory.CreateAttributeInstance<T>());
258  }
259 
260  return (T)(IAttribute)attributes[attClass].Value;
261  }
262 
263  /// <summary>Returns true, iff this AttributeSource has any attributes </summary>
264  public virtual bool HasAttributes
265  {
266  get { return this.attributes.Count != 0; }
267  }
268 
269  /// <summary> The caller must pass in a Class&lt;? extends Attribute&gt; value.
270  /// Returns true, iff this AttributeSource contains the passed-in Attribute.
271  /// </summary>\
272  public virtual bool HasAttribute<T>() where T : IAttribute
273  {
274  return this.attributes.Contains(typeof(T));
275  }
276 
277  /// <summary>
278  /// The caller must pass in a Class&lt;? extends Attribute&gt; value.
279  /// Returns the instance of the passed in Attribute contained in this AttributeSource
280  /// </summary>
281  /// <throws>
282  /// IllegalArgumentException if this AttributeSource does not contain the Attribute.
283  /// It is recommended to always use <see cref="AddAttribute{T}" /> even in consumers
284  /// of TokenStreams, because you cannot know if a specific TokenStream really uses
285  /// a specific Attribute. <see cref="AddAttribute{T}" /> will automatically make the attribute
286  /// available. If you want to only use the attribute, if it is available (to optimize
287  /// consuming), use <see cref="HasAttribute" />.
288  /// </throws>
289  // NOTE: Java has Class<T>, .NET has no Type<T>, this is not a perfect port
290  public virtual T GetAttribute<T>() where T : IAttribute
291  {
292  var attClass = typeof (T);
293  if (!this.attributes.ContainsKey(attClass))
294  {
295  throw new System.ArgumentException("This AttributeSource does not have the attribute '" + attClass.FullName + "'.");
296  }
297  else
298  {
299  return (T)(IAttribute)this.attributes[attClass].Value;
300  }
301  }
302 
303  /// <summary> This class holds the state of an AttributeSource.</summary>
304  /// <seealso cref="CaptureState">
305  /// </seealso>
306  /// <seealso cref="RestoreState">
307  /// </seealso>
308  public sealed class State : System.ICloneable
309  {
310  internal /*private*/ Attribute attribute;
311  internal /*private*/ State next;
312 
313  public System.Object Clone()
314  {
315  State clone = new State();
316  clone.attribute = (Attribute) attribute.Clone();
317 
318  if (next != null)
319  {
320  clone.next = (State) next.Clone();
321  }
322 
323  return clone;
324  }
325  }
326 
327  private State GetCurrentState()
328  {
329  var s = currentState[0];
330  if (s != null || !HasAttributes)
331  {
332  return s;
333  }
334 
335  var c = s = currentState[0] = new State();
336  var it = attributeImpls.Values().GetEnumerator();
337  it.MoveNext();
338  c.attribute = it.Current.Value;
339 
340  while (it.MoveNext())
341  {
342  c.next = new State();
343  c = c.next;
344  c.attribute = it.Current.Value;
345  }
346 
347  return s;
348  }
349 
350  /// <summary> Resets all Attributes in this AttributeSource by calling
351  /// <see cref="Attribute.Clear()" /> on each Attribute implementation.
352  /// </summary>
353  public virtual void ClearAttributes()
354  {
355  for (var state = GetCurrentState(); state != null; state = state.next)
356  {
357  state.attribute.Clear();
358  }
359  }
360 
361  /// <summary> Captures the state of all Attributes. The return value can be passed to
362  /// <see cref="RestoreState" /> to restore the state of this or another AttributeSource.
363  /// </summary>
364  public virtual State CaptureState()
365  {
366  var state = this.GetCurrentState();
367  return (state == null) ? null : (State) state.Clone();
368  }
369 
370  /// <summary> Restores this state by copying the values of all attribute implementations
371  /// that this state contains into the attributes implementations of the targetStream.
372  /// The targetStream must contain a corresponding instance for each argument
373  /// contained in this state (e.g. it is not possible to restore the state of
374  /// an AttributeSource containing a TermAttribute into a AttributeSource using
375  /// a Token instance as implementation).
376  ///
377  /// Note that this method does not affect attributes of the targetStream
378  /// that are not contained in this state. In other words, if for example
379  /// the targetStream contains an OffsetAttribute, but this state doesn't, then
380  /// the value of the OffsetAttribute remains unchanged. It might be desirable to
381  /// reset its value to the default, in which case the caller should first
382  /// call <see cref="AttributeSource.ClearAttributes()" /> on the targetStream.
383  /// </summary>
384  public virtual void RestoreState(State state)
385  {
386  if (state == null)
387  return ;
388 
389  do
390  {
391  if (!attributeImpls.ContainsKey(state.attribute.GetType()))
392  {
393  throw new System.ArgumentException("State contains an AttributeImpl that is not in this AttributeSource");
394  }
395  state.attribute.CopyTo(attributeImpls[state.attribute.GetType()].Value);
396  state = state.next;
397  }
398  while (state != null);
399  }
400 
401  public override int GetHashCode()
402  {
403  var code = 0;
404 
405  for (var state = GetCurrentState(); state != null; state = state.next)
406  {
407  code = code*31 + state.attribute.GetHashCode();
408  }
409 
410  return code;
411  }
412 
413  public override bool Equals(System.Object obj)
414  {
415  if (obj == this)
416  {
417  return true;
418  }
419 
420  if (obj is AttributeSource)
421  {
422  AttributeSource other = (AttributeSource) obj;
423 
424  if (HasAttributes)
425  {
426  if (!other.HasAttributes)
427  {
428  return false;
429  }
430 
431  if (this.attributeImpls.Count != other.attributeImpls.Count)
432  {
433  return false;
434  }
435 
436  // it is only equal if all attribute impls are the same in the same order
437  var thisState = this.GetCurrentState();
438  var otherState = other.GetCurrentState();
439  while (thisState != null && otherState != null)
440  {
441  if (otherState.attribute.GetType() != thisState.attribute.GetType() || !otherState.attribute.Equals(thisState.attribute))
442  {
443  return false;
444  }
445  thisState = thisState.next;
446  otherState = otherState.next;
447  }
448  return true;
449  }
450  else
451  {
452  return !other.HasAttributes;
453  }
454  }
455  else
456  return false;
457  }
458 
459  public override System.String ToString()
460  {
461  System.Text.StringBuilder sb = new System.Text.StringBuilder().Append('(');
462 
463  if (HasAttributes)
464  {
465  if (currentState[0] == null)
466  {
467  currentState[0] = GetCurrentState();
468  }
469  for (var state = currentState[0]; state != null; state = state.next)
470  {
471  if (state != currentState[0])
472  sb.Append(',');
473  sb.Append(state.attribute.ToString());
474  }
475  }
476  return sb.Append(')').ToString();
477  }
478 
479  /// <summary> Performs a clone of all <see cref="Attribute" /> instances returned in a new
480  /// AttributeSource instance. This method can be used to e.g. create another TokenStream
481  /// with exactly the same attributes (using <see cref="AttributeSource(AttributeSource)" />)
482  /// </summary>
483  public virtual AttributeSource CloneAttributes()
484  {
485  var clone = new AttributeSource(this.factory);
486 
487  // first clone the impls
488  if (HasAttributes)
489  {
490  for (var state = GetCurrentState(); state != null; state = state.next)
491  {
492  var impl = (Attribute) state.attribute.Clone();
493 
494  if (!clone.attributeImpls.ContainsKey(impl.GetType()))
495  {
496  clone.attributeImpls.Add(new AttributeImplItem(impl.GetType(), impl));
497  }
498  }
499  }
500 
501  // now the interfaces
502  foreach (var att in this.attributes)
503  {
504  clone.attributes.Add(new AttributeImplItem(att.Key, clone.attributeImpls[att.Value.GetType()].Value));
505  }
506 
507  return clone;
508  }
509  }
510 }