373 lines
11 KiB
C#
373 lines
11 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
|
|
// ReSharper disable once CheckNamespace
|
|
namespace GameDevWare.Serialization
|
|
{
|
|
[Serializable, DebuggerDisplay("IndexedDictionary, Count: {Count}")]
|
|
public class IndexedDictionary<KeyT, ValueT> : IDictionary<KeyT, ValueT>, IDictionary
|
|
{
|
|
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
|
|
private readonly Dictionary<KeyT, ValueT> dictionary;
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
|
private readonly List<KeyT> keys;
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never), NonSerialized]
|
|
private ReadOnlyCollection<KeyT> keysReadOnly;
|
|
|
|
/// <inheritdoc />
|
|
public int Count { get { return this.dictionary.Count; } }
|
|
|
|
/// <inheritdoc />
|
|
public ValueT this[KeyT key]
|
|
{
|
|
get { return this.dictionary[key]; }
|
|
set
|
|
{
|
|
if (this.dictionary.ContainsKey(key) == false)
|
|
this.keys.Add(key);
|
|
this.dictionary[key] = value;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
|
object IDictionary.this[object key]
|
|
{
|
|
get
|
|
{
|
|
var value = default(ValueT);
|
|
return this.TryGetValue((KeyT)key, out value) ? value : default(object);
|
|
}
|
|
set { this[(KeyT)key] = (ValueT)value; }
|
|
}
|
|
/// <inheritdoc />
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
|
object ICollection.SyncRoot { get { return this.dictionary; } }
|
|
/// <inheritdoc />
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
|
bool ICollection.IsSynchronized { get { return false; } }
|
|
/// <inheritdoc />
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
|
public ReadOnlyCollection<KeyT> Keys { get { return this.keysReadOnly ?? (this.keysReadOnly = new ReadOnlyCollection<KeyT>(this.keys)); } }
|
|
/// <inheritdoc />
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
|
public ReadOnlyCollection<ValueT> Values { get { return this.GetValues(); } }
|
|
/// <inheritdoc />
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
|
ICollection IDictionary.Values { get { return this.GetValues(); } }
|
|
/// <inheritdoc />
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
|
ICollection IDictionary.Keys { get { return this.keys; } }
|
|
/// <inheritdoc />
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
|
bool IDictionary.IsReadOnly { get { return false; } }
|
|
/// <inheritdoc />
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
|
bool IDictionary.IsFixedSize { get { return false; } }
|
|
/// <inheritdoc />
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
|
bool ICollection<KeyValuePair<KeyT, ValueT>>.IsReadOnly { get { return false; } }
|
|
/// <inheritdoc />
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
|
ICollection<KeyT> IDictionary<KeyT, ValueT>.Keys { get { return this.keysReadOnly; } }
|
|
/// <inheritdoc />
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
|
ICollection<ValueT> IDictionary<KeyT, ValueT>.Values { get { return this.GetValues(); } }
|
|
|
|
public IndexedDictionary()
|
|
{
|
|
this.dictionary = new Dictionary<KeyT, ValueT>();
|
|
this.keys = new List<KeyT>();
|
|
this.keysReadOnly = new ReadOnlyCollection<KeyT>(this.keys);
|
|
}
|
|
public IndexedDictionary(int count)
|
|
{
|
|
if (count < 0) throw new ArgumentOutOfRangeException("count");
|
|
|
|
if (count == 0) count = 30;
|
|
|
|
this.dictionary = new Dictionary<KeyT, ValueT>(count);
|
|
this.keys = new List<KeyT>(count);
|
|
this.keysReadOnly = new ReadOnlyCollection<KeyT>(this.keys);
|
|
}
|
|
public IndexedDictionary(IDictionary<KeyT, ValueT> dictionary)
|
|
{
|
|
if (dictionary == null) throw new ArgumentNullException("dictionary");
|
|
|
|
this.dictionary = new Dictionary<KeyT, ValueT>(dictionary);
|
|
this.keys = new List<KeyT>(dictionary.Keys);
|
|
this.keysReadOnly = new ReadOnlyCollection<KeyT>(this.keys);
|
|
}
|
|
public IndexedDictionary(IEnumerable<KeyValuePair<KeyT, ValueT>> pairs)
|
|
{
|
|
if (pairs == null) throw new ArgumentNullException("pairs");
|
|
|
|
this.dictionary = new Dictionary<KeyT, ValueT>();
|
|
this.keys = new List<KeyT>();
|
|
this.keysReadOnly = new ReadOnlyCollection<KeyT>(this.keys);
|
|
|
|
foreach (var pair in pairs)
|
|
this.Add(pair.Key, pair.Value);
|
|
}
|
|
public IndexedDictionary(IDictionary<KeyT, ValueT> dictionary, ICollection<KeyT> keys)
|
|
{
|
|
if (dictionary == null) throw new ArgumentNullException("dictionary");
|
|
if (keys == null) throw new ArgumentNullException("keys");
|
|
|
|
|
|
this.dictionary = new Dictionary<KeyT, ValueT>(dictionary);
|
|
this.keys = new List<KeyT>(keys);
|
|
this.keysReadOnly = new ReadOnlyCollection<KeyT>(this.keys);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void Add(KeyT key, ValueT value)
|
|
{
|
|
this.dictionary.Add(key, value);
|
|
this.keys.Add(key);
|
|
}
|
|
/// <inheritdoc />
|
|
public void Add(IndexedDictionary<KeyT, ValueT> other)
|
|
{
|
|
if (other == null) throw new ArgumentNullException("other");
|
|
|
|
if (this.Count == 0)
|
|
{
|
|
this.keys.AddRange(other.keys);
|
|
foreach (var kv in other.dictionary)
|
|
this.dictionary.Add(kv.Key, kv.Value);
|
|
}
|
|
else
|
|
{
|
|
foreach (var kv in other.dictionary)
|
|
{
|
|
this.dictionary.Add(kv.Key, kv.Value);
|
|
this.keys.Add(kv.Key);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void Insert(int index, KeyT key, ValueT value)
|
|
{
|
|
// Dictionary operation first, so exception thrown if key already exists.
|
|
this.dictionary.Add(key, value);
|
|
this.keys.Insert(index, key);
|
|
}
|
|
/// <inheritdoc />
|
|
public bool ContainsKey(KeyT key)
|
|
{
|
|
return this.dictionary.ContainsKey(key);
|
|
}
|
|
/// <inheritdoc />
|
|
public bool ContainsKey(KeyT key, IEqualityComparer<KeyT> keyComparer)
|
|
{
|
|
foreach (var k in this.keys)
|
|
if (keyComparer.Equals(k, key))
|
|
return true;
|
|
return false;
|
|
}
|
|
/// <inheritdoc />
|
|
public bool ContainsValue(ValueT value)
|
|
{
|
|
foreach (var kv in this.dictionary)
|
|
if (Equals(value, kv.Value))
|
|
return true;
|
|
return false;
|
|
}
|
|
/// <inheritdoc />
|
|
public bool ContainsValue(ValueT value, IEqualityComparer comparer)
|
|
{
|
|
if (comparer == null) throw new ArgumentNullException("comparer");
|
|
|
|
foreach (var kv in this.dictionary)
|
|
if (comparer.Equals(value, kv.Value))
|
|
return true;
|
|
return false;
|
|
}
|
|
/// <inheritdoc />
|
|
public bool Remove(KeyT key)
|
|
{
|
|
var wasInDictionary = this.dictionary.Remove(key);
|
|
this.keys.Remove(key);
|
|
|
|
return wasInDictionary;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool TryGetValue(KeyT key, out ValueT value)
|
|
{
|
|
return this.dictionary.TryGetValue(key, out value);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public int IndexOf(KeyT key)
|
|
{
|
|
return this.keys.IndexOf(key);
|
|
}
|
|
/// <inheritdoc />
|
|
public void RemoveAt(int index)
|
|
{
|
|
if (index >= this.Count || index < 0) throw new ArgumentOutOfRangeException("index");
|
|
|
|
var key = this.keys[index];
|
|
this.dictionary.Remove(key);
|
|
this.keys.RemoveAt(index);
|
|
}
|
|
public void SortKeys(IComparer<KeyT> comparer)
|
|
{
|
|
if (comparer == null) throw new ArgumentNullException("comparer");
|
|
|
|
this.keys.Sort(comparer);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void Clear()
|
|
{
|
|
this.dictionary.Clear();
|
|
this.keys.Clear();
|
|
}
|
|
|
|
private ReadOnlyCollection<ValueT> GetValues()
|
|
{
|
|
var values = new ValueT[this.Count];
|
|
var index = 0;
|
|
foreach (var key in this.keys)
|
|
values[index++] = this.dictionary[key];
|
|
return new ReadOnlyCollection<ValueT>(values);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
bool IDictionary.Contains(object key)
|
|
{
|
|
return this.ContainsKey((KeyT)key);
|
|
}
|
|
/// <inheritdoc />
|
|
void IDictionary.Add(object key, object value)
|
|
{
|
|
this.Add((KeyT)key, (ValueT)value);
|
|
}
|
|
/// <inheritdoc />
|
|
IDictionaryEnumerator IDictionary.GetEnumerator()
|
|
{
|
|
return this.GetEnumerator();
|
|
}
|
|
/// <inheritdoc />
|
|
void IDictionary.Remove(object key)
|
|
{
|
|
this.Remove((KeyT)key);
|
|
}
|
|
/// <inheritdoc />
|
|
void ICollection.CopyTo(Array array, int index)
|
|
{
|
|
if (array == null) throw new ArgumentNullException("array");
|
|
if (index >= array.Length) throw new ArgumentOutOfRangeException("index");
|
|
if (index + this.Count > array.Length) throw new ArgumentOutOfRangeException("index");
|
|
|
|
var end = index + this.Count;
|
|
for (var i = 0; i < end; i++)
|
|
array.SetValue(new DictionaryEntry(this.keys[i], this.dictionary[this.keys[i]]), index + i);
|
|
}
|
|
/// <inheritdoc />
|
|
void ICollection<KeyValuePair<KeyT, ValueT>>.Add(KeyValuePair<KeyT, ValueT> item)
|
|
{
|
|
this.Add(item.Key, item.Value);
|
|
}
|
|
/// <inheritdoc />
|
|
bool ICollection<KeyValuePair<KeyT, ValueT>>.Contains(KeyValuePair<KeyT, ValueT> item)
|
|
{
|
|
var value = default(ValueT);
|
|
return this.dictionary.TryGetValue(item.Key, out value) && Equals(value, item.Value);
|
|
}
|
|
/// <inheritdoc />
|
|
void ICollection<KeyValuePair<KeyT, ValueT>>.CopyTo(KeyValuePair<KeyT, ValueT>[] array, int arrayIndex)
|
|
{
|
|
foreach (var pair in this)
|
|
{
|
|
array[arrayIndex] = pair;
|
|
arrayIndex++;
|
|
}
|
|
|
|
}
|
|
/// <inheritdoc />
|
|
bool ICollection<KeyValuePair<KeyT, ValueT>>.Remove(KeyValuePair<KeyT, ValueT> item)
|
|
{
|
|
if (!this.Contains(item))
|
|
return false;
|
|
|
|
return this.Remove(item.Key);
|
|
}
|
|
/// <inheritdoc />
|
|
IEnumerator<KeyValuePair<KeyT, ValueT>> IEnumerable<KeyValuePair<KeyT, ValueT>>.GetEnumerator()
|
|
{
|
|
return this.GetEnumerator();
|
|
}
|
|
/// <inheritdoc />
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return this.GetEnumerator();
|
|
}
|
|
|
|
public Enumerator GetEnumerator()
|
|
{
|
|
return new Enumerator(this);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override string ToString()
|
|
{
|
|
return "Count: " + this.Count.ToString();
|
|
}
|
|
|
|
public struct Enumerator : IEnumerator<KeyValuePair<KeyT, ValueT>>, IDictionaryEnumerator
|
|
{
|
|
private List<KeyT>.Enumerator innerEnumerator;
|
|
private readonly IndexedDictionary<KeyT, ValueT> owner;
|
|
private KeyValuePair<KeyT, ValueT> current;
|
|
|
|
public Enumerator(IndexedDictionary<KeyT, ValueT> owner)
|
|
{
|
|
this.owner = owner;
|
|
this.innerEnumerator = owner.keys.GetEnumerator();
|
|
this.current = new KeyValuePair<KeyT, ValueT>();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public object Key { get { return this.current.Key; } }
|
|
/// <inheritdoc />
|
|
public object Value { get { return this.current.Value; } }
|
|
/// <inheritdoc />
|
|
public DictionaryEntry Entry { get { return new DictionaryEntry(this.current.Key, this.current.Value); } }
|
|
/// <inheritdoc />
|
|
public KeyValuePair<KeyT, ValueT> Current { get { return this.current; } }
|
|
/// <inheritdoc />
|
|
object IEnumerator.Current { get { return this.Entry; } }
|
|
|
|
/// <inheritdoc />
|
|
public bool MoveNext()
|
|
{
|
|
if (!this.innerEnumerator.MoveNext())
|
|
return false;
|
|
|
|
var key = this.innerEnumerator.Current;
|
|
this.current = new KeyValuePair<KeyT, ValueT>(key, this.owner.dictionary[key]);
|
|
return true;
|
|
}
|
|
/// <inheritdoc />
|
|
public void Reset()
|
|
{
|
|
this.innerEnumerator = this.owner.keys.GetEnumerator();
|
|
}
|
|
/// <inheritdoc />
|
|
public void Dispose()
|
|
{
|
|
this.innerEnumerator.Dispose();
|
|
}
|
|
}
|
|
}
|
|
}
|