// // OrderedDictionary.cs // // Author: // Eric Maupin // // Copyright (c) 2009 Eric Maupin (http://www.ermau.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using Xamarin.Forms; namespace Cadenza.Collections { internal sealed class OrderedDictionary : IDictionary, IList> { readonly Dictionary _dict; readonly List _keyOrder; readonly ICollection> _kvpCollection; readonly ReadOnlyCollection _roKeys; readonly ReadOnlyValueCollection _roValues; public OrderedDictionary() : this(0) { } public OrderedDictionary(int capacity) : this(capacity, EqualityComparer.Default) { } public OrderedDictionary(IEqualityComparer equalityComparer) : this(0, equalityComparer) { } public OrderedDictionary(int capacity, IEqualityComparer equalityComparer) { _dict = new Dictionary(capacity, equalityComparer); _kvpCollection = _dict; _keyOrder = new List(capacity); _roKeys = new ReadOnlyCollection(_keyOrder); _roValues = new ReadOnlyValueCollection(this); } public OrderedDictionary(ICollection> dictionary) : this(dictionary, EqualityComparer.Default) { } public OrderedDictionary(ICollection> dictionary, IEqualityComparer equalityComparer) : this(dictionary != null ? dictionary.Count : 0, equalityComparer) { if (dictionary == null) throw new ArgumentNullException("dictionary"); foreach (KeyValuePair kvp in dictionary) Add(kvp.Key, kvp.Value); } /// /// Gets the equality comparer being used for /// /// . /// public IEqualityComparer Comparer { get { return _dict.Comparer; } } /// /// Gets the value at the specified index. /// /// The index to get the value at. /// The value at the specified index. /// /// is less than 0 or greater than /// . /// public TValue this[int index] { get { return _dict[_keyOrder[index]]; } } void ICollection>.Add(KeyValuePair item) { Add(item.Key, item.Value); } /// /// Clears the dictionary. /// public void Clear() { _dict.Clear(); _keyOrder.Clear(); } bool ICollection>.Contains(KeyValuePair item) { return _kvpCollection.Contains(item); } void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) { if (array == null) throw new ArgumentNullException("array"); if (Count > array.Length - arrayIndex) throw new ArgumentException("Not enough space in array to copy"); if (arrayIndex < 0) throw new ArgumentOutOfRangeException("arrayIndex"); for (var i = 0; i < _keyOrder.Count; ++i) { TKey key = _keyOrder[i]; array[arrayIndex++] = new KeyValuePair(key, _dict[key]); } } /// /// Gets the number of items in the dictionary. /// public int Count { get { return _dict.Count; } } bool ICollection>.IsReadOnly { get { return false; } } bool ICollection>.Remove(KeyValuePair item) { return _kvpCollection.Remove(item) && _keyOrder.Remove(item.Key); } /// /// Adds the and to the dictionary. /// /// The key to associate with the . /// The value to add. /// is null. /// already exists in the dictionary. public void Add(TKey key, TValue value) { _dict.Add(key, value); _keyOrder.Add(key); } /// /// Gets whether or not is in the dictionary. /// /// The key to look for. /// true if the key was found, false if not. /// If is null. public bool ContainsKey(TKey key) { return _dict.ContainsKey(key); } /// /// Gets or sets the value associated with . /// /// The key to get or set the value for. /// The value associated with the key. /// was not found attempting to get. public TValue this[TKey key] { get { return _dict[key]; } set { if (!_dict.ContainsKey(key)) _keyOrder.Add(key); _dict[key] = value; } } /// /// Gets a read only collection of keys in the dictionary. /// public ICollection Keys { get { return _roKeys; } } /// /// Removes the key and associated value from the dictionary if found. /// /// The key to remove. /// true if the key was found, false if not. /// is null. public bool Remove(TKey key) { return _dict.Remove(key) && _keyOrder.Remove(key); } /// /// Attempts to get the for the . /// /// The key to search for. /// The value, if found. /// true if the key was found, false otherwise. /// If is null. public bool TryGetValue(TKey key, out TValue value) { return _dict.TryGetValue(key, out value); } /// /// Gets a read only collection of values in the dictionary. /// public ICollection Values { get { return _roValues; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public IEnumerator> GetEnumerator() { foreach (TKey key in _keyOrder) yield return new KeyValuePair(key, this[key]); } int IList>.IndexOf(KeyValuePair item) { return _keyOrder.IndexOf(item.Key); } void IList>.Insert(int index, KeyValuePair item) { Insert(index, item.Key, item.Value); } KeyValuePair IList>.this[int index] { get { return new KeyValuePair(_keyOrder[index], this[index]); } set { _keyOrder[index] = value.Key; _dict[value.Key] = value.Value; } } /// /// Removes they key and associated value from the dictionary located at . /// /// The index at which to remove an item. public void RemoveAt(int index) { TKey key = _keyOrder[index]; Remove(key); } /// /// Gets whether or not is in the dictionary. /// /// The value to look for. /// true if the value was found, false if not. public bool ContainsValue(TValue value) { return _dict.ContainsValue(value); } /// /// Gets the index of . /// /// The key to find the index of. /// -1 if the key was not found, the index otherwise. /// is null. public int IndexOf(TKey key) { if (key == null) throw new ArgumentNullException("key"); return _keyOrder.IndexOf(key); } /// /// Gets the index of starting with . /// /// The key to find the index of. /// The index to start the search at. /// -1 if the key was not found, the index otherwise. /// is null. /// is not within the valid range of indexes. public int IndexOf(TKey key, int startIndex) { if (key == null) throw new ArgumentNullException("key"); return _keyOrder.IndexOf(key, startIndex); } /// /// Gets the index of between the range given by and /// . /// /// The key to find the index of. /// The index to start the search at. /// How many items to search, including the . /// -1 if the key was not found, the index otherwise. /// is null. /// is not within the valid range of indexes. /// /// and are not a /// valid range. /// /// is less than 0. public int IndexOf(TKey key, int startIndex, int count) { if (key == null) throw new ArgumentNullException("key"); return _keyOrder.IndexOf(key, startIndex, count); } /// /// Inserts the and at the specified index. /// /// The index to insert the key and value at. /// The key to assicate with the . /// The value to insert. /// is null. /// /// is less than 0 or greater than /// /// public void Insert(int index, TKey key, TValue value) { _keyOrder.Insert(index, key); _dict.Add(key, value); } public void Move(int oldIndex, int newIndex) { TKey key = _keyOrder[oldIndex]; _keyOrder.RemoveAt(oldIndex); _keyOrder.Insert(newIndex, key); } class ReadOnlyValueCollection : IList { readonly OrderedDictionary _odict; public ReadOnlyValueCollection(OrderedDictionary dict) { _odict = dict; } public void Add(TValue item) { throw new NotSupportedException(); } public void Clear() { throw new NotSupportedException(); } public bool Contains(TValue item) { return _odict.ContainsValue(item); } public void CopyTo(TValue[] array, int arrayIndex) { if (array == null) throw new ArgumentNullException("array"); if (Count > array.Length - arrayIndex) throw new ArgumentException("Not enough space in array to copy"); if (arrayIndex < 0 || arrayIndex > array.Length) throw new ArgumentOutOfRangeException("arrayIndex"); for (var i = 0; i < _odict.Count; ++i) array[arrayIndex++] = _odict[i]; } public int Count { get { return _odict.Count; } } public bool IsReadOnly { get { return true; } } public bool Remove(TValue item) { throw new NotSupportedException(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public IEnumerator GetEnumerator() { for (var i = 0; i < _odict._keyOrder.Count; ++i) yield return _odict[i]; } public int IndexOf(TValue item) { return _odict._dict.Values.IndexOf(item); } public void Insert(int index, TValue item) { throw new NotSupportedException(); } public TValue this[int index] { get { return _odict[index]; } set { throw new NotSupportedException(); } } public void RemoveAt(int index) { throw new NotSupportedException(); } } } }