diff options
Diffstat (limited to 'Xamarin.Forms.Core/OrderedDictionary.cs')
-rw-r--r-- | Xamarin.Forms.Core/OrderedDictionary.cs | 451 |
1 files changed, 451 insertions, 0 deletions
diff --git a/Xamarin.Forms.Core/OrderedDictionary.cs b/Xamarin.Forms.Core/OrderedDictionary.cs new file mode 100644 index 00000000..028979c5 --- /dev/null +++ b/Xamarin.Forms.Core/OrderedDictionary.cs @@ -0,0 +1,451 @@ +// +// OrderedDictionary.cs +// +// Author: +// Eric Maupin <me@ermau.com> +// +// 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<TKey, TValue> : IDictionary<TKey, TValue>, IList<KeyValuePair<TKey, TValue>> + { + readonly Dictionary<TKey, TValue> _dict; + readonly List<TKey> _keyOrder; + readonly ICollection<KeyValuePair<TKey, TValue>> _kvpCollection; + readonly ReadOnlyCollection<TKey> _roKeys; + + readonly ReadOnlyValueCollection _roValues; + + public OrderedDictionary() : this(0) + { + } + + public OrderedDictionary(int capacity) : this(capacity, EqualityComparer<TKey>.Default) + { + } + + public OrderedDictionary(IEqualityComparer<TKey> equalityComparer) : this(0, equalityComparer) + { + } + + public OrderedDictionary(int capacity, IEqualityComparer<TKey> equalityComparer) + { + _dict = new Dictionary<TKey, TValue>(capacity, equalityComparer); + _kvpCollection = _dict; + _keyOrder = new List<TKey>(capacity); + _roKeys = new ReadOnlyCollection<TKey>(_keyOrder); + _roValues = new ReadOnlyValueCollection(this); + } + + public OrderedDictionary(ICollection<KeyValuePair<TKey, TValue>> dictionary) : this(dictionary, EqualityComparer<TKey>.Default) + { + } + + public OrderedDictionary(ICollection<KeyValuePair<TKey, TValue>> dictionary, IEqualityComparer<TKey> equalityComparer) : this(dictionary != null ? dictionary.Count : 0, equalityComparer) + { + if (dictionary == null) + throw new ArgumentNullException("dictionary"); + + foreach (KeyValuePair<TKey, TValue> kvp in dictionary) + Add(kvp.Key, kvp.Value); + } + + /// <summary> + /// Gets the equality comparer being used for + /// <typeparam name="TKey" /> + /// . + /// </summary> + public IEqualityComparer<TKey> Comparer + { + get { return _dict.Comparer; } + } + + /// <summary> + /// Gets the value at the specified index. + /// </summary> + /// <param name="index">The index to get the value at.</param> + /// <returns>The value at the specified index.</returns> + /// <exception cref="IndexOutOfRangeException"> + /// <paramref name="index" /> is less than 0 or greater than + /// <see cref="Count" />. + /// </exception> + public TValue this[int index] + { + get { return _dict[_keyOrder[index]]; } + } + + void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item) + { + Add(item.Key, item.Value); + } + + /// <summary> + /// Clears the dictionary. + /// </summary> + public void Clear() + { + _dict.Clear(); + _keyOrder.Clear(); + } + + bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item) + { + return _kvpCollection.Contains(item); + } + + void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, 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) + throw new ArgumentOutOfRangeException("arrayIndex"); + + for (var i = 0; i < _keyOrder.Count; ++i) + { + TKey key = _keyOrder[i]; + array[arrayIndex++] = new KeyValuePair<TKey, TValue>(key, _dict[key]); + } + } + + /// <summary> + /// Gets the number of items in the dictionary. + /// </summary> + public int Count + { + get { return _dict.Count; } + } + + bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly + { + get { return false; } + } + + bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item) + { + return _kvpCollection.Remove(item) && _keyOrder.Remove(item.Key); + } + + /// <summary> + /// Adds the <paramref name="key" /> and <paramref name="value" /> to the dictionary. + /// </summary> + /// <param name="key">The key to associate with the <paramref name="value" />.</param> + /// <param name="value">The value to add.</param> + /// <exception cref="ArgumentNullException"><paramref name="key" /> is <c>null</c>.</exception> + /// <exception cref="ArgumentException"><paramref name="key" /> already exists in the dictionary.</exception> + public void Add(TKey key, TValue value) + { + _dict.Add(key, value); + _keyOrder.Add(key); + } + + /// <summary> + /// Gets whether or not <paramref name="key" /> is in the dictionary. + /// </summary> + /// <param name="key">The key to look for.</param> + /// <returns><c>true</c> if the key was found, <c>false</c> if not.</returns> + /// <exception cref="ArgumentNullException">If <paramref name="key" /> is <c>null</c>.</exception> + public bool ContainsKey(TKey key) + { + return _dict.ContainsKey(key); + } + + /// <summary> + /// Gets or sets the value associated with <paramref name="key" />. + /// </summary> + /// <param name="key">The key to get or set the value for.</param> + /// <returns>The value associated with the key.</returns> + /// <exception cref="KeyNotFoundException"><paramref name="key" /> was not found attempting to get.</exception> + public TValue this[TKey key] + { + get { return _dict[key]; } + set + { + if (!_dict.ContainsKey(key)) + _keyOrder.Add(key); + + _dict[key] = value; + } + } + + /// <summary> + /// Gets a read only collection of keys in the dictionary. + /// </summary> + public ICollection<TKey> Keys + { + get { return _roKeys; } + } + + /// <summary> + /// Removes the key and associated value from the dictionary if found. + /// </summary> + /// <param name="key">The key to remove.</param> + /// <returns><c>true</c> if the key was found, <c>false</c> if not.</returns> + /// <exception cref="ArgumentNullException"><paramref name="key" /> is <c>null</c>.</exception> + public bool Remove(TKey key) + { + return _dict.Remove(key) && _keyOrder.Remove(key); + } + + /// <summary> + /// Attempts to get the <paramref name="value" /> for the <paramref name="key" />. + /// </summary> + /// <param name="key">The key to search for.</param> + /// <param name="value">The value, if found.</param> + /// <returns><c>true</c> if the key was found, <c>false</c> otherwise.</returns> + /// <exception cref="ArgumentNullException">If <paramref name="key" /> is <c>null</c>.</exception> + public bool TryGetValue(TKey key, out TValue value) + { + return _dict.TryGetValue(key, out value); + } + + /// <summary> + /// Gets a read only collection of values in the dictionary. + /// </summary> + public ICollection<TValue> Values + { + get { return _roValues; } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() + { + foreach (TKey key in _keyOrder) + yield return new KeyValuePair<TKey, TValue>(key, this[key]); + } + + int IList<KeyValuePair<TKey, TValue>>.IndexOf(KeyValuePair<TKey, TValue> item) + { + return _keyOrder.IndexOf(item.Key); + } + + void IList<KeyValuePair<TKey, TValue>>.Insert(int index, KeyValuePair<TKey, TValue> item) + { + Insert(index, item.Key, item.Value); + } + + KeyValuePair<TKey, TValue> IList<KeyValuePair<TKey, TValue>>.this[int index] + { + get { return new KeyValuePair<TKey, TValue>(_keyOrder[index], this[index]); } + set + { + _keyOrder[index] = value.Key; + _dict[value.Key] = value.Value; + } + } + + /// <summary> + /// Removes they key and associated value from the dictionary located at <paramref name="index" />. + /// </summary> + /// <param name="index">The index at which to remove an item.</param> + public void RemoveAt(int index) + { + TKey key = _keyOrder[index]; + Remove(key); + } + + /// <summary> + /// Gets whether or not <paramref name="value" /> is in the dictionary. + /// </summary> + /// <param name="value">The value to look for.</param> + /// <returns><c>true</c> if the value was found, <c>false</c> if not.</returns> + public bool ContainsValue(TValue value) + { + return _dict.ContainsValue(value); + } + + /// <summary> + /// Gets the index of <paramref name="key" />. + /// </summary> + /// <param name="key">The key to find the index of.</param> + /// <returns>-1 if the key was not found, the index otherwise.</returns> + /// <exception cref="ArgumentNullException"><paramref name="key" /> is <c>null</c>.</exception> + public int IndexOf(TKey key) + { + if (key == null) + throw new ArgumentNullException("key"); + + return _keyOrder.IndexOf(key); + } + + /// <summary> + /// Gets the index of <paramref name="key" /> starting with <paramref name="startIndex" />. + /// </summary> + /// <param name="key">The key to find the index of.</param> + /// <param name="startIndex">The index to start the search at.</param> + /// <returns>-1 if the key was not found, the index otherwise.</returns> + /// <exception cref="ArgumentNullException"><paramref name="key" /> is <c>null</c>.</exception> + /// <exception cref="ArgumentOutOfRangeException"><paramref name="startIndex" /> is not within the valid range of indexes.</exception> + public int IndexOf(TKey key, int startIndex) + { + if (key == null) + throw new ArgumentNullException("key"); + + return _keyOrder.IndexOf(key, startIndex); + } + + /// <summary> + /// Gets the index of <paramref name="key" /> between the range given by <paramref name="startIndex" /> and + /// <paramref name="count" />. + /// </summary> + /// <param name="key">The key to find the index of.</param> + /// <param name="startIndex">The index to start the search at.</param> + /// <param name="count">How many items to search, including the <paramref name="startIndex" />.</param> + /// <returns>-1 if the key was not found, the index otherwise.</returns> + /// <exception cref="ArgumentNullException"><paramref name="key" /> is <c>null</c>.</exception> + /// <exception cref="ArgumentOutOfRangeException"><paramref name="startIndex" /> is not within the valid range of indexes.</exception> + /// <exception cref="ArgumentOutOfRangeException"> + /// <paramref name="startIndex" /> and <paramref name="count" /> are not a + /// valid range. + /// </exception> + /// <exception cref="ArgumentOutOfRangeException"><paramref name="count" /> is less than 0.</exception> + public int IndexOf(TKey key, int startIndex, int count) + { + if (key == null) + throw new ArgumentNullException("key"); + + return _keyOrder.IndexOf(key, startIndex, count); + } + + /// <summary> + /// Inserts the <paramref name="key" /> and <paramref name="value" /> at the specified index. + /// </summary> + /// <param name="index">The index to insert the key and value at.</param> + /// <param name="key">The key to assicate with the <paramref name="value" />.</param> + /// <param name="value">The value to insert.</param> + /// <exception cref="ArgumentNullException"><paramref name="key" /> is <c>null</c>.</exception> + /// <exception cref="ArgumentOutOfRangeException"> + /// <paramref name="index" /> is less than 0 or greater than + /// <see cref="Count" /> + /// </exception> + 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<TValue> + { + readonly OrderedDictionary<TKey, TValue> _odict; + + public ReadOnlyValueCollection(OrderedDictionary<TKey, TValue> 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<TValue> 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(); + } + } + } +}
\ No newline at end of file |