using System; using System.Collections.Generic; using JD.Collections.Details; using JD.Collections.Extensions; using JD.Collections.Interfaces; using JD.Collections.ObjectModel; using JD.Kernel.StaticHelpers; namespace JD.Collections { /// /// A KeyedCollection that relies on a HashSet and has no inner list. /// [Serializable] public abstract class KeyedCollection : CollectionBase, IKeyedCollection { #region Variables private IVariableSizeHash m_values; #endregion #region Constructor protected KeyedCollection() { m_values = new ShortHash(new EqualityComparer(this, null)); } protected KeyedCollection(IEnumerable collection) { m_values = new ShortHash(new EqualityComparer(this, null)); if (collection != null) { AddRange(collection); } } protected KeyedCollection(IEqualityComparer comparer) { m_values = new ShortHash(new EqualityComparer(this, comparer)); } #endregion #region Methods /// /// Gets the key associtated with the item. /// protected abstract TKey GetKeyForItem(TValue value); /// /// Returns the value if found in the collection. /// public bool TryGetValue(TKey key, out TValue result) { return m_values.TryGetValue(new KeyLookup(key, (EqualityComparer)m_values.Comparer), out result); } public TValue this[TKey key] { get { TValue result; Throw.InvalidArgumentIf(!TryGetValue(key, out result), "Key is not present in dictionary", "key"); return result; } } public override bool Contains(TValue item) { return m_values.Contains(item); } /// /// Same as ContainsKey, just there to be consistent with .net KeyedCollection /// public bool Contains(TKey key) { return ContainsKey(key); } public bool ContainsKey(TKey key) { TValue dummy; return TryGetValue(key, out dummy); } public bool Remove(TKey key) { TValue ignored; return Remove(key, out ignored); } public bool Remove(TKey key, out TValue removedValue) { return m_values.Remove(new KeyLookup(key, (EqualityComparer)m_values.Comparer), out removedValue); } public void AddRange(IEnumerable enumerable) { if (enumerable is ICollection collection) { m_values.Capacity = collection.Count + m_values.Count; } enumerable.ForEach(Add); } public void TrimExcess() { m_values.TrimExcess(); } public bool Replace(TValue item) { return m_values.Add(item, CollisionBehavior.Replace, ref m_values); } public bool TryAdd(TValue item) { return m_values.Add(item, CollisionBehavior.Replace, ref m_values); } public override void Add(TValue item) { m_values.Add(item, CollisionBehavior.Throw, ref m_values); } public override void Clear() { m_values.Clear(); } public override bool Remove(TValue item) { return m_values.Remove(item); } public override int Count { get { return m_values.Count; } } public override IEnumerator GetEnumerator() { return m_values.GetEnumerator(); } /// /// Creates a concrete collection from a converter. /// /// /// public static KeyedCollection Create(Converter getKeyForItem) { return new ConcreteKeyedCollection(getKeyForItem); } /// /// Enumerates the content of the hash but does not garantee to return elements added after the enumeration started. Garantees to not throw or not cause any threading issue. /// public IEnumerable ThreadSafeEnumerate { get { return m_values.ThreadSafeEnumerate; } } #endregion #region Nested [Serializable] private class EqualityComparer : IEqualityComparer { private readonly KeyedCollection m_owner; private readonly IEqualityComparer m_keyComparer; public EqualityComparer(KeyedCollection owner, IEqualityComparer keyComparer) { m_owner = owner; m_keyComparer = keyComparer ?? EqualityComparer.Default; } public bool Equals(TValue x, TValue y) { return KeyEquals(m_owner.GetKeyForItem(x), m_owner.GetKeyForItem(y)); } public int GetHashCode(TValue obj) { return GetHashCode(m_owner.GetKeyForItem(obj)); } public int GetHashCode(TKey key) { return Equals(key, null) ? 0 : m_keyComparer.GetHashCode(key); } public bool KeyEquals(TKey a, TKey b) { return m_keyComparer.Equals(a, b); } public TKey GetKeyForItem(TValue value) { return m_owner.GetKeyForItem(value); } } private class KeyLookup : ILookup { private readonly TKey m_key; private readonly EqualityComparer m_comparer; public KeyLookup(TKey key, EqualityComparer comparer) { m_key = key; m_comparer = comparer; } public bool Equals(TValue other) { return m_comparer.KeyEquals(m_comparer.GetKeyForItem(other), m_key); } public int HashCode { get { return m_comparer.GetHashCode(m_key); } } } private class ConcreteKeyedCollection : KeyedCollection { private readonly Converter m_getKeyForItem; public ConcreteKeyedCollection(Converter getKeyForItem) { Throw.IfNull(getKeyForItem, "getKeyForItem"); m_getKeyForItem = getKeyForItem; } protected override TKey GetKeyForItem(TValue item) { return m_getKeyForItem(item); } } #endregion } }