//****************************** // Written by Peter Golde // Copyright (c) 2004-2005, Wintellect // // Use and restribution of this code is subject to the license agreement // contained in the file "License.txt" accompanying this file. //****************************** using System; using System.Collections; using System.Collections.Generic; namespace Wintellect.PowerCollections { /// /// The OrderedMultiDictionary class that associates values with a key. Unlike an OrderedDictionary, /// each key can have multiple values associated with it. When indexing an OrderedMultidictionary, instead /// of a single value associated with a key, you retrieve an enumeration of values. /// All of the key are stored in sorted order. Also, the values associated with a given key /// are kept in sorted order as well. /// When constructed, you can chose to allow the same value to be associated with a key multiple /// times, or only one time. /// /// The type of the keys. /// The of values associated with the keys. /// /// [Serializable] public class OrderedMultiDictionary : MultiDictionaryBase, ICloneable { // The comparer for comparing keys private IComparer keyComparer; // The comparer for comparing values; private IComparer valueComparer; // The comparer for comparing key-value pairs. Ordered by keys, then by values private IComparer> comparer; // The red-black tree that stores the keys and values. Each key-value pair is stored as a separate item, // sorted first by keys, then by value. Thus, all values associated with a given key are stored together. private RedBlackTree> tree; // Whether duplicate values for the same key are allowed. private bool allowDuplicateValues; // Total number of keys in the tree. private int keyCount; /// /// Helper function to create a new KeyValuePair struct. /// /// The key. /// The value. /// A new KeyValuePair. private static KeyValuePair NewPair(TKey key, TValue value) { KeyValuePair pair = new KeyValuePair(key, value); return pair; } /// /// Helper function to create a new KeyValuePair struct with a default value. /// /// The key. /// A new KeyValuePair. private static KeyValuePair NewPair(TKey key) { KeyValuePair pair = new KeyValuePair(key, default(TValue)); return pair; } /// /// Get a RangeTester that maps to the range of all items with the /// given key. /// /// Key in the given range. /// A RangeTester delegate that selects the range of items with that range. private RedBlackTree>.RangeTester KeyRange(TKey key) { return delegate(KeyValuePair pair) { return keyComparer.Compare(pair.Key, key); }; } /// /// Gets a range tester that defines a range by first and last items. /// /// The lower bound. /// True if the lower bound is inclusive, false if exclusive. /// The upper bound. /// True if the upper bound is inclusive, false if exclusive. /// A RangeTester delegate that tests for a key in the given range. private RedBlackTree>.RangeTester DoubleBoundedKeyRangeTester(TKey first, bool firstInclusive, TKey last, bool lastInclusive) { return delegate(KeyValuePair pair) { if (firstInclusive) { if (keyComparer.Compare(first, pair.Key) > 0) return -1; // item is before first. } else { if (keyComparer.Compare(first, pair.Key) >= 0) return -1; // item is before or equal to first. } if (lastInclusive) { if (keyComparer.Compare(last, pair.Key) < 0) return 1; // item is after last. } else { if (keyComparer.Compare(last, pair.Key) <= 0) return 1; // item is after or equal to last } return 0; // item is between first and last. }; } /// /// Gets a range tester that defines a range by a lower bound. /// /// The lower bound. /// True if the lower bound is inclusive, false if exclusive. /// A RangeTester delegate that tests for a key in the given range. private RedBlackTree>.RangeTester LowerBoundedKeyRangeTester(TKey first, bool inclusive) { return delegate(KeyValuePair pair) { if (inclusive) { if (keyComparer.Compare(first, pair.Key) > 0) return -1; // item is before first. else return 0; // item is after or equal to first } else { if (keyComparer.Compare(first, pair.Key) >= 0) return -1; // item is before or equal to first. else return 0; // item is after first } }; } /// /// Gets a range tester that defines a range by upper bound. /// /// The upper bound. /// True if the upper bound is inclusive, false if exclusive. /// A RangeTester delegate that tests for a key in the given range. private RedBlackTree>.RangeTester UpperBoundedKeyRangeTester(TKey last, bool inclusive) { return delegate(KeyValuePair pair) { if (inclusive) { if (keyComparer.Compare(last, pair.Key) < 0) return 1; // item is after last. else return 0; // item is before or equal to last. } else { if (keyComparer.Compare(last, pair.Key) <= 0) return 1; // item is after or equal to last else return 0; // item is before last. } }; } #region Constructors /// /// Create a new OrderedMultiDictionary. The default ordering of keys and values are used. If duplicate values /// are allowed, multiple copies of the same value can be associated with the same key. For example, the key "foo" /// could have "a", "a", and "b" associated with it. If duplicate values are not allowed, only one copies of a given value can /// be associated with the same key, although different keys can have the same value. For example, the key "foo" could /// have "a" and "b" associated with it, which key "bar" has values "b" and "c" associated with it. /// /// The default ordering of keys and values will be used, as defined by TKey and TValue's implementation /// of IComparable<T> (or IComparable if IComparable<T> is not implemented). If a different ordering should be /// used, other constructors allow a custom Comparer or IComparer to be passed to changed the ordering. /// Can the same value be associated with a key multiple times? /// TKey or TValue does not implement either IComparable<T> or IComparable. public OrderedMultiDictionary(bool allowDuplicateValues) : this(allowDuplicateValues, Comparers.DefaultComparer(), Comparers.DefaultComparer()) { } /// /// Create a new OrderedMultiDictionary. If duplicate values /// are allowed, multiple copies of the same value can be associated with the same key. For example, the key "foo" /// could have "a", "a", and "b" associated with it. If duplicate values are not allowed, only one copies of a given value can /// be associated with the same key, although different keys can have the same value. For example, the key "foo" could /// have "a" and "b" associated with it, which key "bar" has values "b" and "c" associated with it. /// /// Can the same value be associated with a key multiple times? /// A delegate to a method that will be used to compare keys. /// TValue does not implement either IComparable<TValue> or IComparable. public OrderedMultiDictionary(bool allowDuplicateValues, Comparison keyComparison) : this(allowDuplicateValues, Comparers.ComparerFromComparison(keyComparison), Comparers.DefaultComparer()) { } /// /// Create a new OrderedMultiDictionary. If duplicate values /// are allowed, multiple copies of the same value can be associated with the same key. For example, the key "foo" /// could have "a", "a", and "b" associated with it. If duplicate values are not allowed, only one copies of a given value can /// be associated with the same key, although different keys can have the same value. For example, the key "foo" could /// have "a" and "b" associated with it, which key "bar" has values "b" and "c" associated with it. /// /// Can the same value be associated with a key multiple times? /// A delegate to a method that will be used to compare keys. /// A delegate to a method that will be used to compare values. public OrderedMultiDictionary(bool allowDuplicateValues, Comparison keyComparison, Comparison valueComparison) : this(allowDuplicateValues, Comparers.ComparerFromComparison(keyComparison), Comparers.ComparerFromComparison(valueComparison)) { } /// /// Create a new OrderedMultiDictionary. If duplicate values /// are allowed, multiple copies of the same value can be associated with the same key. For example, the key "foo" /// could have "a", "a", and "b" associated with it. If duplicate values are not allowed, only one copies of a given value can /// be associated with the same key, although different keys can have the same value. For example, the key "foo" could /// have "a" and "b" associated with it, which key "bar" has values "b" and "c" associated with it. /// /// Can the same value be associated with a key multiple times? /// An IComparer<TKey> instance that will be used to compare keys. /// TValue does not implement either IComparable<TValue> or IComparable. public OrderedMultiDictionary(bool allowDuplicateValues, IComparer keyComparer) : this(allowDuplicateValues, keyComparer, Comparers.DefaultComparer()) { } /// /// Create a new OrderedMultiDictionary. If duplicate values /// are allowed, multiple copies of the same value can be associated with the same key. For example, the key "foo" /// could have "a", "a", and "b" associated with it. If duplicate values are not allowed, only one copies of a given value can /// be associated with the same key, although different keys can have the same value. For example, the key "foo" could /// have "a" and "b" associated with it, which key "bar" has values "b" and "c" associated with it. /// /// Can the same value be associated with a key multiple times? /// An IComparer<TKey> instance that will be used to compare keys. /// An IComparer<TValue> instance that will be used to compare values. public OrderedMultiDictionary(bool allowDuplicateValues, IComparer keyComparer, IComparer valueComparer) { if (keyComparer == null) throw new ArgumentNullException("keyComparer"); if (valueComparer == null) throw new ArgumentNullException("valueComparer"); this.allowDuplicateValues = allowDuplicateValues; this.keyComparer = keyComparer; this.valueComparer = valueComparer; this.comparer = Comparers.ComparerPairFromKeyValueComparers(keyComparer, valueComparer); this.tree = new RedBlackTree>(this.comparer); } /// /// Create a new OrderedMultiDictionary. Used internally for cloning. /// /// Can the same value be associated with a key multiple times? /// Number of keys. /// An IComparer<TKey> instance that will be used to compare keys. /// An IComparer<TValue> instance that will be used to compare values. /// Comparer of key-value pairs. /// The red-black tree used to store the data. private OrderedMultiDictionary(bool allowDuplicateValues, int keyCount, IComparer keyComparer, IComparer valueComparer, IComparer> comparer, RedBlackTree> tree) { this.allowDuplicateValues = allowDuplicateValues; this.keyCount = keyCount; this.keyComparer = keyComparer; this.valueComparer = valueComparer; this.comparer = comparer; this.tree = tree; } #endregion Constructors #region Add or remove items /// /// Adds a new value to be associated with a key. If duplicate values are permitted, this /// method always adds a new key-value pair to the dictionary. /// If duplicate values are not permitted, and already has a value /// equal to associated with it, then that value is replaced with , /// and the number of values associate with is unchanged. /// /// The key to associate with. /// The value to associated with . public sealed override void Add(TKey key, TValue value) { KeyValuePair pair = NewPair(key, value); KeyValuePair dummy; if (!ContainsKey(key)) ++keyCount; tree.Insert(pair, allowDuplicateValues ? DuplicatePolicy.InsertLast : DuplicatePolicy.ReplaceLast, out dummy); } /// /// Removes a given value from the values associated with a key. If the /// last value is removed from a key, the key is removed also. /// /// A key to remove a value from. /// The value to remove. /// True if was associated with (and was /// therefore removed). False if was not associated with . public sealed override bool Remove(TKey key, TValue value) { KeyValuePair dummy; bool found = tree.Delete(NewPair(key, value), false, out dummy); if (found && !ContainsKey(key)) --keyCount; // Removed the last value associated with the key. return found; } /// /// Removes a key and all associated values from the dictionary. If the /// key is not present in the dictionary, it is unchanged and false is returned. /// /// The key to remove. /// True if the key was present and was removed. Returns /// false if the key was not present. public sealed override bool Remove(TKey key) { if (tree.DeleteRange(KeyRange(key)) > 0) { --keyCount; return true; } else { return false; } } /// /// Removes all keys and values from the dictionary. /// public sealed override void Clear() { tree.StopEnumerations(); // Invalidate any enumerations. // The simplest and fastest way is simply to throw away the old tree and create a new one. tree = new RedBlackTree>(comparer); keyCount = 0; } #endregion Add or remove items #region Query items /// /// Returns the IComparer<T> used to compare keys in this dictionary. /// /// If the dictionary was created using a comparer, that comparer is returned. If the dictionary was /// created using a comparison delegate, then a comparer equivalent to that delegate /// is returned. Otherwise /// the default comparer for TKey (Comparer<TKey>.Default) is returned. public IComparer KeyComparer { get { return this.keyComparer; } } /// /// Returns the IComparer<T> used to compare values in this dictionary. /// /// If the dictionary was created using a comparer, that comparer is returned. If the dictionary was /// created using a comparison delegate, then a comparer equivalent to that delegate /// is returned. Otherwise /// the default comparer for TValue (Comparer<TValue>.Default) is returned. public IComparer ValueComparer { get { return this.valueComparer; } } /// /// Determine if two values are equal. /// /// First value to compare. /// Second value to compare. /// True if the values are equal. protected sealed override bool EqualValues(TValue value1, TValue value2) { return valueComparer.Compare(value1, value2) == 0; } /// /// Gets the number of key-value pairs in the dictionary. Each value associated /// with a given key is counted. If duplicate values are permitted, each duplicate /// value is included in the count. /// /// The number of key-value pairs in the dictionary. public sealed override int Count { get { return keyCount; } } /// /// Checks to see if is associated with /// in the dictionary. /// /// The key to check. /// The value to check. /// True if is associated with . public sealed override bool Contains(TKey key, TValue value) { KeyValuePair dummy; return tree.Find(NewPair(key, value), true, false, out dummy); } /// /// Checks to see if the key is present in the dictionary and has /// at least one value associated with it. /// /// The key to check. /// True if is present and has at least /// one value associated with it. Returns false otherwise. public sealed override bool ContainsKey(TKey key) { KeyValuePair dummy; return (tree.FirstItemInRange(KeyRange(key), out dummy) >= 0); } /// /// A private helper method that returns an enumerable that /// enumerates all the keys in a range. /// /// Defines the range to enumerate. /// Should the keys be enumerated in reverse order? /// An IEnumerable<TKey> that enumerates the keys in the given range. /// in the dictionary. private IEnumerator EnumerateKeys(RedBlackTree>.RangeTester rangeTester, bool reversed) { bool isFirst = true; TKey lastKey = default(TKey); IEnumerable> pairs; if (reversed) pairs = tree.EnumerateRangeReversed(rangeTester); else pairs = tree.EnumerateRange(rangeTester); // Enumerate pairs; yield a new key when the key changes. foreach (KeyValuePair pair in pairs) { if (isFirst || keyComparer.Compare(lastKey, pair.Key) != 0) { lastKey = pair.Key; yield return lastKey; } isFirst = false; } } /// /// A private helper method for the indexer to return an enumerable that /// enumerates all the values for a key. This is separate method because indexers /// can't use the yield return construct. /// /// /// An IEnumerable<TValue> that can be used to enumerate all the /// values associated with . If is not present, /// an enumerable that enumerates no items is returned. private IEnumerator EnumerateValuesForKey(TKey key) { foreach (KeyValuePair pair in tree.EnumerateRange(KeyRange(key))) yield return pair.Value; } /// /// Determines if this dictionary contains a key equal to . If so, all the values /// associated with that key are returned through the values parameter. /// /// The key to search for. /// Returns all values associated with key, if true was returned. /// True if the dictionary contains key. False if the dictionary does not contain key. protected sealed override bool TryEnumerateValuesForKey(TKey key, out IEnumerator values) { // CONSIDER: It would be nice to eliminate the double lookup here, but there doesn't seem to be an easy way. if (ContainsKey(key)) { values = EnumerateValuesForKey(key); return true; } else { values = null; return false; } } /// /// Enumerate all of the keys in the dictionary. /// /// An IEnumerator<TKey> of all of the keys in the dictionary. protected sealed override IEnumerator EnumerateKeys() { return EnumerateKeys(tree.EntireRangeTester, false); } /// /// Gets the number of values associated with a given key. /// /// The key to count values of. /// The number of values associated with . If /// is not present in the dictionary, zero is returned. protected sealed override int CountValues(TKey key) { return tree.CountRange(KeyRange(key)); } /// /// Gets a total count of values in the collection. /// /// The total number of values associated with all keys in the dictionary. protected sealed override int CountAllValues() { return tree.ElementCount; } #endregion Query items #region Cloning /// /// Makes a shallow clone of this dictionary; i.e., if keys or values of the /// dictionary are reference types, then they are not cloned. If TKey or TValue is a value type, /// then each element is copied as if by simple assignment. /// /// Cloning the dictionary takes time O(N), where N is the number of key-value pairs in the dictionary. /// The cloned dictionary. public OrderedMultiDictionary Clone() { OrderedMultiDictionary newDict = new OrderedMultiDictionary(allowDuplicateValues, keyCount, keyComparer, valueComparer, comparer, tree.Clone()); return newDict; } /// /// Implements ICloneable.Clone. Makes a shallow clone of this dictionary; i.e., if keys or values are reference types, then they are not cloned. /// /// The cloned dictionary. object ICloneable.Clone() { return Clone(); } /// /// Throw an InvalidOperationException indicating that this type is not cloneable. /// /// Type to test. private void NonCloneableType(Type t) { throw new InvalidOperationException(string.Format(Strings.TypeNotCloneable, t.FullName)); } /// /// Makes a deep clone of this dictionary. A new dictionary is created with a clone of /// each entry of this dictionary, by calling ICloneable.Clone on each element. If TKey or TValue is /// a value type, then each element is copied as if by simple assignment. /// /// If TKey or TValue is a reference type, it must implement /// ICloneable. Otherwise, an InvalidOperationException is thrown. /// Cloning the dictionary takes time O(N log N), where N is the number of key-value pairs in the dictionary. /// The cloned dictionary. /// TKey or TValue is a reference type that does not implement ICloneable. public OrderedMultiDictionary CloneContents() { bool keyIsValueType, valueIsValueType; // Make sure that TKey and TValue can be cloned. if (!Util.IsCloneableType(typeof(TKey), out keyIsValueType)) NonCloneableType(typeof(TKey)); if (!Util.IsCloneableType(typeof(TValue), out valueIsValueType)) NonCloneableType(typeof(TValue)); OrderedMultiDictionary newDict = new OrderedMultiDictionary(allowDuplicateValues, keyComparer, valueComparer); foreach (KeyValuePair pair in tree) { // Clone the key and value parts of the pair. Value types can be cloned // by just copying them, otherwise, ICloneable is used. TKey keyClone; TValue valueClone; if (keyIsValueType) keyClone = pair.Key; else { if (pair.Key == null) keyClone = default(TKey); // Really null, because we know TKey isn't a value type. else keyClone = (TKey)(((ICloneable)pair.Key).Clone()); } if (valueIsValueType) valueClone = pair.Value; else { if (pair.Value == null) valueClone = default(TValue); // Really null, because we know TKey isn't a value type. else valueClone = (TValue)(((ICloneable)pair.Value).Clone()); } newDict.Add(keyClone, valueClone); } return newDict; } #endregion Cloning #region KeyValuePairsCollection /// /// Gets a read-only collection of all key-value pairs in the dictionary. If a key has multiple /// values associated with it, then a key-value pair is present for each value associated /// with the key. /// public sealed override ICollection> KeyValuePairs { get { return new KeyValuePairsCollection(this); } } /// /// A private class that implements ICollection<KeyValuePair<TKey,TValue>> and ICollection for the /// KeyValuePairs collection. The collection is read-only. /// [Serializable] private sealed class KeyValuePairsCollection : ReadOnlyCollectionBase> { private OrderedMultiDictionary myDictionary; public KeyValuePairsCollection(OrderedMultiDictionary myDictionary) { this.myDictionary = myDictionary; } public override int Count { get { return myDictionary.CountAllValues(); } } public override IEnumerator> GetEnumerator() { return myDictionary.tree.GetEnumerator(); } public override bool Contains(KeyValuePair pair) { KeyValuePair dummy; return myDictionary.tree.Find(pair, true, false, out dummy); } } #endregion KeyValuePairs collection #region Views /// /// Returns a View collection that can be used for enumerating the keys and values in the collection in /// reversed order. /// /// ///

Typically, this method is used in conjunction with a foreach statement. For example: /// /// foreach(KeyValuePair<TKey, TValue> pair in dictionary.Reversed()) { /// // process pair /// } ///

///

If an entry is added to or deleted from the dictionary while the View is being enumerated, then /// the enumeration will end with an InvalidOperationException.

///

Calling Reverse does not copy the data in the dictionary, and the operation takes constant time.

///
/// An OrderedDictionary.View of key-value pairs in reverse order. public View Reversed() { return new View(this, tree.EntireRangeTester, true, true); } /// /// Returns a collection that can be used for enumerating some of the keys and values in the collection. /// Only keys that are greater than and /// less than are included. The keys are enumerated in sorted order. /// Keys equal to the end points of the range can be included or excluded depending on the /// and parameters. /// /// ///

If is greater than or equal to , the returned collection is empty.

///

The sorted order of the keys is determined by the comparison instance or delegate used /// to create the dictionary.

///

Typically, this property is used in conjunction with a foreach statement. For example:

/// /// foreach(KeyValuePair<TKey, TValue> pair in dictionary.Range(from, true, to, false)) { /// // process pair /// } /// ///

Calling Range does not copy the data in the dictionary, and the operation takes constant time.

/// The lower bound of the range. /// If true, the lower bound is inclusive--keys equal to the lower bound will /// be included in the range. If false, the lower bound is exclusive--keys equal to the lower bound will not /// be included in the range. /// The upper bound of the range. /// If true, the upper bound is inclusive--keys equal to the upper bound will /// be included in the range. If false, the upper bound is exclusive--keys equal to the upper bound will not /// be included in the range. /// An OrderedMultiDictionary.View of key-value pairs in the given range. public View Range(TKey from, bool fromInclusive, TKey to, bool toInclusive) { return new View(this, DoubleBoundedKeyRangeTester(from, fromInclusive, to, toInclusive), false, false); } /// /// Returns a collection that can be used for enumerating some of the keys and values in the collection. /// Only keys that are greater than (and optionally, equal to) are included. /// The keys are enumerated in sorted order. Keys equal to can be included /// or excluded depending on the parameter. /// /// ///

The sorted order of the keys is determined by the comparison instance or delegate used /// to create the dictionary.

///

Typically, this property is used in conjunction with a foreach statement. For example:

/// /// foreach(KeyValuePair<TKey, TValue> pair in dictionary.RangeFrom(from, true)) { /// // process pair /// } /// ///

Calling RangeFrom does not copy of the data in the dictionary, and the operation takes constant time.

///
/// The lower bound of the range. /// If true, the lower bound is inclusive--keys equal to the lower bound will /// be included in the range. If false, the lower bound is exclusive--keys equal to the lower bound will not /// be included in the range. /// An OrderedMultiDictionary.View of key-value pairs in the given range. public View RangeFrom(TKey from, bool fromInclusive) { return new View(this, LowerBoundedKeyRangeTester(from, fromInclusive), false, false); } /// /// Returns a collection that can be used for enumerating some of the keys and values in the collection. /// Only items that are less than (and optionally, equal to) are included. /// The items are enumerated in sorted order. Items equal to can be included /// or excluded depending on the parameter. /// /// ///

The sorted order of the keys is determined by the comparison instance or delegate used /// to create the dictionary.

///

Typically, this property is used in conjunction with a foreach statement. For example:

/// /// foreach(KeyValuePair<TKey, TValue> pair in dictionary.RangeFrom(from, false)) { /// // process pair /// } /// ///

Calling RangeTo does not copy the data in the dictionary, and the operation takes constant time.

///
/// The upper bound of the range. /// If true, the upper bound is inclusive--keys equal to the upper bound will /// be included in the range. If false, the upper bound is exclusive--keys equal to the upper bound will not /// be included in the range. /// An OrderedMultiDictionary.View of key-value pairs in the given range. public View RangeTo(TKey to, bool toInclusive) { return new View(this, UpperBoundedKeyRangeTester(to, toInclusive), false, false); } #endregion Views /// /// The OrderedMultiDictionary<TKey,TValue>.View class is used to look at a subset of the keys and values /// inside an ordered multi-dictionary. It is returned from the Range, RangeTo, RangeFrom, and Reversed methods. /// /// ///

Views are dynamic. If the underlying dictionary changes, the view changes in sync. If a change is made /// to the view, the underlying dictionary changes accordingly.

///

Typically, this class is used in conjunction with a foreach statement to enumerate the keys /// and values in a subset of the OrderedMultiDictionary. For example:

/// /// foreach(KeyValuePair<TKey, TValue> pair in dictionary.Range(from, to)) { /// // process pair /// } /// ///
[Serializable] public class View : MultiDictionaryBase { private OrderedMultiDictionary myDictionary; private RedBlackTree>.RangeTester rangeTester; // range tester for the range being used. private bool entireTree; // is the view the whole tree? private bool reversed; // is the view reversed? /// /// Initialize the View. /// /// Associated OrderedMultiDictionary to be viewed. /// Range tester that defines the range being used. /// If true, then rangeTester defines the entire tree. /// Is the view enuemerated in reverse order? internal View(OrderedMultiDictionary myDictionary, RedBlackTree>.RangeTester rangeTester, bool entireTree, bool reversed) { this.myDictionary = myDictionary; this.rangeTester = rangeTester; this.entireTree = entireTree; this.reversed = reversed; } /// /// Determine if the given key lies within the bounds of this view. /// /// Key to test. /// True if the key is within the bounds of this view. private bool KeyInView(TKey key) { return rangeTester(NewPair(key, default(TValue))) == 0; } /// /// Enumerate all the keys in the dictionary. /// /// An IEnumerator<TKey> that enumerates all of the keys in the collection that /// have at least one value associated with them. protected sealed override IEnumerator EnumerateKeys() { return myDictionary.EnumerateKeys(rangeTester, reversed); } /// /// Enumerate all of the values associated with a given key. If the key exists and has values associated with it, an enumerator for those /// values is returned throught . If the key does not exist, false is returned. /// /// The key to get values for. /// If true is returned, this parameter receives an enumerators that /// enumerates the values associated with that key. /// True if the key exists and has values associated with it. False otherwise. protected sealed override bool TryEnumerateValuesForKey(TKey key, out IEnumerator values) { if (!KeyInView(key)) { values = null; return false; } else return myDictionary.TryEnumerateValuesForKey(key, out values); } /// /// Number of keys in this view. /// /// Number of keys that lie within the bounds the view. public sealed override int Count { get { if (entireTree) return myDictionary.Count; else { int count = 0; using (IEnumerator enumKeys = myDictionary.EnumerateKeys(rangeTester, reversed)) { while (enumKeys.MoveNext()) { ++count; } } return count; } } } /// /// Tests if the key is present in the part of the dictionary being viewed. /// /// Key to check /// True if the key is within this view. public sealed override bool ContainsKey(TKey key) { if (!KeyInView(key)) return false; else return myDictionary.ContainsKey(key); } /// /// Tests if the key-value pair is present in the part of the dictionary being viewed. /// /// Key to check for. /// Value to check for. /// True if the key-value pair is within this view. public sealed override bool Contains(TKey key, TValue value) { if (!KeyInView(key)) return false; else return myDictionary.Contains(key, value); } /// /// Gets the number of values associated with a given key. /// /// The key to count values of. /// The number of values associated with . If /// is not present in this view, zero is returned. protected sealed override int CountValues(TKey key) { if (!KeyInView(key)) return 0; else return myDictionary.CountValues(key); } /// /// Adds the given key-value pair to the underlying dictionary of this view. /// If is not within the range of this view, an /// ArgumentException is thrown. /// /// /// /// is not /// within the range of this view. public sealed override void Add(TKey key, TValue value) { if (!KeyInView(key)) throw new ArgumentException(Strings.OutOfViewRange, "key"); else myDictionary.Add(key, value); } /// /// Removes the key (and associated value) from the underlying dictionary of this view. If /// no key in the view is equal to the passed key, the dictionary and view are unchanged. /// /// The key to remove. /// True if the key was found and removed. False if the key was not found. public sealed override bool Remove(TKey key) { if (!KeyInView(key)) return false; else return myDictionary.Remove(key); } /// /// Removes the key and value from the underlying dictionary of this view. that is equal to the passed in key. If /// no key in the view is equal to the passed key, or has the given value associated with it, the dictionary and view are unchanged. /// /// The key to remove. /// The value to remove. /// True if the key-value pair was found and removed. False if the key-value pair was not found. public sealed override bool Remove(TKey key, TValue value) { if (!KeyInView(key)) return false; else return myDictionary.Remove(key, value); } /// /// Removes all the keys and values within this view from the underlying OrderedMultiDictionary. /// /// The following removes all the keys that start with "A" from an OrderedMultiDictionary. /// /// dictionary.Range("A", "B").Clear(); /// /// public sealed override void Clear() { if (entireTree) { myDictionary.Clear(); } else { myDictionary.keyCount -= this.Count; myDictionary.tree.DeleteRange(rangeTester); } } /// /// Creates a new View that has the same keys and values as this, in the reversed order. /// /// A new View that has the reversed order of this view. public View Reversed() { return new View(myDictionary, rangeTester, entireTree, !reversed); } } } }