| using Lucene.Net.Analysis.TokenAttributes; |
| using Lucene.Net.Diagnostics; |
| using Lucene.Net.Support; |
| using System; |
| using System.Collections; |
| using System.Collections.Generic; |
| using System.Runtime.CompilerServices; |
| using System.Text; |
| using FlagsAttribute = Lucene.Net.Analysis.TokenAttributes.FlagsAttribute; |
| using JCG = J2N.Collections.Generic; |
| |
| namespace Lucene.Net.Util |
| { |
| /* |
| * 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. |
| */ |
| |
| /// <summary> |
| /// An <see cref="AttributeSource"/> contains a list of different <see cref="Attribute"/>s, |
| /// and methods to add and get them. There can only be a single instance |
| /// of an attribute in the same <see cref="AttributeSource"/> instance. This is ensured |
| /// by passing in the actual type of the <see cref="IAttribute"/> to |
| /// the <see cref="AddAttribute{T}"/>, 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. |
| /// </summary> |
| public class AttributeSource |
| { |
| /// <summary> |
| /// An <see cref="AttributeFactory"/> creates instances of <see cref="Attribute"/>s. |
| /// </summary> |
| public abstract class AttributeFactory // LUCENENET TODO: API - de-nest |
| { |
| /// <summary> |
| /// returns an <see cref="Attribute"/> for the supplied <see cref="IAttribute"/> interface. |
| /// </summary> |
| public abstract Attribute CreateAttributeInstance<T>() where T : IAttribute; |
| |
| /// <summary> |
| /// This is the default factory that creates <see cref="Attribute"/>s using the |
| /// <see cref="Type"/> of the supplied <see cref="IAttribute"/> interface by removing the <code>I</code> from the prefix. |
| /// </summary> |
| public static readonly AttributeFactory DEFAULT_ATTRIBUTE_FACTORY = new DefaultAttributeFactory(); |
| |
| private sealed class DefaultAttributeFactory : AttributeFactory |
| { |
| // LUCENENET: Using ConditionalWeakTable instead of WeakIdentityMap. A Type IS an |
| // identity for a class, so there is no need for an identity wrapper for it. |
| private static readonly ConditionalWeakTable<Type, WeakReference<Type>> attClassImplMap = |
| new ConditionalWeakTable<Type, WeakReference<Type>>(); |
| private static readonly object attClassImplMapLock = new object(); |
| |
| internal DefaultAttributeFactory() |
| { |
| } |
| |
| public override Attribute CreateAttributeInstance<S>() |
| { |
| try |
| { |
| Type attributeType = GetClassForInterface<S>(); |
| |
| // LUCENENET: Optimize for creating instances of the most common attributes |
| // directly rather than using Activator.CreateInstance() |
| return CreateInstance(attributeType) ?? (Attribute)Activator.CreateInstance(attributeType); |
| } |
| catch (Exception e) |
| { |
| throw new ArgumentException("Could not instantiate implementing class for " + typeof(S).FullName, e); |
| } |
| } |
| |
| // LUCENENET: optimize known creation of built-in types |
| private Attribute CreateInstance(Type attributeType) |
| { |
| if (ReferenceEquals(typeof(CharTermAttribute), attributeType)) |
| return new CharTermAttribute(); |
| if (ReferenceEquals(typeof(FlagsAttribute), attributeType)) |
| return new FlagsAttribute(); |
| if (ReferenceEquals(typeof(OffsetAttribute), attributeType)) |
| return new OffsetAttribute(); |
| if (ReferenceEquals(typeof(PayloadAttribute), attributeType)) |
| return new PayloadAttribute(); |
| if (ReferenceEquals(typeof(PositionIncrementAttribute), attributeType)) |
| return new PositionIncrementAttribute(); |
| if (ReferenceEquals(typeof(PositionLengthAttribute), attributeType)) |
| return new PositionLengthAttribute(); |
| if (ReferenceEquals(typeof(TypeAttribute), attributeType)) |
| return new TypeAttribute(); |
| |
| return null; |
| } |
| |
| internal static Type GetClassForInterface<T>() where T : IAttribute |
| { |
| var attClass = typeof(T); |
| Type clazz; |
| |
| // LUCENENET: If the weakreference is dead, we need to explicitly update its key. |
| // We synchronize on attClassImplMapLock to make the operation atomic. |
| lock (attClassImplMapLock) |
| { |
| if (!attClassImplMap.TryGetValue(attClass, out var @ref) || !@ref.TryGetTarget(out clazz)) |
| { |
| #if FEATURE_CONDITIONALWEAKTABLE_ADDORUPDATE |
| attClassImplMap.AddOrUpdate(attClass, CreateAttributeWeakReference(attClass, out clazz)); |
| #else |
| attClassImplMap.Remove(attClass); |
| attClassImplMap.Add(attClass, CreateAttributeWeakReference(attClass, out clazz)); |
| #endif |
| } |
| } |
| |
| return clazz; |
| } |
| |
| // LUCENENET specific - factored this out so we can reuse |
| private static WeakReference<Type> CreateAttributeWeakReference(Type attributeInterfaceType, out Type clazz) |
| { |
| try |
| { |
| string name = ConvertAttributeInterfaceToClassName(attributeInterfaceType); |
| return new WeakReference<Type>(clazz = attributeInterfaceType.Assembly.GetType(name, true)); |
| } |
| catch (Exception e) |
| { |
| throw new ArgumentException("Could not find implementing class for " + attributeInterfaceType.Name, e); |
| } |
| } |
| |
| private static string ConvertAttributeInterfaceToClassName(Type attributeInterfaceType) |
| { |
| int lastPlus = attributeInterfaceType.FullName.LastIndexOf('+'); |
| if (lastPlus == -1) |
| { |
| return string.Concat( |
| attributeInterfaceType.Namespace, |
| ".", |
| attributeInterfaceType.Name.Substring(1)); |
| } |
| else |
| { |
| return string.Concat( |
| attributeInterfaceType.FullName.Substring(0, lastPlus + 1), |
| attributeInterfaceType.Name.Substring(1)); |
| } |
| } |
| } |
| } |
| |
| /// <summary> |
| /// This class holds the state of an <see cref="AttributeSource"/>. </summary> |
| /// <seealso cref="CaptureState()"/> |
| /// <seealso cref="RestoreState(State)"/> |
| public sealed class State |
| #if FEATURE_CLONEABLE |
| : System.ICloneable |
| #endif |
| { |
| internal Attribute attribute; |
| internal State next; |
| |
| public object Clone() |
| { |
| State clone = new State(); |
| clone.attribute = (Attribute)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 readonly IDictionary<Type, Util.Attribute> attributes; |
| |
| private readonly IDictionary<Type, Util.Attribute> attributeImpls; |
| private readonly State[] currentState; |
| |
| private readonly AttributeFactory factory; |
| |
| /// <summary> |
| /// An <see cref="AttributeSource"/> using the default attribute factory <see cref="AttributeSource.AttributeFactory.DEFAULT_ATTRIBUTE_FACTORY"/>. |
| /// </summary> |
| public AttributeSource() |
| : this(AttributeFactory.DEFAULT_ATTRIBUTE_FACTORY) |
| { |
| } |
| |
| /// <summary> |
| /// An <see cref="AttributeSource"/> that uses the same attributes as the supplied one. |
| /// </summary> |
| public AttributeSource(AttributeSource input) |
| { |
| if (input == null) |
| { |
| throw new ArgumentException("input AttributeSource must not be null"); |
| } |
| this.attributes = input.attributes; |
| this.attributeImpls = input.attributeImpls; |
| this.currentState = input.currentState; |
| this.factory = input.factory; |
| } |
| |
| /// <summary> |
| /// An <see cref="AttributeSource"/> using the supplied <see cref="AttributeFactory"/> for creating new <see cref="IAttribute"/> instances. |
| /// </summary> |
| public AttributeSource(AttributeFactory factory) |
| { |
| this.attributes = new JCG.LinkedDictionary<Type, Util.Attribute>(); |
| this.attributeImpls = new JCG.LinkedDictionary<Type, Util.Attribute>(); |
| this.currentState = new State[1]; |
| this.factory = factory; |
| } |
| |
| /// <summary> |
| /// Returns the used <see cref="AttributeFactory"/>. |
| /// </summary> |
| public AttributeFactory GetAttributeFactory() |
| { |
| return this.factory; |
| } |
| |
| /// <summary> |
| /// Returns a new iterator that iterates the attribute classes |
| /// in the same order they were added in. |
| /// </summary> |
| public IEnumerator<Type> GetAttributeClassesEnumerator() |
| { |
| return attributes.Keys.GetEnumerator(); |
| } |
| |
| /// <summary> |
| /// Returns a new iterator that iterates all unique <see cref="IAttribute"/> implementations. |
| /// This iterator may contain less entries than <see cref="GetAttributeClassesEnumerator()"/>, |
| /// if one instance implements more than one <see cref="IAttribute"/> interface. |
| /// </summary> |
| public IEnumerator<Attribute> GetAttributeImplsEnumerator() |
| { |
| State initState = GetCurrentState(); |
| if (initState != null) |
| { |
| return new IteratorAnonymousInnerClassHelper(initState); |
| } |
| else |
| { |
| return Collections.EmptySet<Attribute>().GetEnumerator(); |
| } |
| } |
| |
| private class IteratorAnonymousInnerClassHelper : IEnumerator<Attribute> |
| { |
| public IteratorAnonymousInnerClassHelper(AttributeSource.State initState) |
| { |
| state = initState; |
| } |
| |
| private Attribute current; |
| private State state; |
| |
| //public virtual void Remove() // LUCENENET specific - not used |
| //{ |
| // throw new NotSupportedException(); |
| //} |
| |
| public void Dispose() |
| { |
| } |
| |
| public bool MoveNext() |
| { |
| if (state == null) |
| { |
| return false; |
| } |
| |
| Attribute att = state.attribute; |
| state = state.next; |
| current = att; |
| return true; |
| } |
| |
| public void Reset() |
| { |
| throw new NotSupportedException(); |
| } |
| |
| public Attribute Current => current; |
| |
| object IEnumerator.Current => current; |
| } |
| |
| /// <summary> |
| /// A cache that stores all interfaces for known implementation classes for performance (slow reflection) </summary> |
| // LUCENENET: Using ConditionalWeakTable instead of WeakIdentityMap. A Type IS an |
| // identity for a class, so there is no need for an identity wrapper for it. |
| private static readonly ConditionalWeakTable<Type, LinkedList<WeakReference<Type>>> knownImplClasses = |
| new ConditionalWeakTable<Type, LinkedList<WeakReference<Type>>>(); |
| |
| internal static LinkedList<WeakReference<Type>> GetAttributeInterfaces(Type clazz) |
| { |
| return knownImplClasses.GetValue(clazz, (key) => |
| { |
| LinkedList<WeakReference<Type>> foundInterfaces = new LinkedList<WeakReference<Type>>(); |
| // find all interfaces that this attribute instance implements |
| // and that extend the Attribute interface |
| Type actClazz = clazz; |
| do |
| { |
| foreach (Type curInterface in actClazz.GetInterfaces()) |
| { |
| if (curInterface != typeof(IAttribute) && typeof(IAttribute).IsAssignableFrom(curInterface)) |
| { |
| foundInterfaces.AddLast(new WeakReference<Type>(curInterface)); |
| } |
| } |
| actClazz = actClazz.BaseType; |
| } while (actClazz != null); |
| |
| return foundInterfaces; |
| }); |
| } |
| |
| /// <summary> |
| /// <b>Expert:</b> Adds a custom <see cref="Attribute"/> instance with one or more <see cref="IAttribute"/> interfaces. |
| /// <para><font color="red"><b>Please note:</b> It is not guaranteed, that <paramref name="att"/> is added to |
| /// the <see cref="AttributeSource"/>, because the provided attributes may already exist. |
| /// You should always retrieve the wanted attributes using <see cref="GetAttribute{T}"/> after adding |
| /// with this method and cast to your <see cref="Type"/>. |
| /// The recommended way to use custom implementations is using an <see cref="AttributeFactory"/>. |
| /// </font></para> |
| /// </summary> |
| public void AddAttributeImpl(Attribute att) |
| { |
| Type clazz = att.GetType(); |
| if (attributeImpls.ContainsKey(clazz)) |
| { |
| return; |
| } |
| |
| LinkedList<WeakReference<Type>> foundInterfaces = GetAttributeInterfaces(clazz); |
| |
| // add all interfaces of this Attribute to the maps |
| foreach (var curInterfaceRef in foundInterfaces) |
| { |
| curInterfaceRef.TryGetTarget(out Type curInterface); |
| if (Debugging.AssertsEnabled) Debugging.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(curInterface, att); |
| if (!attributeImpls.ContainsKey(clazz)) |
| { |
| attributeImpls.Add(clazz, att); |
| } |
| } |
| } |
| } |
| |
| /// <summary> |
| /// The caller must pass in an interface type that extends <see cref="IAttribute"/>. |
| /// This method first checks if an instance of the corresponding class is |
| /// already in this <see cref="AttributeSource"/> and returns it. Otherwise a |
| /// new instance is created, added to this <see cref="AttributeSource"/> and returned. |
| /// </summary> |
| public T AddAttribute<T>() |
| where T : IAttribute |
| { |
| var attClass = typeof(T); |
| // LUCENENET: Eliminated exception and used TryGetValue |
| if (!attributes.TryGetValue(attClass, out var result)) |
| { |
| if (!(attClass.IsInterface && typeof(IAttribute).IsAssignableFrom(attClass))) |
| { |
| throw new ArgumentException("AddAttribute() only accepts an interface that extends IAttribute, but " + attClass.FullName + " does not fulfil this contract."); |
| } |
| |
| result = this.factory.CreateAttributeInstance<T>(); |
| AddAttributeImpl(result); |
| } |
| |
| return (T)(IAttribute)result; |
| } |
| |
| /// <summary> |
| /// Returns <c>true</c>, if this <see cref="AttributeSource"/> has any attributes </summary> |
| public bool HasAttributes => this.attributes.Count > 0; |
| |
| /// <summary> |
| /// The caller must pass in an interface type that extends <see cref="IAttribute"/>. |
| /// Returns <c>true</c>, if this <see cref="AttributeSource"/> contains the corrsponding <see cref="Attribute"/>. |
| /// </summary> |
| public bool HasAttribute<T>() where T : IAttribute |
| { |
| var attClass = typeof(T); |
| return this.attributes.ContainsKey(attClass); |
| } |
| |
| /// <summary> |
| /// The caller must pass in an interface type that extends <see cref="IAttribute"/>. |
| /// Returns the instance of the corresponding <see cref="Attribute"/> contained in this <see cref="AttributeSource"/> |
| /// </summary> |
| /// <exception cref="ArgumentException"> if this <see cref="AttributeSource"/> does not contain the |
| /// <see cref="Attribute"/>. It is recommended to always use <see cref="AddAttribute{T}()"/> even in consumers |
| /// of <see cref="Analysis.TokenStream"/>s, because you cannot know if a specific <see cref="Analysis.TokenStream"/> really uses |
| /// a specific <see cref="Attribute"/>. <see cref="AddAttribute{T}()"/> will automatically make the attribute |
| /// available. If you want to only use the attribute, if it is available (to optimize |
| /// consuming), use <see cref="HasAttribute{T}()"/>. </exception> |
| public virtual T GetAttribute<T>() where T : IAttribute |
| { |
| var attClass = typeof(T); |
| if (!attributes.TryGetValue(attClass, out var result)) |
| { |
| throw new ArgumentException($"this AttributeSource does not have the attribute '{attClass.Name}'."); |
| } |
| return (T)(IAttribute)result; |
| } |
| |
| private State GetCurrentState() |
| { |
| State s = currentState[0]; |
| if (s != null || !HasAttributes) |
| { |
| return s; |
| } |
| var c = s = currentState[0] = new State(); |
| using var it = attributeImpls.Values.GetEnumerator(); |
| it.MoveNext(); |
| c.attribute = it.Current; |
| while (it.MoveNext()) |
| { |
| c.next = new State(); |
| c = c.next; |
| c.attribute = it.Current; |
| } |
| return s; |
| } |
| |
| /// <summary> |
| /// Resets all <see cref="Attribute"/>s in this <see cref="AttributeSource"/> by calling |
| /// <see cref="Attribute.Clear()"/> on each <see cref="IAttribute"/> implementation. |
| /// </summary> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| public void ClearAttributes() |
| { |
| for (State state = GetCurrentState(); state != null; state = state.next) |
| { |
| state.attribute.Clear(); |
| } |
| } |
| |
| /// <summary> |
| /// Captures the state of all <see cref="Attribute"/>s. The return value can be passed to |
| /// <see cref="RestoreState(State)"/> to restore the state of this or another <see cref="AttributeSource"/>. |
| /// </summary> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| public virtual State CaptureState() |
| { |
| State state = this.GetCurrentState(); |
| return (state == null) ? null : (State)state.Clone(); |
| } |
| |
| /// <summary> |
| /// 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 <see cref="AttributeSource"/> containing a <see cref="Analysis.TokenAttributes.ICharTermAttribute"/> into a <see cref="AttributeSource"/> using |
| /// a <see cref="Analysis.Token"/> instance as implementation). |
| /// <para/> |
| /// 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 <see cref="Analysis.TokenAttributes.IOffsetAttribute"/>, but this state doesn't, then |
| /// the value of the <see cref="Analysis.TokenAttributes.IOffsetAttribute"/> remains unchanged. It might be desirable to |
| /// reset its value to the default, in which case the caller should first |
| /// call <see cref="AttributeSource.ClearAttributes()"/> (<c>TokenStream.ClearAttributes()</c> on the targetStream. |
| /// </summary> |
| public void RestoreState(State state) |
| { |
| if (state == null) |
| { |
| return; |
| } |
| |
| do |
| { |
| if (!attributeImpls.ContainsKey(state.attribute.GetType())) |
| { |
| throw new ArgumentException("State contains Attribute of type " + state.attribute.GetType().Name + " that is not in in this AttributeSource"); |
| } |
| state.attribute.CopyTo(attributeImpls[state.attribute.GetType()]); |
| 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(object obj) |
| { |
| if (obj == this) |
| { |
| return true; |
| } |
| |
| if (obj is AttributeSource other) |
| { |
| 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; |
| } |
| } |
| |
| /// <summary> |
| /// This method returns the current attribute values as a string in the following format |
| /// by calling the <see cref="ReflectWith(IAttributeReflector)"/> method: |
| /// |
| /// <list type="bullet"> |
| /// <item><term>if <paramref name="prependAttClass"/>=true:</term> <description> <c>"AttributeClass.Key=value,AttributeClass.Key=value"</c> </description></item> |
| /// <item><term>if <paramref name="prependAttClass"/>=false:</term> <description> <c>"key=value,key=value"</c> </description></item> |
| /// </list> |
| /// </summary> |
| /// <seealso cref="ReflectWith(IAttributeReflector)"/> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| public string ReflectAsString(bool prependAttClass) |
| { |
| StringBuilder buffer = new StringBuilder(); |
| ReflectWith(new AttributeReflectorAnonymousInnerClassHelper(prependAttClass, buffer)); |
| return buffer.ToString(); |
| } |
| |
| private class AttributeReflectorAnonymousInnerClassHelper : IAttributeReflector |
| { |
| private readonly bool prependAttClass; |
| private readonly StringBuilder buffer; |
| |
| public AttributeReflectorAnonymousInnerClassHelper(bool prependAttClass, StringBuilder buffer) |
| { |
| this.prependAttClass = prependAttClass; |
| this.buffer = buffer; |
| } |
| |
| public void Reflect<T>(string key, object value) |
| where T : IAttribute |
| { |
| Reflect(typeof(T), key, value); |
| } |
| |
| public void Reflect(Type attClass, string key, object value) |
| { |
| if (buffer.Length > 0) |
| { |
| buffer.Append(','); |
| } |
| if (prependAttClass) |
| { |
| buffer.Append(attClass.Name).Append('#'); |
| } |
| buffer.Append(key).Append('='); |
| if (value is null) |
| buffer.Append("null"); |
| else |
| buffer.Append(value); |
| } |
| } |
| |
| /// <summary> |
| /// This method is for introspection of attributes, it should simply |
| /// add the key/values this <see cref="AttributeSource"/> holds to the given <see cref="IAttributeReflector"/>. |
| /// |
| /// <para>This method iterates over all <see cref="IAttribute"/> implementations and calls the |
| /// corresponding <see cref="Attribute.ReflectWith(IAttributeReflector)"/> method.</para> |
| /// </summary> |
| /// <seealso cref="Attribute.ReflectWith(IAttributeReflector)"/> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| public void ReflectWith(IAttributeReflector reflector) |
| { |
| for (State state = GetCurrentState(); state != null; state = state.next) |
| { |
| state.attribute.ReflectWith(reflector); |
| } |
| } |
| |
| /// <summary> |
| /// Performs a clone of all <see cref="Attribute"/> instances returned in a new |
| /// <see cref="AttributeSource"/> instance. This method can be used to e.g. create another <see cref="Analysis.TokenStream"/> |
| /// with exactly the same attributes (using <see cref="AttributeSource(AttributeSource)"/>). |
| /// You can also use it as a (non-performant) replacement for <see cref="CaptureState()"/>, if you need to look |
| /// into / modify the captured state. |
| /// </summary> |
| public AttributeSource CloneAttributes() |
| { |
| AttributeSource clone = new AttributeSource(this.factory); |
| |
| if (HasAttributes) |
| { |
| // first clone the impls |
| for (State state = GetCurrentState(); state != null; state = state.next) |
| { |
| //clone.AttributeImpls[state.attribute.GetType()] = state.attribute.Clone(); |
| var impl = (Attribute)state.attribute.Clone(); |
| |
| if (!clone.attributeImpls.ContainsKey(impl.GetType())) |
| { |
| clone.attributeImpls.Add(impl.GetType(), impl); |
| } |
| } |
| |
| // now the interfaces |
| foreach (var entry in this.attributes) |
| { |
| clone.attributes.Add(entry.Key, clone.attributeImpls[entry.Value.GetType()]); |
| } |
| } |
| |
| return clone; |
| } |
| |
| /// <summary> |
| /// Copies the contents of this <see cref="AttributeSource"/> to the given target <see cref="AttributeSource"/>. |
| /// The given instance has to provide all <see cref="IAttribute"/>s this instance contains. |
| /// The actual attribute implementations must be identical in both <see cref="AttributeSource"/> instances; |
| /// ideally both <see cref="AttributeSource"/> instances should use the same <see cref="AttributeFactory"/>. |
| /// You can use this method as a replacement for <see cref="RestoreState(State)"/>, if you use |
| /// <see cref="CloneAttributes()"/> instead of <see cref="CaptureState()"/>. |
| /// </summary> |
| public void CopyTo(AttributeSource target) |
| { |
| for (State state = GetCurrentState(); state != null; state = state.next) |
| { |
| Attribute targetImpl = target.attributeImpls[state.attribute.GetType()]; |
| if (targetImpl == null) |
| { |
| throw new ArgumentException("this AttributeSource contains Attribute of type " + state.attribute.GetType().Name + " that is not in the target"); |
| } |
| state.attribute.CopyTo(targetImpl); |
| } |
| } |
| |
| /// <summary> |
| /// Returns a string consisting of the class's simple name, the hex representation of the identity hash code, |
| /// and the current reflection of all attributes. </summary> |
| /// <seealso cref="ReflectAsString(bool)"/> |
| public override string ToString() |
| { |
| return this.GetType().Name + '@' + RuntimeHelpers.GetHashCode(this).ToString("x") + " " + ReflectAsString(false); |
| } |
| } |
| } |