452 строки
14 KiB
C#
452 строки
14 KiB
C#
//
|
|
// 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;
|
|
using Xamarin.Forms.Internals;
|
|
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
} |