// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.Serialization; namespace System.Collections.Generic { [DebuggerTypeProxy(typeof(IDictionaryDebugView<,>))] [DebuggerDisplay("Count = {Count}")] [Serializable] [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class SortedDictionary : IDictionary, IDictionary, IReadOnlyDictionary { [NonSerialized] private KeyCollection _keys; [NonSerialized] private ValueCollection _values; private TreeSet> _set; // Do not rename (binary serialization) public SortedDictionary() : this((IComparer)null) { } public SortedDictionary(IDictionary dictionary) : this(dictionary, null) { } public SortedDictionary(IDictionary dictionary, IComparer comparer) { if (dictionary == null) { throw new ArgumentNullException(nameof(dictionary)); } _set = new TreeSet>(new KeyValuePairComparer(comparer)); foreach (KeyValuePair pair in dictionary) { _set.Add(pair); } } public SortedDictionary(IComparer comparer) { _set = new TreeSet>(new KeyValuePairComparer(comparer)); } void ICollection>.Add(KeyValuePair keyValuePair) { _set.Add(keyValuePair); } bool ICollection>.Contains(KeyValuePair keyValuePair) { TreeSet>.Node node = _set.FindNode(keyValuePair); if (node == null) { return false; } if (keyValuePair.Value == null) { return node.Item.Value == null; } else { return EqualityComparer.Default.Equals(node.Item.Value, keyValuePair.Value); } } bool ICollection>.Remove(KeyValuePair keyValuePair) { TreeSet>.Node node = _set.FindNode(keyValuePair); if (node == null) { return false; } if (EqualityComparer.Default.Equals(node.Item.Value, keyValuePair.Value)) { _set.Remove(keyValuePair); return true; } return false; } bool ICollection>.IsReadOnly { get { return false; } } public TValue this[TKey key] { get { if (key == null) { throw new ArgumentNullException(nameof(key)); } TreeSet>.Node node = _set.FindNode(new KeyValuePair(key, default(TValue))); if (node == null) { throw new KeyNotFoundException(); } return node.Item.Value; } set { if (key == null) { throw new ArgumentNullException(nameof(key)); } TreeSet>.Node node = _set.FindNode(new KeyValuePair(key, default(TValue))); if (node == null) { _set.Add(new KeyValuePair(key, value)); } else { node.Item = new KeyValuePair(node.Item.Key, value); _set.UpdateVersion(); } } } public int Count { get { return _set.Count; } } public IComparer Comparer { get { return ((KeyValuePairComparer)_set.Comparer).keyComparer; } } public KeyCollection Keys { get { if (_keys == null) _keys = new KeyCollection(this); return _keys; } } ICollection IDictionary.Keys { get { return Keys; } } IEnumerable IReadOnlyDictionary.Keys { get { return Keys; } } public ValueCollection Values { get { if (_values == null) _values = new ValueCollection(this); return _values; } } ICollection IDictionary.Values { get { return Values; } } IEnumerable IReadOnlyDictionary.Values { get { return Values; } } public void Add(TKey key, TValue value) { if (key == null) { throw new ArgumentNullException(nameof(key)); } _set.Add(new KeyValuePair(key, value)); } public void Clear() { _set.Clear(); } public bool ContainsKey(TKey key) { if (key == null) { throw new ArgumentNullException(nameof(key)); } return _set.Contains(new KeyValuePair(key, default(TValue))); } public bool ContainsValue(TValue value) { bool found = false; if (value == null) { _set.InOrderTreeWalk(delegate (TreeSet>.Node node) { if (node.Item.Value == null) { found = true; return false; // stop the walk } return true; }); } else { EqualityComparer valueComparer = EqualityComparer.Default; _set.InOrderTreeWalk(delegate (TreeSet>.Node node) { if (valueComparer.Equals(node.Item.Value, value)) { found = true; return false; // stop the walk } return true; }); } return found; } public void CopyTo(KeyValuePair[] array, int index) { _set.CopyTo(array, index); } public Enumerator GetEnumerator() { return new Enumerator(this, Enumerator.KeyValuePair); } IEnumerator> IEnumerable>.GetEnumerator() { return new Enumerator(this, Enumerator.KeyValuePair); } public bool Remove(TKey key) { if (key == null) { throw new ArgumentNullException(nameof(key)); } return _set.Remove(new KeyValuePair(key, default(TValue))); } public bool TryGetValue(TKey key, out TValue value) { if (key == null) { throw new ArgumentNullException(nameof(key)); } TreeSet>.Node node = _set.FindNode(new KeyValuePair(key, default(TValue))); if (node == null) { value = default(TValue); return false; } value = node.Item.Value; return true; } void ICollection.CopyTo(Array array, int index) { ((ICollection)_set).CopyTo(array, index); } bool IDictionary.IsFixedSize { get { return false; } } bool IDictionary.IsReadOnly { get { return false; } } ICollection IDictionary.Keys { get { return (ICollection)Keys; } } ICollection IDictionary.Values { get { return (ICollection)Values; } } object IDictionary.this[object key] { get { if (IsCompatibleKey(key)) { TValue value; if (TryGetValue((TKey)key, out value)) { return value; } } return null; } set { if (key == null) { throw new ArgumentNullException(nameof(key)); } if (value == null && !(default(TValue) == null)) throw new ArgumentNullException(nameof(value)); try { TKey tempKey = (TKey)key; try { this[tempKey] = (TValue)value; } catch (InvalidCastException) { throw new ArgumentException(SR.Format(SR.Arg_WrongType, value, typeof(TValue)), nameof(value)); } } catch (InvalidCastException) { throw new ArgumentException(SR.Format(SR.Arg_WrongType, key, typeof(TKey)), nameof(key)); } } } void IDictionary.Add(object key, object value) { if (key == null) { throw new ArgumentNullException(nameof(key)); } if (value == null && !(default(TValue) == null)) throw new ArgumentNullException(nameof(value)); try { TKey tempKey = (TKey)key; try { Add(tempKey, (TValue)value); } catch (InvalidCastException) { throw new ArgumentException(SR.Format(SR.Arg_WrongType, value, typeof(TValue)), nameof(value)); } } catch (InvalidCastException) { throw new ArgumentException(SR.Format(SR.Arg_WrongType, key, typeof(TKey)), nameof(key)); } } bool IDictionary.Contains(object key) { if (IsCompatibleKey(key)) { return ContainsKey((TKey)key); } return false; } private static bool IsCompatibleKey(object key) { if (key == null) { throw new ArgumentNullException(nameof(key)); } return (key is TKey); } IDictionaryEnumerator IDictionary.GetEnumerator() { return new Enumerator(this, Enumerator.DictEntry); } void IDictionary.Remove(object key) { if (IsCompatibleKey(key)) { Remove((TKey)key); } } bool ICollection.IsSynchronized { get { return false; } } object ICollection.SyncRoot { get { return ((ICollection)_set).SyncRoot; } } IEnumerator IEnumerable.GetEnumerator() { return new Enumerator(this, Enumerator.KeyValuePair); } [SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes", Justification = "not an expected scenario")] public struct Enumerator : IEnumerator>, IDictionaryEnumerator { private TreeSet>.Enumerator _treeEnum; private int _getEnumeratorRetType; // What should Enumerator.Current return? internal const int KeyValuePair = 1; internal const int DictEntry = 2; internal Enumerator(SortedDictionary dictionary, int getEnumeratorRetType) { _treeEnum = dictionary._set.GetEnumerator(); _getEnumeratorRetType = getEnumeratorRetType; } public bool MoveNext() { return _treeEnum.MoveNext(); } public void Dispose() { _treeEnum.Dispose(); } public KeyValuePair Current { get { return _treeEnum.Current; } } internal bool NotStartedOrEnded { get { return _treeEnum.NotStartedOrEnded; } } internal void Reset() { _treeEnum.Reset(); } void IEnumerator.Reset() { _treeEnum.Reset(); } object IEnumerator.Current { get { if (NotStartedOrEnded) { throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); } if (_getEnumeratorRetType == DictEntry) { return new DictionaryEntry(Current.Key, Current.Value); } else { return new KeyValuePair(Current.Key, Current.Value); } } } object IDictionaryEnumerator.Key { get { if (NotStartedOrEnded) { throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); } return Current.Key; } } object IDictionaryEnumerator.Value { get { if (NotStartedOrEnded) { throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); } return Current.Value; } } DictionaryEntry IDictionaryEnumerator.Entry { get { if (NotStartedOrEnded) { throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); } return new DictionaryEntry(Current.Key, Current.Value); } } } [DebuggerTypeProxy(typeof(DictionaryKeyCollectionDebugView<,>))] [DebuggerDisplay("Count = {Count}")] public sealed class KeyCollection : ICollection, ICollection, IReadOnlyCollection { private SortedDictionary _dictionary; public KeyCollection(SortedDictionary dictionary) { if (dictionary == null) { throw new ArgumentNullException(nameof(dictionary)); } _dictionary = dictionary; } public Enumerator GetEnumerator() { return new Enumerator(_dictionary); } IEnumerator IEnumerable.GetEnumerator() { return new Enumerator(_dictionary); } IEnumerator IEnumerable.GetEnumerator() { return new Enumerator(_dictionary); } public void CopyTo(TKey[] array, int index) { if (array == null) { throw new ArgumentNullException(nameof(array)); } if (index < 0) { throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_NeedNonNegNum); } if (array.Length - index < Count) { throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); } _dictionary._set.InOrderTreeWalk(delegate (TreeSet>.Node node) { array[index++] = node.Item.Key; return true; }); } void ICollection.CopyTo(Array array, int index) { if (array == null) { throw new ArgumentNullException(nameof(array)); } if (array.Rank != 1) { throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); } if (array.GetLowerBound(0) != 0) { throw new ArgumentException(SR.Arg_NonZeroLowerBound, nameof(array)); } if (index < 0) { throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_NeedNonNegNum); } if (array.Length - index < _dictionary.Count) { throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); } TKey[] keys = array as TKey[]; if (keys != null) { CopyTo(keys, index); } else { try { object[] objects = (object[])array; _dictionary._set.InOrderTreeWalk(delegate (TreeSet>.Node node) { objects[index++] = node.Item.Key; return true; }); } catch (ArrayTypeMismatchException) { throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); } } } public int Count { get { return _dictionary.Count; } } bool ICollection.IsReadOnly { get { return true; } } void ICollection.Add(TKey item) { throw new NotSupportedException(SR.NotSupported_KeyCollectionSet); } void ICollection.Clear() { throw new NotSupportedException(SR.NotSupported_KeyCollectionSet); } bool ICollection.Contains(TKey item) { return _dictionary.ContainsKey(item); } bool ICollection.Remove(TKey item) { throw new NotSupportedException(SR.NotSupported_KeyCollectionSet); } bool ICollection.IsSynchronized { get { return false; } } object ICollection.SyncRoot { get { return ((ICollection)_dictionary).SyncRoot; } } [SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes", Justification = "not an expected scenario")] public struct Enumerator : IEnumerator, IEnumerator { private SortedDictionary.Enumerator _dictEnum; internal Enumerator(SortedDictionary dictionary) { _dictEnum = dictionary.GetEnumerator(); } public void Dispose() { _dictEnum.Dispose(); } public bool MoveNext() { return _dictEnum.MoveNext(); } public TKey Current { get { return _dictEnum.Current.Key; } } object IEnumerator.Current { get { if (_dictEnum.NotStartedOrEnded) { throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); } return Current; } } void IEnumerator.Reset() { _dictEnum.Reset(); } } } [DebuggerTypeProxy(typeof(DictionaryValueCollectionDebugView<,>))] [DebuggerDisplay("Count = {Count}")] public sealed class ValueCollection : ICollection, ICollection, IReadOnlyCollection { private SortedDictionary _dictionary; public ValueCollection(SortedDictionary dictionary) { if (dictionary == null) { throw new ArgumentNullException(nameof(dictionary)); } _dictionary = dictionary; } public Enumerator GetEnumerator() { return new Enumerator(_dictionary); } IEnumerator IEnumerable.GetEnumerator() { return new Enumerator(_dictionary); } IEnumerator IEnumerable.GetEnumerator() { return new Enumerator(_dictionary); } public void CopyTo(TValue[] array, int index) { if (array == null) { throw new ArgumentNullException(nameof(array)); } if (index < 0) { throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_NeedNonNegNum); } if (array.Length - index < Count) { throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); } _dictionary._set.InOrderTreeWalk(delegate (TreeSet>.Node node) { array[index++] = node.Item.Value; return true; }); } void ICollection.CopyTo(Array array, int index) { if (array == null) { throw new ArgumentNullException(nameof(array)); } if (array.Rank != 1) { throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); } if (array.GetLowerBound(0) != 0) { throw new ArgumentException(SR.Arg_NonZeroLowerBound, nameof(array)); } if (index < 0) { throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_NeedNonNegNum); } if (array.Length - index < _dictionary.Count) { throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); } TValue[] values = array as TValue[]; if (values != null) { CopyTo(values, index); } else { try { object[] objects = (object[])array; _dictionary._set.InOrderTreeWalk(delegate (TreeSet>.Node node) { objects[index++] = node.Item.Value; return true; }); } catch (ArrayTypeMismatchException) { throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); } } } public int Count { get { return _dictionary.Count; } } bool ICollection.IsReadOnly { get { return true; } } void ICollection.Add(TValue item) { throw new NotSupportedException(SR.NotSupported_ValueCollectionSet); } void ICollection.Clear() { throw new NotSupportedException(SR.NotSupported_ValueCollectionSet); } bool ICollection.Contains(TValue item) { return _dictionary.ContainsValue(item); } bool ICollection.Remove(TValue item) { throw new NotSupportedException(SR.NotSupported_ValueCollectionSet); } bool ICollection.IsSynchronized { get { return false; } } object ICollection.SyncRoot { get { return ((ICollection)_dictionary).SyncRoot; } } [SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes", Justification = "not an expected scenario")] public struct Enumerator : IEnumerator, IEnumerator { private SortedDictionary.Enumerator _dictEnum; internal Enumerator(SortedDictionary dictionary) { _dictEnum = dictionary.GetEnumerator(); } public void Dispose() { _dictEnum.Dispose(); } public bool MoveNext() { return _dictEnum.MoveNext(); } public TValue Current { get { return _dictEnum.Current.Value; } } object IEnumerator.Current { get { if (_dictEnum.NotStartedOrEnded) { throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); } return Current; } } void IEnumerator.Reset() { _dictEnum.Reset(); } } } [Serializable] public sealed class KeyValuePairComparer : Comparer> { internal IComparer keyComparer; // Do not rename (binary serialization) public KeyValuePairComparer(IComparer keyComparer) { if (keyComparer == null) { this.keyComparer = Comparer.Default; } else { this.keyComparer = keyComparer; } } public override int Compare(KeyValuePair x, KeyValuePair y) { return keyComparer.Compare(x.Key, y.Key); } } } /// /// This class is intended as a helper for backwards compatibility with existing SortedDictionaries. /// TreeSet has been converted into SortedSet, which will be exposed publicly. SortedDictionaries /// have the problem where they have already been serialized to disk as having a backing class named /// TreeSet. To ensure that we can read back anything that has already been written to disk, we need to /// make sure that we have a class named TreeSet that does everything the way it used to. /// /// The only thing that makes it different from SortedSet is that it throws on duplicates /// /// [Serializable] [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed class TreeSet : SortedSet { public TreeSet() : base() { } public TreeSet(IComparer comparer) : base(comparer) { } private TreeSet(SerializationInfo siInfo, StreamingContext context) : base(siInfo, context) { } internal override bool AddIfNotPresent(T item) { bool ret = base.AddIfNotPresent(item); if (!ret) { throw new ArgumentException(SR.Format(SR.Argument_AddingDuplicate, item)); } return ret; } } }