PageRenderTime 49ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/IronPython_Main/Runtime/Microsoft.Dynamic/Runtime/CustomStringDictionary.cs

#
C# | 463 lines | 339 code | 84 blank | 40 comment | 93 complexity | 19b51d361693380208248b3d9fb280a4 MD5 | raw file
Possible License(s): GPL-2.0, MPL-2.0-no-copyleft-exception, CPL-1.0, CC-BY-SA-3.0, BSD-3-Clause, ISC, AGPL-3.0, LGPL-2.1, Apache-2.0
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Apache License, Version 2.0, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. #if !CLR2
  16. using System.Linq.Expressions;
  17. #else
  18. using Microsoft.Scripting.Ast;
  19. #endif
  20. using System;
  21. using System.Collections;
  22. using System.Collections.Generic;
  23. using System.Diagnostics;
  24. using Microsoft.Contracts;
  25. using Microsoft.Scripting.Utils;
  26. namespace Microsoft.Scripting.Runtime {
  27. /// <summary>
  28. /// Abstract base class used for optimized thread-safe dictionaries which have a set
  29. /// of pre-defined string keys.
  30. ///
  31. /// Implementers derive from this class and override the GetExtraKeys, TrySetExtraValue,
  32. /// and TryGetExtraValue methods. When looking up a value first the extra keys will be
  33. /// searched using the optimized Try*ExtraValue functions. If the value isn't found there
  34. /// then the value is stored in the underlying .NET dictionary.
  35. ///
  36. /// This dictionary can store object values in addition to string values. It also supports
  37. /// null keys.
  38. /// </summary>
  39. public abstract class CustomStringDictionary :
  40. #if CLR2
  41. IValueEquality,
  42. #endif
  43. IDictionary, IDictionary<object, object> {
  44. private Dictionary<object, object> _data;
  45. private static readonly object _nullObject = new object();
  46. protected CustomStringDictionary() {
  47. }
  48. /// <summary>
  49. /// Gets a list of the extra keys that are cached by the the optimized implementation
  50. /// of the module.
  51. /// </summary>
  52. public abstract string[] GetExtraKeys();
  53. /// <summary>
  54. /// Try to set the extra value and return true if the specified key was found in the
  55. /// list of extra values.
  56. /// </summary>
  57. protected internal abstract bool TrySetExtraValue(string key, object value);
  58. /// <summary>
  59. /// Try to get the extra value and returns true if the specified key was found in the
  60. /// list of extra values. Returns true even if the value is Uninitialized.
  61. /// </summary>
  62. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")]
  63. protected internal abstract bool TryGetExtraValue(string key, out object value);
  64. private void InitializeData() {
  65. Debug.Assert(_data == null);
  66. _data = new Dictionary<object, object>();
  67. }
  68. #region IDictionary<object, object> Members
  69. void IDictionary<object, object>.Add(object key, object value) {
  70. string strKey = key as string;
  71. if (strKey != null) {
  72. lock (this) {
  73. if (_data == null) InitializeData();
  74. if (TrySetExtraValue(strKey, value))
  75. return;
  76. _data.Add(strKey, value);
  77. }
  78. } else {
  79. AddObjectKey(key, value);
  80. }
  81. }
  82. private void AddObjectKey(object key, object value) {
  83. if (_data == null) {
  84. InitializeData();
  85. }
  86. _data[key] = value;
  87. }
  88. [Confined]
  89. bool IDictionary<object, object>.ContainsKey(object key) {
  90. lock (this) {
  91. if (_data == null) {
  92. return false;
  93. }
  94. object dummy;
  95. return _data.TryGetValue(key, out dummy);
  96. }
  97. }
  98. public ICollection<object> Keys {
  99. get {
  100. List<object> res = new List<object>();
  101. lock (this) {
  102. if (_data != null) {
  103. res.AddRange(_data.Keys);
  104. }
  105. }
  106. foreach (var key in GetExtraKeys()) {
  107. object dummy;
  108. if (TryGetExtraValue(key, out dummy) && dummy != Uninitialized.Instance) {
  109. res.Add(key);
  110. }
  111. }
  112. return res;
  113. }
  114. }
  115. bool IDictionary<object, object>.Remove(object key) {
  116. string strKey = key as string;
  117. if (strKey != null) {
  118. lock (this) {
  119. if (TrySetExtraValue(strKey, Uninitialized.Instance)) return true;
  120. if (_data == null) return false;
  121. return _data.Remove(strKey);
  122. }
  123. }
  124. return RemoveObjectKey(key);
  125. }
  126. private bool RemoveObjectKey(object key) {
  127. return _data.Remove(key);
  128. }
  129. public bool TryGetValue(object key, out object value) {
  130. string strKey = key as string;
  131. if (strKey != null) {
  132. lock (this) {
  133. if (TryGetExtraValue(strKey, out value) && value != Uninitialized.Instance) return true;
  134. if (_data == null) return false;
  135. return _data.TryGetValue(strKey, out value);
  136. }
  137. }
  138. return TryGetObjectValue(key, out value);
  139. }
  140. private bool TryGetObjectValue(object key, out object value) {
  141. if (_data == null) {
  142. value = null;
  143. return false;
  144. }
  145. return _data.TryGetValue(key, out value);
  146. }
  147. ICollection<object> IDictionary<object, object>.Values {
  148. get {
  149. List<object> res = new List<object>();
  150. lock (this) {
  151. if (_data != null) {
  152. res.AddRange(_data.Values);
  153. }
  154. }
  155. foreach (var key in GetExtraKeys()) {
  156. object value;
  157. if (TryGetExtraValue(key, out value) && value != Uninitialized.Instance) {
  158. res.Add(value);
  159. }
  160. }
  161. return res;
  162. }
  163. }
  164. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")]
  165. public object this[object key] {
  166. get {
  167. string strKey = key as string;
  168. object res;
  169. if (strKey != null) {
  170. lock (this) {
  171. if (TryGetExtraValue(strKey, out res) && !(res is Uninitialized)) return res;
  172. if (_data == null) {
  173. throw new KeyNotFoundException(strKey);
  174. }
  175. return _data[strKey];
  176. }
  177. }
  178. if (TryGetObjectValue(key, out res))
  179. return res;
  180. throw new KeyNotFoundException(key.ToString());
  181. }
  182. set {
  183. string strKey = key as string;
  184. if (strKey != null) {
  185. lock (this) {
  186. if (TrySetExtraValue(strKey, value)) return;
  187. if (_data == null) InitializeData();
  188. _data[strKey] = value;
  189. }
  190. } else {
  191. AddObjectKey(key, value);
  192. }
  193. }
  194. }
  195. #endregion
  196. #region ICollection<KeyValuePair<string,object>> Members
  197. public void Add(KeyValuePair<object, object> item) {
  198. throw new NotImplementedException();
  199. }
  200. public void Clear() {
  201. lock (this) {
  202. foreach (var key in GetExtraKeys()) {
  203. TrySetExtraValue(key, Uninitialized.Instance);
  204. }
  205. _data = null;
  206. }
  207. }
  208. [Confined]
  209. public bool Contains(KeyValuePair<object, object> item) {
  210. throw new NotImplementedException();
  211. }
  212. public void CopyTo(KeyValuePair<object, object>[] array, int arrayIndex) {
  213. ContractUtils.RequiresNotNull(array, "array");
  214. ContractUtils.RequiresArrayRange(array, arrayIndex, Count, "araryIndex", "Count");
  215. foreach (KeyValuePair<object, object> kvp in ((IEnumerable<KeyValuePair<object, object>>)this)) {
  216. array[arrayIndex++] = kvp;
  217. }
  218. }
  219. public int Count {
  220. get {
  221. int count = _data != null ? _data.Count : 0;
  222. lock (this) {
  223. foreach (var key in GetExtraKeys()) {
  224. object dummy;
  225. if (TryGetExtraValue(key, out dummy) && dummy != Uninitialized.Instance) count++;
  226. }
  227. }
  228. return count;
  229. }
  230. }
  231. public bool IsReadOnly {
  232. get { return false; }
  233. }
  234. public bool Remove(KeyValuePair<object, object> item) {
  235. throw new NotImplementedException();
  236. }
  237. public bool Remove(object key) {
  238. string strKey = key as string;
  239. if (strKey != null) {
  240. if (TrySetExtraValue(strKey, Uninitialized.Instance)) {
  241. return true;
  242. }
  243. lock (this) {
  244. if (_data != null) {
  245. return _data.Remove(strKey);
  246. }
  247. return false;
  248. }
  249. } else {
  250. return RemoveObjectKey(key);
  251. }
  252. }
  253. #endregion
  254. #region IEnumerable<KeyValuePair<object,object>> Members
  255. [Pure]
  256. IEnumerator<KeyValuePair<object, object>> IEnumerable<KeyValuePair<object, object>>.GetEnumerator() {
  257. if (_data != null) {
  258. foreach (KeyValuePair<object, object> o in _data) {
  259. yield return o;
  260. }
  261. }
  262. foreach (var o in GetExtraKeys()) {
  263. object val;
  264. if (TryGetExtraValue(o, out val) && val != Uninitialized.Instance) {
  265. yield return new KeyValuePair<object, object>(o, val);
  266. }
  267. }
  268. }
  269. #endregion
  270. #region IEnumerable Members
  271. [Pure]
  272. public System.Collections.IEnumerator GetEnumerator() {
  273. List<object> l = new List<object>(this.Keys);
  274. for (int i = 0; i < l.Count; i++) {
  275. object baseVal = l[i];
  276. object nullVal = l[i] = CustomStringDictionary.ObjToNull(l[i]);
  277. if (baseVal != nullVal) {
  278. // we've transformed null, stop looking
  279. break;
  280. }
  281. }
  282. return l.GetEnumerator();
  283. }
  284. #endregion
  285. #region IDictionary Members
  286. [Pure]
  287. void IDictionary.Add(object key, object value) {
  288. ((IDictionary<object, object>)this).Add(key, value);
  289. }
  290. [Pure]
  291. public bool Contains(object key) {
  292. object dummy;
  293. return ((IDictionary<object, object>)this).TryGetValue(key, out dummy);
  294. }
  295. [Pure]
  296. IDictionaryEnumerator IDictionary.GetEnumerator() {
  297. List<IDictionaryEnumerator> enums = new List<IDictionaryEnumerator>();
  298. enums.Add(new ExtraKeyEnumerator(this));
  299. if (_data != null) enums.Add(((IDictionary)_data).GetEnumerator());
  300. return new DictionaryUnionEnumerator(enums);
  301. }
  302. public bool IsFixedSize {
  303. get { return false; }
  304. }
  305. ICollection IDictionary.Keys {
  306. get {
  307. return new List<object>(((IDictionary<object, object>)this).Keys);
  308. }
  309. }
  310. void IDictionary.Remove(object key) {
  311. Remove(key);
  312. }
  313. ICollection IDictionary.Values {
  314. get {
  315. return new List<object>(((IDictionary<object, object>)this).Values);
  316. }
  317. }
  318. #endregion
  319. #region IValueEquality Members
  320. public int GetValueHashCode() {
  321. throw Error.DictionaryNotHashable();
  322. }
  323. public virtual bool ValueEquals(object other) {
  324. if (Object.ReferenceEquals(this, other)) return true;
  325. IDictionary<object, object> oth = other as IDictionary<object, object>;
  326. IDictionary<object, object> ths = this as IDictionary<object, object>;
  327. if (oth == null) return false;
  328. if (oth.Count != ths.Count) return false;
  329. foreach (KeyValuePair<object, object> o in ths) {
  330. object res;
  331. if (!oth.TryGetValue(o.Key, out res))
  332. return false;
  333. #if CLR2
  334. IValueEquality ve = res as IValueEquality;
  335. if (ve != null) {
  336. if (!ve.ValueEquals(o.Value)) return false;
  337. } else if ((ve = (o.Value as IValueEquality)) != null) {
  338. if (!ve.Equals(res)) return false;
  339. } else
  340. #endif
  341. if (res != null) {
  342. if (!res.Equals(o.Value)) return false;
  343. } else if (o.Value != null) {
  344. if (!o.Value.Equals(res)) return false;
  345. } // else both null and are equal
  346. }
  347. return true;
  348. }
  349. #endregion
  350. public void CopyTo(Array array, int index) {
  351. throw Error.MethodOrOperatorNotImplemented();
  352. }
  353. public bool IsSynchronized {
  354. get {
  355. return true;
  356. }
  357. }
  358. public object SyncRoot {
  359. get {
  360. // TODO: Sync root shouldn't be this, it should be data.
  361. return this;
  362. }
  363. }
  364. public static object NullToObj(object o) {
  365. if (o == null) return _nullObject;
  366. return o;
  367. }
  368. public static object ObjToNull(object o) {
  369. if (o == _nullObject) return null;
  370. return o;
  371. }
  372. public static bool IsNullObject(object o) {
  373. return o == _nullObject;
  374. }
  375. }
  376. [Obsolete("Derive directly from CustomStringDictionary instead")]
  377. public abstract class CustomSymbolDictionary : CustomStringDictionary {
  378. }
  379. }