/* * 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 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 System.Collections.Hashtable attClassImplMap = new System.Collections.Hashtable(); // {{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}} 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 class " + attClass.FullName); } catch (System.Exception e) { throw new System.ArgumentException("Could not instantiate class " + attClass.FullName); } } private static System.Type GetClassForInterface(System.Type attClass) { lock (attClassImplMap) { System.Type clazz = (System.Type) attClassImplMap[attClass]; if (clazz == null) { try { string name = attClass.FullName + "Impl," + attClass.Assembly.FullName; attClassImplMap.Add(attClass, clazz = System.Type.GetType(name, true)); } catch (System.Exception e) { throw new System.ArgumentException("Could not find implementing class for " + attClass.FullName); } } return clazz; } } } } // These two maps must always be in sync!!! // So they are private, final and read-only from the outside (read-only iterators) private SupportClass.GeneralKeyedCollection attributes; private SupportClass.GeneralKeyedCollection attributeImpls; 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.factory = input.factory; } /// An AttributeSource using the supplied {@link AttributeFactory} for creating new {@link Attribute} instances. public AttributeSource(AttributeFactory factory) { this.attributes = new SupportClass.GeneralKeyedCollection(delegate(SupportClass.AttributeImplItem att) { return att.Key; }); this.attributeImpls = new SupportClass.GeneralKeyedCollection(delegate(SupportClass.AttributeImplItem att) { return att.Key; }); 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 System.Collections.Generic.IEnumerable GetAttributeClassesIterator() { foreach (SupportClass.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 System.Collections.Generic.IEnumerable GetAttributeImplsIterator() { if (HasAttributes()) { if (currentState == null) { ComputeCurrentState(); } while (currentState != null) { AttributeImpl att = currentState.attribute; currentState = currentState.next; yield return att; } } } /// a cache that stores all interfaces for known implementation classes for performance (slow reflection) private static readonly System.Collections.Hashtable knownImplClasses = new System.Collections.Hashtable(); // {{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 ; System.Collections.ArrayList foundInterfaces; lock (knownImplClasses) { foundInterfaces = (System.Collections.ArrayList) knownImplClasses[clazz]; if (foundInterfaces == null) { knownImplClasses.Add(clazz, foundInterfaces = new System.Collections.ArrayList()); // 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(Attribute) && typeof(Attribute).IsAssignableFrom(curInterface)) { foundInterfaces.Add(curInterface); } } actClazz = actClazz.BaseType; } while (actClazz != null); } } // add all interfaces of this AttributeImpl to the maps for (System.Collections.IEnumerator it = foundInterfaces.GetEnumerator(); it.MoveNext(); ) { System.Type curInterface = (System.Type) it.Current; // Attribute is a superclass of this interface if (!attributes.ContainsKey(curInterface)) { // invalidate state to force recomputation in captureState() this.currentState = null; attributes.Add(new SupportClass.AttributeImplItem(curInterface, att)); if (!attributeImpls.ContainsKey(clazz)) { attributeImpls.Add(new SupportClass.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 Attribute AddAttribute(System.Type attClass) { if (!attributes.ContainsKey(attClass)) { 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 Attribute 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; } } /// 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; } } private State currentState = null; private void ComputeCurrentState() { currentState = new State(); State c = currentState; System.Collections.Generic.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; } } /// Resets all Attributes in this AttributeSource by calling /// {@link AttributeImpl#Clear()} on each Attribute implementation. /// public virtual void ClearAttributes() { if (HasAttributes()) { if (currentState == null) { ComputeCurrentState(); } for (State state = currentState; 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() { if (!HasAttributes()) { return null; } if (currentState == null) { ComputeCurrentState(); } return (State) this.currentState.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; if (HasAttributes()) { if (currentState == null) { ComputeCurrentState(); } for (State state = currentState; 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 if (this.currentState == null) { this.ComputeCurrentState(); } State thisState = this.currentState; if (other.currentState == null) { other.ComputeCurrentState(); } State otherState = other.currentState; 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 == null) { ComputeCurrentState(); } for (State state = currentState; state != null; state = state.next) { if (state != currentState) 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()) { if (currentState == null) { ComputeCurrentState(); } for (State state = currentState; state != null; state = state.next) { AttributeImpl impl = (AttributeImpl) state.attribute.Clone(); clone.attributeImpls.Add(new SupportClass.AttributeImplItem(impl.GetType(), impl)); } } // now the interfaces foreach (SupportClass.AttributeImplItem att in this.attributes) { clone.attributes.Add(new SupportClass.AttributeImplItem(att.Key, clone.attributeImpls[att.Value.GetType()].Value)); } return clone; } } }