#region License // Copyright (c) 2007 James Newton-King // // 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. #endregion using System; using System.Collections.Generic; using System.Collections; using System.Threading; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Diagnostics.CodeAnalysis; #if !HAVE_LINQ using LC.Newtonsoft.Json.Utilities.LinqBridge; #else using System.Linq; #endif namespace LC.Newtonsoft.Json.Utilities { internal interface IWrappedDictionary : IDictionary { object UnderlyingDictionary { get; } } internal class DictionaryWrapper : IDictionary, IWrappedDictionary { private readonly IDictionary? _dictionary; private readonly IDictionary? _genericDictionary; #if HAVE_READ_ONLY_COLLECTIONS private readonly IReadOnlyDictionary? _readOnlyDictionary; #endif private object? _syncRoot; public DictionaryWrapper(IDictionary dictionary) { ValidationUtils.ArgumentNotNull(dictionary, nameof(dictionary)); _dictionary = dictionary; } public DictionaryWrapper(IDictionary dictionary) { ValidationUtils.ArgumentNotNull(dictionary, nameof(dictionary)); _genericDictionary = dictionary; } #if HAVE_READ_ONLY_COLLECTIONS public DictionaryWrapper(IReadOnlyDictionary dictionary) { ValidationUtils.ArgumentNotNull(dictionary, nameof(dictionary)); _readOnlyDictionary = dictionary; } #endif internal IDictionary GenericDictionary { get { MiscellaneousUtils.Assert(_genericDictionary != null); return _genericDictionary; } } public void Add(TKey key, TValue value) { if (_dictionary != null) { _dictionary.Add(key, value); } else if (_genericDictionary != null) { _genericDictionary.Add(key, value); } else { throw new NotSupportedException(); } } public bool ContainsKey(TKey key) { if (_dictionary != null) { return _dictionary.Contains(key); } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { return _readOnlyDictionary.ContainsKey(key); } #endif else { return GenericDictionary.ContainsKey(key); } } public ICollection Keys { get { if (_dictionary != null) { return _dictionary.Keys.Cast().ToList(); } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { return _readOnlyDictionary.Keys.ToList(); } #endif else { return GenericDictionary.Keys; } } } public bool Remove(TKey key) { if (_dictionary != null) { if (_dictionary.Contains(key)) { _dictionary.Remove(key); return true; } else { return false; } } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { throw new NotSupportedException(); } #endif else { return GenericDictionary.Remove(key); } } #pragma warning disable CS8767 // Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). public bool TryGetValue(TKey key, out TValue? value) #pragma warning restore CS8767 // Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). { if (_dictionary != null) { if (!_dictionary.Contains(key)) { #pragma warning disable CS8653 // A default expression introduces a null value for a type parameter. value = default; #pragma warning restore CS8653 // A default expression introduces a null value for a type parameter. return false; } else { value = (TValue)_dictionary[key]; return true; } } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { throw new NotSupportedException(); } #endif else { return GenericDictionary.TryGetValue(key, out value); } } public ICollection Values { get { if (_dictionary != null) { return _dictionary.Values.Cast().ToList(); } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { return _readOnlyDictionary.Values.ToList(); } #endif else { return GenericDictionary.Values; } } } public TValue this[TKey key] { get { if (_dictionary != null) { return (TValue)_dictionary[key]; } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { return _readOnlyDictionary[key]; } #endif else { return GenericDictionary[key]; } } set { if (_dictionary != null) { _dictionary[key] = value; } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { throw new NotSupportedException(); } #endif else { GenericDictionary[key] = value; } } } public void Add(KeyValuePair item) { if (_dictionary != null) { ((IList)_dictionary).Add(item); } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { throw new NotSupportedException(); } #endif else { _genericDictionary?.Add(item); } } public void Clear() { if (_dictionary != null) { _dictionary.Clear(); } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { throw new NotSupportedException(); } #endif else { GenericDictionary.Clear(); } } public bool Contains(KeyValuePair item) { if (_dictionary != null) { return ((IList)_dictionary).Contains(item); } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { return _readOnlyDictionary.Contains(item); } #endif else { return GenericDictionary.Contains(item); } } public void CopyTo(KeyValuePair[] array, int arrayIndex) { if (_dictionary != null) { // Manual use of IDictionaryEnumerator instead of foreach to avoid DictionaryEntry box allocations. IDictionaryEnumerator e = _dictionary.GetEnumerator(); try { while (e.MoveNext()) { DictionaryEntry entry = e.Entry; array[arrayIndex++] = new KeyValuePair((TKey)entry.Key, (TValue)entry.Value); } } finally { (e as IDisposable)?.Dispose(); } } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { throw new NotSupportedException(); } #endif else { GenericDictionary.CopyTo(array, arrayIndex); } } public int Count { get { if (_dictionary != null) { return _dictionary.Count; } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { return _readOnlyDictionary.Count; } #endif else { return GenericDictionary.Count; } } } public bool IsReadOnly { get { if (_dictionary != null) { return _dictionary.IsReadOnly; } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { return true; } #endif else { return GenericDictionary.IsReadOnly; } } } public bool Remove(KeyValuePair item) { if (_dictionary != null) { if (_dictionary.Contains(item.Key)) { object value = _dictionary[item.Key]; if (Equals(value, item.Value)) { _dictionary.Remove(item.Key); return true; } else { return false; } } else { return true; } } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { throw new NotSupportedException(); } #endif else { return GenericDictionary.Remove(item); } } public IEnumerator> GetEnumerator() { if (_dictionary != null) { return _dictionary.Cast().Select(de => new KeyValuePair((TKey)de.Key, (TValue)de.Value)).GetEnumerator(); } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { return _readOnlyDictionary.GetEnumerator(); } #endif else { return GenericDictionary.GetEnumerator(); } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } void IDictionary.Add(object key, object value) { if (_dictionary != null) { _dictionary.Add(key, value); } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { throw new NotSupportedException(); } #endif else { GenericDictionary.Add((TKey)key, (TValue)value); } } object? IDictionary.this[object key] { get { if (_dictionary != null) { return _dictionary[key]; } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { return _readOnlyDictionary[(TKey)key]; } #endif else { return GenericDictionary[(TKey)key]; } } set { if (_dictionary != null) { _dictionary[key] = value; } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { throw new NotSupportedException(); } #endif else { // Consider changing this code to call GenericDictionary.Remove when value is null. // #pragma warning disable CS8601 // Possible null reference assignment. #pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. GenericDictionary[(TKey)key] = (TValue)value; #pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. #pragma warning restore CS8601 // Possible null reference assignment. } } } private readonly struct DictionaryEnumerator : IDictionaryEnumerator { private readonly IEnumerator> _e; public DictionaryEnumerator(IEnumerator> e) { ValidationUtils.ArgumentNotNull(e, nameof(e)); _e = e; } public DictionaryEntry Entry => (DictionaryEntry)Current; public object Key => Entry.Key; public object Value => Entry.Value; public object Current => new DictionaryEntry(_e.Current.Key, _e.Current.Value); public bool MoveNext() { return _e.MoveNext(); } public void Reset() { _e.Reset(); } } IDictionaryEnumerator IDictionary.GetEnumerator() { if (_dictionary != null) { return _dictionary.GetEnumerator(); } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { return new DictionaryEnumerator(_readOnlyDictionary.GetEnumerator()); } #endif else { return new DictionaryEnumerator(GenericDictionary.GetEnumerator()); } } bool IDictionary.Contains(object key) { if (_genericDictionary != null) { return _genericDictionary.ContainsKey((TKey)key); } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { return _readOnlyDictionary.ContainsKey((TKey)key); } #endif else { return _dictionary!.Contains(key); } } bool IDictionary.IsFixedSize { get { if (_genericDictionary != null) { return false; } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { return true; } #endif else { return _dictionary!.IsFixedSize; } } } ICollection IDictionary.Keys { get { if (_genericDictionary != null) { return _genericDictionary.Keys.ToList(); } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { return _readOnlyDictionary.Keys.ToList(); } #endif else { return _dictionary!.Keys; } } } public void Remove(object key) { if (_dictionary != null) { _dictionary.Remove(key); } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { throw new NotSupportedException(); } #endif else { GenericDictionary.Remove((TKey)key); } } ICollection IDictionary.Values { get { if (_genericDictionary != null) { return _genericDictionary.Values.ToList(); } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { return _readOnlyDictionary.Values.ToList(); } #endif else { return _dictionary!.Values; } } } void ICollection.CopyTo(Array array, int index) { if (_dictionary != null) { _dictionary.CopyTo(array, index); } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { throw new NotSupportedException(); } #endif else { GenericDictionary.CopyTo((KeyValuePair[])array, index); } } bool ICollection.IsSynchronized { get { if (_dictionary != null) { return _dictionary.IsSynchronized; } else { return false; } } } object ICollection.SyncRoot { get { if (_syncRoot == null) { Interlocked.CompareExchange(ref _syncRoot, new object(), null); } return _syncRoot; } } public object UnderlyingDictionary { get { if (_dictionary != null) { return _dictionary; } #if HAVE_READ_ONLY_COLLECTIONS else if (_readOnlyDictionary != null) { return _readOnlyDictionary; } #endif else { return GenericDictionary; } } } } }