You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

319 lines
8.3 KiB

  1. #region License
  2. // Copyright (c) 2007 James Newton-King
  3. //
  4. // Permission is hereby granted, free of charge, to any person
  5. // obtaining a copy of this software and associated documentation
  6. // files (the "Software"), to deal in the Software without
  7. // restriction, including without limitation the rights to use,
  8. // copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the
  10. // Software is furnished to do so, subject to the following
  11. // conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be
  14. // included in all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  18. // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  20. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  21. // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  22. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  23. // OTHER DEALINGS IN THE SOFTWARE.
  24. #endregion
  25. using System;
  26. using System.Collections;
  27. using System.Collections.Generic;
  28. using System.Threading;
  29. using System.Globalization;
  30. #if NET20
  31. using Newtonsoft.Json.Utilities.LinqBridge;
  32. #else
  33. using System.Linq;
  34. #endif
  35. namespace Newtonsoft.Json.Utilities
  36. {
  37. internal interface IWrappedCollection : IList
  38. {
  39. object UnderlyingCollection { get; }
  40. }
  41. internal class CollectionWrapper<T> : ICollection<T>, IWrappedCollection
  42. {
  43. private readonly IList _list;
  44. private readonly ICollection<T> _genericCollection;
  45. private object _syncRoot;
  46. public CollectionWrapper(IList list)
  47. {
  48. ValidationUtils.ArgumentNotNull(list, nameof(list));
  49. if (list is ICollection<T> collection)
  50. {
  51. _genericCollection = collection;
  52. }
  53. else
  54. {
  55. _list = list;
  56. }
  57. }
  58. public CollectionWrapper(ICollection<T> list)
  59. {
  60. ValidationUtils.ArgumentNotNull(list, nameof(list));
  61. _genericCollection = list;
  62. }
  63. public virtual void Add(T item)
  64. {
  65. if (_genericCollection != null)
  66. {
  67. _genericCollection.Add(item);
  68. }
  69. else
  70. {
  71. _list.Add(item);
  72. }
  73. }
  74. public virtual void Clear()
  75. {
  76. if (_genericCollection != null)
  77. {
  78. _genericCollection.Clear();
  79. }
  80. else
  81. {
  82. _list.Clear();
  83. }
  84. }
  85. public virtual bool Contains(T item)
  86. {
  87. if (_genericCollection != null)
  88. {
  89. return _genericCollection.Contains(item);
  90. }
  91. else
  92. {
  93. return _list.Contains(item);
  94. }
  95. }
  96. public virtual void CopyTo(T[] array, int arrayIndex)
  97. {
  98. if (_genericCollection != null)
  99. {
  100. _genericCollection.CopyTo(array, arrayIndex);
  101. }
  102. else
  103. {
  104. _list.CopyTo(array, arrayIndex);
  105. }
  106. }
  107. public virtual int Count
  108. {
  109. get
  110. {
  111. if (_genericCollection != null)
  112. {
  113. return _genericCollection.Count;
  114. }
  115. else
  116. {
  117. return _list.Count;
  118. }
  119. }
  120. }
  121. public virtual bool IsReadOnly
  122. {
  123. get
  124. {
  125. if (_genericCollection != null)
  126. {
  127. return _genericCollection.IsReadOnly;
  128. }
  129. else
  130. {
  131. return _list.IsReadOnly;
  132. }
  133. }
  134. }
  135. public virtual bool Remove(T item)
  136. {
  137. if (_genericCollection != null)
  138. {
  139. return _genericCollection.Remove(item);
  140. }
  141. else
  142. {
  143. bool contains = _list.Contains(item);
  144. if (contains)
  145. {
  146. _list.Remove(item);
  147. }
  148. return contains;
  149. }
  150. }
  151. public virtual IEnumerator<T> GetEnumerator()
  152. {
  153. return (_genericCollection ?? _list.Cast<T>()).GetEnumerator();
  154. }
  155. IEnumerator IEnumerable.GetEnumerator()
  156. {
  157. return ((IEnumerable)_genericCollection ?? _list).GetEnumerator();
  158. }
  159. int IList.Add(object value)
  160. {
  161. VerifyValueType(value);
  162. Add((T)value);
  163. return (Count - 1);
  164. }
  165. bool IList.Contains(object value)
  166. {
  167. if (IsCompatibleObject(value))
  168. {
  169. return Contains((T)value);
  170. }
  171. return false;
  172. }
  173. int IList.IndexOf(object value)
  174. {
  175. if (_genericCollection != null)
  176. {
  177. throw new InvalidOperationException("Wrapped ICollection<T> does not support IndexOf.");
  178. }
  179. if (IsCompatibleObject(value))
  180. {
  181. return _list.IndexOf((T)value);
  182. }
  183. return -1;
  184. }
  185. void IList.RemoveAt(int index)
  186. {
  187. if (_genericCollection != null)
  188. {
  189. throw new InvalidOperationException("Wrapped ICollection<T> does not support RemoveAt.");
  190. }
  191. _list.RemoveAt(index);
  192. }
  193. void IList.Insert(int index, object value)
  194. {
  195. if (_genericCollection != null)
  196. {
  197. throw new InvalidOperationException("Wrapped ICollection<T> does not support Insert.");
  198. }
  199. VerifyValueType(value);
  200. _list.Insert(index, (T)value);
  201. }
  202. bool IList.IsFixedSize
  203. {
  204. get
  205. {
  206. if (_genericCollection != null)
  207. {
  208. // ICollection<T> only has IsReadOnly
  209. return _genericCollection.IsReadOnly;
  210. }
  211. else
  212. {
  213. return _list.IsFixedSize;
  214. }
  215. }
  216. }
  217. void IList.Remove(object value)
  218. {
  219. if (IsCompatibleObject(value))
  220. {
  221. Remove((T)value);
  222. }
  223. }
  224. object IList.this[int index]
  225. {
  226. get
  227. {
  228. if (_genericCollection != null)
  229. {
  230. throw new InvalidOperationException("Wrapped ICollection<T> does not support indexer.");
  231. }
  232. return _list[index];
  233. }
  234. set
  235. {
  236. if (_genericCollection != null)
  237. {
  238. throw new InvalidOperationException("Wrapped ICollection<T> does not support indexer.");
  239. }
  240. VerifyValueType(value);
  241. _list[index] = (T)value;
  242. }
  243. }
  244. void ICollection.CopyTo(Array array, int arrayIndex)
  245. {
  246. CopyTo((T[])array, arrayIndex);
  247. }
  248. bool ICollection.IsSynchronized => false;
  249. object ICollection.SyncRoot
  250. {
  251. get
  252. {
  253. if (_syncRoot == null)
  254. {
  255. Interlocked.CompareExchange(ref _syncRoot, new object(), null);
  256. }
  257. return _syncRoot;
  258. }
  259. }
  260. private static void VerifyValueType(object value)
  261. {
  262. if (!IsCompatibleObject(value))
  263. {
  264. throw new ArgumentException("The value '{0}' is not of type '{1}' and cannot be used in this generic collection.".FormatWith(CultureInfo.InvariantCulture, value, typeof(T)), nameof(value));
  265. }
  266. }
  267. private static bool IsCompatibleObject(object value)
  268. {
  269. if (!(value is T) && (value != null || (typeof(T).IsValueType() && !ReflectionUtils.IsNullableType(typeof(T)))))
  270. {
  271. return false;
  272. }
  273. return true;
  274. }
  275. public object UnderlyingCollection => (object)_genericCollection ?? _list;
  276. }
  277. }