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
}
}