//http://visualizer.codeplex.com/license
//Microsoft Public License (Ms-PL)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
namespace Redwerb.BizArk.Core.Collections
{
/// <summary>
/// Represents a generic collection of key/value pairs. The enumerator returns the values in the order assigned.
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
public class HashList<TKey, TValue>
: IList<TValue>
{
#region Fields and Properties
private Dictionary<TKey, int> mDictionary = new Dictionary<TKey, int>();
private List<TValue> mList = new List<TValue>();
/// <summary>
/// Gets or sets the value for the given key.
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public TValue this[TKey key]
{
get { return GetValue(key); }
set
{
if (mIsReadOnly) throw new InvalidOperationException("The list is readonly.");
SetValue(key, value);
}
}
/// <summary>
/// Gets or sets the value at the designated index.
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public TValue this[int index]
{
get { return mList[index]; }
set
{
if (mIsReadOnly) throw new InvalidOperationException("The list is readonly.");
mList[index] = value;
}
}
/// <summary>
/// Gets the number of items in the list.
/// </summary>
public int Count
{
get { return mList.Count; }
}
private bool mIsReadOnly = false;
/// <summary>
/// Gets a value that determines if the list if readonly.
/// </summary>
public bool IsReadOnly
{
get { return mIsReadOnly; }
protected set { mIsReadOnly = value; }
}
#endregion
#region Methods
/// <summary>
/// Adds the value to the list.
/// </summary>
/// <param name="index"></param>
/// <param name="key"></param>
/// <param name="value"></param>
public void Insert(int index, TKey key, TValue value)
{
if (mIsReadOnly) throw new InvalidOperationException("The list is readonly.");
if (mDictionary.ContainsKey(key)) throw new ArgumentException("Key already exists in the collection.");
mList.Insert(index, value);
foreach (var item in mDictionary.ToArray())
{
if (item.Value >= index)
mDictionary[item.Key] = item.Value + 1;
}
mDictionary.Add(key, index);
}
/// <summary>
/// Adds the value to the list.
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void Add(TKey key, TValue value)
{
if (mIsReadOnly) throw new InvalidOperationException("The list is readonly.");
if (mDictionary.ContainsKey(key)) throw new ArgumentException("Key already exists in the collection.");
SetValue(key, value);
}
/// <summary>
/// Removes the item from the list.
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public bool Remove(TKey key)
{
if (mIsReadOnly) throw new InvalidOperationException("The list is readonly.");
if (!mDictionary.ContainsKey(key)) return false;
var i = mDictionary[key];
mDictionary.Remove(key);
mList.RemoveAt(i);
foreach (var item in mDictionary.ToArray())
{
if (item.Value > i)
mDictionary[item.Key] = item.Value - 1;
}
return true;
}
/// <summary>
/// Removes the item from the list.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public bool Remove(TValue value)
{
if (mIsReadOnly) throw new InvalidOperationException("The list is readonly.");
var key = GetKey(value);
return Remove(key);
}
/// <summary>
/// Removes the value at the designated index.
/// </summary>
/// <param name="index"></param>
public void RemoveAt(int index)
{
if (mIsReadOnly) throw new InvalidOperationException("The list is readonly.");
var key = GetKeyFromIndex(index);
Remove(key);
}
/// <summary>
/// Removes all the items from the list.
/// </summary>
public void Clear()
{
if (mIsReadOnly) throw new InvalidOperationException("The list is readonly.");
mDictionary.Clear();
mList.Clear();
}
/// <summary>
/// Gets the value from the list.
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
protected TValue GetValue(TKey key)
{
var i = mDictionary[key];
return mList[i];
}
/// <summary>
/// Gets the value from the list. If the key is not in the list, returns the default value.
/// </summary>
/// <param name="key"></param>
/// <param name="dflt">Default value to return if the key does not exist.</param>
/// <returns></returns>
public TValue GetValue(TKey key, TValue dflt)
{
TValue value;
if (TryGetValue(key, out value)) return value;
return dflt;
}
/// <summary>
/// Gets the value from the list. Returns true if the value exists, otherwise false.
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public bool TryGetValue(TKey key, out TValue value)
{
if (!mDictionary.ContainsKey(key))
{
value = default(TValue);
return false;
}
var i = mDictionary[key];
value = mList[i];
return true;
}
/// <summary>
/// Sets the value in the list.
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
protected int SetValue(TKey key, TValue value)
{
int i;
if (mDictionary.ContainsKey(key))
{
i = mDictionary[key];
mList[i] = value;
}
else
{
mList.Add(value);
i = mList.Count - 1;
mDictionary.Add(key, i);
}
return i;
}
/// <summary>
/// Determines if the key is in the list.
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public bool ContainsKey(TKey key)
{
return mDictionary.ContainsKey(key);
}
/// <summary>
/// Determines if the item is in the list.
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public bool Contains(TValue item)
{
return mList.Contains(item);
}
/// <summary>
/// Gets the index of the given item.
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public int IndexOf(TKey key)
{
if (!mDictionary.ContainsKey(key)) return -1;
return mDictionary[key];
}
/// <summary>
/// Gets the index of the given item.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public int IndexOf(TValue value)
{
return mList.IndexOf(value);
}
/// <summary>
/// Gets the key based on the value.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public TKey GetKey(TValue value)
{
var i = mList.IndexOf(value);
if (i < 0) throw new ArgumentException("Value not found in the collection.");
foreach (var keyVal in mDictionary)
{
if (keyVal.Value == i)
return keyVal.Key;
}
// This should never happen, but just in case.
throw new InvalidOperationException("The key was not found in the collection.");
}
/// <summary>
/// Gets the key based on the index.
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public TKey GetKeyFromIndex(int index)
{
foreach (var item in mDictionary)
if (item.Value == index) return item.Key;
throw new ArgumentOutOfRangeException("The index was not found in the dictionary.");
}
/// <summary>
/// Returns the collection of keys.
/// </summary>
public ICollection<TKey> Keys
{
get { return mDictionary.Keys; }
}
/// <summary>
/// Gets the enumerator for the list.
/// </summary>
/// <returns></returns>
public IEnumerator<TValue> GetEnumerator()
{
return mList.GetEnumerator();
}
/// <summary>
/// Gets the enumerator for the list.
/// </summary>
/// <returns></returns>
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// Gets an array of the values.
/// </summary>
/// <returns></returns>
public TValue[] ToArray()
{
return mList.ToArray();
}
/// <summary>
/// Copies the values to an array.
/// </summary>
/// <param name="array"></param>
/// <param name="arrayIndex"></param>
void ICollection<TValue>.CopyTo(TValue[] array, int arrayIndex)
{
mList.CopyTo(array, arrayIndex);
}
#endregion
#region Unsupported IList methods
void IList<TValue>.Insert(int index, TValue item)
{
throw new NotSupportedException("Cannot insert values without the key.");
}
void ICollection<TValue>.Add(TValue item)
{
throw new NotSupportedException("Cannot add values without the key.");
}
#endregion
}
}
|