/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using TokenStream = Lucene.Net.Analysis.TokenStream; namespace Lucene.Net.Util { /// An AttributeSource contains a list of different {@link AttributeImpl}s, /// and methods to add and get them. There can only be a single instance /// of an attribute in the same AttributeSource instance. This is ensured /// by passing in the actual type of the Attribute (Class<Attribute>) to /// the {@link #AddAttribute(Class)}, which then checks if an instance of /// that type is already present. If yes, it returns the instance, otherwise /// it creates a new instance and returns it. /// public class AttributeSource { /// An AttributeFactory creates instances of {@link AttributeImpl}s. public abstract class AttributeFactory { /// returns an {@link AttributeImpl} for the supplied {@link Attribute} interface class. ///

Signature for Java 1.5: public AttributeImpl createAttributeInstance(Class%lt;? extends Attribute> attClass) ///

public abstract AttributeImpl CreateAttributeInstance(System.Type attClass); /// This is the default factory that creates {@link AttributeImpl}s using the /// class name of the supplied {@link Attribute} interface class by appending Impl to it. /// public static readonly AttributeFactory DEFAULT_ATTRIBUTE_FACTORY = new DefaultAttributeFactory(); private sealed class DefaultAttributeFactory : AttributeFactory { private static readonly Support.WeakDictionary attClassImplMap = new Support.WeakDictionary(); internal DefaultAttributeFactory() { } public override AttributeImpl CreateAttributeInstance(System.Type attClass) { try { return (AttributeImpl)System.Activator.CreateInstance(GetClassForInterface(attClass)); } catch (System.UnauthorizedAccessException e) { throw new System.ArgumentException("Could not instantiate implementing class for " + attClass.FullName); } catch (System.Exception e) { throw new System.ArgumentException("Could not instantiate implementing class for " + attClass.FullName); } } private static System.Type GetClassForInterface(System.Type attClass) { lock (attClassImplMap) { WeakReference refz = attClassImplMap[attClass]; System.Type clazz = (refz == null) ? null : ((System.Type)refz.Target); if (clazz == null) { try { string name = attClass.FullName + "Impl," + attClass.Assembly.FullName; attClassImplMap.Add(attClass, new WeakReference(clazz = System.Type.GetType(name, true))); //OK } catch (System.Exception e) { throw new System.ArgumentException("Could not find implementing class for " + attClass.FullName); } } return clazz; } } } } /// This class holds the state of an AttributeSource. /// /// /// /// public sealed class State : System.ICloneable { internal /*private*/ AttributeImpl attribute; internal /*private*/ State next; public System.Object Clone() { State clone = new State(); clone.attribute = (AttributeImpl)attribute.Clone(); if (next != null) { clone.next = (State)next.Clone(); } return clone; } } // These two maps must always be in sync!!! // So they are private, final and read-only from the outside (read-only iterators) private Support.GeneralKeyedCollection attributes; private Support.GeneralKeyedCollection attributeImpls; private State[] currentState = null; private AttributeFactory factory; /// An AttributeSource using the default attribute factory {@link AttributeSource.AttributeFactory#DEFAULT_ATTRIBUTE_FACTORY}. public AttributeSource() : this(AttributeFactory.DEFAULT_ATTRIBUTE_FACTORY) { } /// An AttributeSource that uses the same attributes as the supplied one. public AttributeSource(AttributeSource input) { if (input == null) { throw new System.ArgumentException("input AttributeSource must not be null"); } this.attributes = input.attributes; this.attributeImpls = input.attributeImpls; this.currentState = input.currentState; this.factory = input.factory; } /// An AttributeSource using the supplied {@link AttributeFactory} for creating new {@link Attribute} instances. public AttributeSource(AttributeFactory factory) { this.attributes = new Support.GeneralKeyedCollection(delegate(Support.AttributeImplItem att) { return att.Key; }); this.attributeImpls = new Support.GeneralKeyedCollection(delegate(Support.AttributeImplItem att) { return att.Key; }); this.currentState = new State[1]; this.factory = factory; } /// returns the used AttributeFactory. public virtual AttributeFactory GetAttributeFactory() { return this.factory; } /// Returns a new iterator that iterates the attribute classes /// in the same order they were added in. /// Signature for Java 1.5: public Iterator<Class<? extends Attribute>> getAttributeClassesIterator() /// /// Note that this return value is different from Java in that it enumerates over the values /// and not the keys /// public virtual IEnumerable GetAttributeClassesIterator() { foreach (Support.AttributeImplItem item in this.attributes) { yield return item.Key; } } /// Returns a new iterator that iterates all unique Attribute implementations. /// This iterator may contain less entries that {@link #getAttributeClassesIterator}, /// if one instance implements more than one Attribute interface. /// Signature for Java 1.5: public Iterator<AttributeImpl> getAttributeImplsIterator() /// public virtual IEnumerable GetAttributeImplsIterator() { State initState = GetCurrentState(); if (initState != null) { return new AnonymousEnumerable(initState); } else { return new List(); } } class AnonymousEnumerable : IEnumerable { State state; public AnonymousEnumerable(State state) { this.state = state; } public IEnumerator GetEnumerator() { return new AnonymousEnumerator(state); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return new AnonymousEnumerator(state); } } class AnonymousEnumerator : IEnumerator { State state; public AnonymousEnumerator(State state) { this.state = state; } AttributeImpl InternalCurrent { get { if (state == null) throw new KeyNotFoundException(); AttributeImpl att = state.attribute; state = state.next; return att; } } public AttributeImpl Current { get { return InternalCurrent; } } public void Dispose() { } object System.Collections.IEnumerator.Current { get { return InternalCurrent; } } public bool MoveNext() { return state != null; } public void Reset() { } } /// a cache that stores all interfaces for known implementation classes for performance (slow reflection) private static readonly Support.WeakDictionary> knownImplClasses = new Support.WeakDictionary>(); // {{Aroush-2.9 Port issue, need to mimic java's IdentityHashMap /* * From Java docs: * This class implements the Map interface with a hash table, using * reference-equality in place of object-equality when comparing keys * (and values). In other words, in an IdentityHashMap, two keys k1 and k2 * are considered equal if and only if (k1==k2). (In normal Map * implementations (like HashMap) two keys k1 and k2 are considered * equal if and only if (k1==null ? k2==null : k1.equals(k2)).) */ // Aroush-2.9}} /// Adds a custom AttributeImpl instance with one or more Attribute interfaces. public virtual void AddAttributeImpl(AttributeImpl att) { System.Type clazz = att.GetType(); if (attributeImpls.Contains(clazz)) return; List foundInterfaces; lock (knownImplClasses) { foundInterfaces = knownImplClasses[clazz]; if (foundInterfaces == null) { // we have a strong reference to the class instance holding all interfaces in the list (parameter "att"), // so all WeakReferences are never evicted by GC knownImplClasses.Add(clazz, foundInterfaces = new List()); // find all interfaces that this attribute instance implements // and that extend the Attribute interface System.Type actClazz = clazz; do { System.Type[] interfaces = actClazz.GetInterfaces(); for (int i = 0; i < interfaces.Length; i++) { System.Type curInterface = interfaces[i]; if (curInterface != typeof(IAttribute) && typeof(IAttribute).IsAssignableFrom(curInterface)) { foundInterfaces.Add(new WeakReference(curInterface)); } } actClazz = actClazz.BaseType; } while (actClazz != null); } } // add all interfaces of this AttributeImpl to the maps foreach (WeakReference curInterfaceRef in foundInterfaces) { System.Type curInterface = (System.Type)curInterfaceRef.Target; System.Diagnostics.Debug.Assert(curInterface != null, "We have a strong reference on the class holding the interfaces, so they should never get evicted"); // Attribute is a superclass of this interface if (!attributes.ContainsKey(curInterface)) { // invalidate state to force recomputation in captureState() this.currentState[0] = null; attributes.Add(new Support.AttributeImplItem(curInterface, att)); if (!attributeImpls.ContainsKey(clazz)) { attributeImpls.Add(new Support.AttributeImplItem(clazz, att)); } } } } /// The caller must pass in a Class<? extends Attribute> value. /// This method first checks if an instance of that class is /// already in this AttributeSource and returns it. Otherwise a /// new instance is created, added to this AttributeSource and returned. /// Signature for Java 1.5: public <T extends Attribute> T addAttribute(Class<T>) /// public virtual IAttribute AddAttribute(System.Type attClass) { if (!attributes.ContainsKey(attClass)) { if (!(attClass.IsInterface && typeof(IAttribute).IsAssignableFrom(attClass))) { throw new ArgumentException( "AddAttribute() only accepts an interface that extends Attribute, but " + attClass.FullName + " does not fulfil this contract." ); } AttributeImpl attImpl = this.factory.CreateAttributeInstance(attClass); AddAttributeImpl(attImpl); return attImpl; } else { return attributes[attClass].Value; } } /// Returns true, iff this AttributeSource has any attributes public virtual bool HasAttributes() { return !(this.attributes.Count == 0); } /// The caller must pass in a Class<? extends Attribute> value. /// Returns true, iff this AttributeSource contains the passed-in Attribute. /// Signature for Java 1.5: public boolean hasAttribute(Class<? extends Attribute>) /// public virtual bool HasAttribute(System.Type attClass) { return this.attributes.Contains(attClass); } /// The caller must pass in a Class<? extends Attribute> value. /// Returns the instance of the passed in Attribute contained in this AttributeSource /// Signature for Java 1.5: public <T extends Attribute> T getAttribute(Class<T>) /// /// /// IllegalArgumentException if this AttributeSource does not contain the /// Attribute. It is recommended to always use {@link #addAttribute} even in consumers /// of TokenStreams, because you cannot know if a specific TokenStream really uses /// a specific Attribute. {@link #addAttribute} will automatically make the attribute /// available. If you want to only use the attribute, if it is available (to optimize /// consuming), use {@link #hasAttribute}. /// public virtual IAttribute GetAttribute(System.Type attClass) { if (!this.attributes.ContainsKey(attClass)) { throw new System.ArgumentException("This AttributeSource does not have the attribute '" + attClass.FullName + "'."); } else { return this.attributes[attClass].Value; } } //private void ComputeCurrentState() //{ // currentState = new State(); // State c = s = currentState[0] = new State(); // IEnumerator it = attributeImpls.GetEnumerator(); // if (it.MoveNext()) // c.attribute = it.Current.Value; // while (it.MoveNext()) // { // c.next = new State(); // c = c.next; // c.attribute = it.Current.Value; // } //} private State GetCurrentState() { State s = currentState[0]; if (s != null || !HasAttributes()) { return s; } State c = s = currentState[0] = new State(); var it = attributeImpls.Values().GetEnumerator(); it.MoveNext(); c.attribute = it.Current.Value; while (it.MoveNext()) { c.next = new State(); c = c.next; c.attribute = it.Current.Value; } return s; } /// Resets all Attributes in this AttributeSource by calling /// {@link AttributeImpl#Clear()} on each Attribute implementation. /// public virtual void ClearAttributes() { for (State state = GetCurrentState(); state != null; state = state.next) { state.attribute.Clear(); } } /// Captures the state of all Attributes. The return value can be passed to /// {@link #restoreState} to restore the state of this or another AttributeSource. /// public virtual State CaptureState() { State state = this.GetCurrentState(); return (state == null) ? null : (State)state.Clone(); } /// Restores this state by copying the values of all attribute implementations /// that this state contains into the attributes implementations of the targetStream. /// The targetStream must contain a corresponding instance for each argument /// contained in this state (e.g. it is not possible to restore the state of /// an AttributeSource containing a TermAttribute into a AttributeSource using /// a Token instance as implementation). /// /// Note that this method does not affect attributes of the targetStream /// that are not contained in this state. In other words, if for example /// the targetStream contains an OffsetAttribute, but this state doesn't, then /// the value of the OffsetAttribute remains unchanged. It might be desirable to /// reset its value to the default, in which case the caller should first /// call {@link TokenStream#ClearAttributes()} on the targetStream. /// public virtual void RestoreState(State state) { if (state == null) return; do { if (!attributeImpls.ContainsKey(state.attribute.GetType())) { throw new System.ArgumentException("State contains an AttributeImpl that is not in this AttributeSource"); } state.attribute.CopyTo(attributeImpls[state.attribute.GetType()].Value); state = state.next; } while (state != null); } public override int GetHashCode() { int code = 0; for (State state = GetCurrentState(); state != null; state = state.next) { code = code * 31 + state.attribute.GetHashCode(); } return code; } public override bool Equals(System.Object obj) { if (obj == this) { return true; } if (obj is AttributeSource) { AttributeSource other = (AttributeSource)obj; if (HasAttributes()) { if (!other.HasAttributes()) { return false; } if (this.attributeImpls.Count != other.attributeImpls.Count) { return false; } // it is only equal if all attribute impls are the same in the same order State thisState = this.GetCurrentState(); State otherState = other.GetCurrentState(); while (thisState != null && otherState != null) { if (otherState.attribute.GetType() != thisState.attribute.GetType() || !otherState.attribute.Equals(thisState.attribute)) { return false; } thisState = thisState.next; otherState = otherState.next; } return true; } else { return !other.HasAttributes(); } } else return false; } public override System.String ToString() { System.Text.StringBuilder sb = new System.Text.StringBuilder(); sb.Append('('); if (HasAttributes()) { if (currentState[0] == null) { currentState[0] = GetCurrentState(); } for (State state = currentState[0]; state != null; state = state.next) { if (state != currentState[0]) sb.Append(','); sb.Append(state.attribute.ToString()); } } sb.Append(')'); return sb.ToString(); } /// 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)}) /// public virtual AttributeSource CloneAttributes() { AttributeSource clone = new AttributeSource(this.factory); // first clone the impls if (HasAttributes()) { for (State state = GetCurrentState(); state != null; state = state.next) { AttributeImpl impl = (AttributeImpl)state.attribute.Clone(); if (!clone.attributeImpls.ContainsKey(impl.GetType())) { clone.attributeImpls.Add(new Support.AttributeImplItem(impl.GetType(), impl)); } } } // now the interfaces foreach (Support.AttributeImplItem att in this.attributes) { clone.attributes.Add(new Support.AttributeImplItem(att.Key, clone.attributeImpls[att.Value.GetType()].Value)); } return clone; } } }