PageRenderTime 40ms CodeModel.GetById 18ms app.highlight 16ms RepoModel.GetById 2ms 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
  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
 16#if !CLR2
 17using System.Linq.Expressions;
 18#else
 19using Microsoft.Scripting.Ast;
 20#endif
 21
 22using System;
 23using System.Collections;
 24using System.Collections.Generic;
 25using System.Diagnostics;
 26using Microsoft.Contracts;
 27using Microsoft.Scripting.Utils;
 28
 29namespace Microsoft.Scripting.Runtime {
 30
 31    /// <summary>
 32    /// Abstract base class used for optimized thread-safe dictionaries which have a set
 33    /// of pre-defined string keys.
 34    /// 
 35    /// Implementers derive from this class and override the GetExtraKeys, TrySetExtraValue, 
 36    /// and TryGetExtraValue methods. When looking up a value first the extra keys will be 
 37    /// searched using the optimized Try*ExtraValue functions.  If the value isn't found there
 38    /// then the value is stored in the underlying .NET dictionary.
 39    /// 
 40    /// This dictionary can store object values in addition to string values.  It also supports
 41    /// null keys.
 42    /// </summary>
 43    public abstract class CustomStringDictionary : 
 44#if CLR2
 45        IValueEquality,
 46#endif
 47        IDictionary, IDictionary<object, object> {
 48
 49        private Dictionary<object, object> _data;
 50        private static readonly object _nullObject = new object();
 51
 52        protected CustomStringDictionary() {
 53        }
 54
 55        /// <summary>
 56        /// Gets a list of the extra keys that are cached by the the optimized implementation
 57        /// of the module.
 58        /// </summary>
 59        public abstract string[] GetExtraKeys();
 60
 61        /// <summary>
 62        /// Try to set the extra value and return true if the specified key was found in the 
 63        /// list of extra values.
 64        /// </summary>
 65        protected internal abstract bool TrySetExtraValue(string key, object value);
 66
 67        /// <summary>
 68        /// Try to get the extra value and returns true if the specified key was found in the
 69        /// list of extra values.  Returns true even if the value is Uninitialized.
 70        /// </summary>
 71        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")]
 72        protected internal abstract bool TryGetExtraValue(string key, out object value);
 73
 74        private void InitializeData() {
 75            Debug.Assert(_data == null);
 76
 77            _data = new Dictionary<object, object>();
 78        }
 79
 80        #region IDictionary<object, object> Members
 81
 82        void IDictionary<object, object>.Add(object key, object value) {
 83            string strKey = key as string;
 84            if (strKey != null) {
 85                lock (this) {
 86                    if (_data == null) InitializeData();
 87                    if (TrySetExtraValue(strKey, value))
 88                        return;
 89                    _data.Add(strKey, value);
 90                }
 91            } else {
 92                AddObjectKey(key, value);
 93            }
 94        }
 95
 96        private void AddObjectKey(object key, object value) {
 97            if (_data == null) {
 98                InitializeData();
 99            }
100            _data[key] = value;
101        }
102
103        [Confined]
104        bool IDictionary<object, object>.ContainsKey(object key) {
105            lock (this) {
106                if (_data == null) {
107                    return false;
108                }
109                object dummy;
110                return _data.TryGetValue(key, out dummy);
111            }
112        }
113
114        public ICollection<object> Keys {
115            get {
116                List<object> res = new List<object>();
117                lock (this) {
118                    if (_data != null) {
119                        res.AddRange(_data.Keys);
120                    }
121                }
122
123                foreach (var key in GetExtraKeys()) {
124                    object dummy;
125                    if (TryGetExtraValue(key, out dummy) && dummy != Uninitialized.Instance) {
126                        res.Add(key);
127                    }
128                }
129
130                return res;
131            }
132        }
133
134        bool IDictionary<object, object>.Remove(object key) {
135            string strKey = key as string;
136            if (strKey != null) {
137                lock (this) {
138                    if (TrySetExtraValue(strKey, Uninitialized.Instance)) return true;
139
140                    if (_data == null) return false;
141                    return _data.Remove(strKey);
142                }
143            }
144
145            return RemoveObjectKey(key);
146        }
147
148        private bool RemoveObjectKey(object key) {
149            return _data.Remove(key);
150        }
151
152        public bool TryGetValue(object key, out object value) {
153            string strKey = key as string;
154            if (strKey != null) {
155                lock (this) {
156                    if (TryGetExtraValue(strKey, out value) && value != Uninitialized.Instance) return true;
157
158                    if (_data == null) return false;
159                    return _data.TryGetValue(strKey, out value);
160                }
161            }
162
163            return TryGetObjectValue(key, out value);
164        }
165
166        private bool TryGetObjectValue(object key, out object value) {
167            if (_data == null) {
168                value = null;
169                return false;
170            }
171            return _data.TryGetValue(key, out value);
172        }
173
174        ICollection<object> IDictionary<object, object>.Values {
175            get {
176                List<object> res = new List<object>();
177                lock (this) {
178                    if (_data != null) {
179                        res.AddRange(_data.Values);
180                    }
181                }
182
183                foreach (var key in GetExtraKeys()) {
184                    object value;
185                    if (TryGetExtraValue(key, out value) && value != Uninitialized.Instance) {
186                        res.Add(value);
187                    }
188                }
189
190                return res;
191            }
192        }
193
194        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")]
195        public object this[object key] {
196            get {
197                string strKey = key as string;
198                object res;
199                if (strKey != null) {
200                    lock (this) {
201                        if (TryGetExtraValue(strKey, out res) && !(res is Uninitialized)) return res;
202
203                        if (_data == null) {
204                            throw new KeyNotFoundException(strKey);
205                        }
206
207                        return _data[strKey];
208                    }
209                }
210
211                if (TryGetObjectValue(key, out res))
212                    return res;
213
214                throw new KeyNotFoundException(key.ToString());
215            }
216            set {
217                string strKey = key as string;
218                if (strKey != null) {
219                    lock (this) {
220                        if (TrySetExtraValue(strKey, value)) return;
221
222                        if (_data == null) InitializeData();
223                        _data[strKey] = value;
224                    }
225                } else {
226                    AddObjectKey(key, value);
227                }
228            }
229        }
230
231        #endregion
232
233        #region ICollection<KeyValuePair<string,object>> Members
234
235        public void Add(KeyValuePair<object, object> item) {
236            throw new NotImplementedException();
237        }
238
239        public void Clear() {
240            lock (this) {
241                foreach (var key in GetExtraKeys()) {
242                    TrySetExtraValue(key, Uninitialized.Instance);
243                }
244                _data = null;
245            }
246        }
247
248        [Confined]
249        public bool Contains(KeyValuePair<object, object> item) {
250            throw new NotImplementedException();
251        }
252
253        public void CopyTo(KeyValuePair<object, object>[] array, int arrayIndex) {
254            ContractUtils.RequiresNotNull(array, "array");
255            ContractUtils.RequiresArrayRange(array, arrayIndex, Count, "araryIndex", "Count");
256
257            foreach (KeyValuePair<object, object> kvp in ((IEnumerable<KeyValuePair<object, object>>)this)) {
258                array[arrayIndex++] = kvp;
259            }
260        }
261
262        public int Count {
263            get {
264                int count = _data != null ? _data.Count : 0;
265
266                lock (this) {
267                    foreach (var key in GetExtraKeys()) {
268                        object dummy;
269                        if (TryGetExtraValue(key, out dummy) && dummy != Uninitialized.Instance) count++;
270                    }
271                }
272
273                return count;
274            }
275        }
276
277        public bool IsReadOnly {
278            get { return false; }
279        }
280
281        public bool Remove(KeyValuePair<object, object> item) {
282            throw new NotImplementedException();
283        }
284
285        public bool Remove(object key) {
286            string strKey = key as string;
287            if (strKey != null) {
288                if (TrySetExtraValue(strKey, Uninitialized.Instance)) {
289                    return true;
290                }
291
292                lock (this) {
293                    if (_data != null) {
294                        return _data.Remove(strKey);
295                    }
296                    return false;
297                }
298            } else {
299                return RemoveObjectKey(key);
300            }
301
302        }
303
304        #endregion
305
306        #region IEnumerable<KeyValuePair<object,object>> Members
307
308        [Pure]
309        IEnumerator<KeyValuePair<object, object>> IEnumerable<KeyValuePair<object, object>>.GetEnumerator() {
310            if (_data != null) {
311                foreach (KeyValuePair<object, object> o in _data) {
312                    yield return o;
313                }
314            }
315
316            foreach (var o in GetExtraKeys()) {
317                object val;
318                if (TryGetExtraValue(o, out val) && val != Uninitialized.Instance) {
319                    yield return new KeyValuePair<object, object>(o, val);
320                }
321            }
322        }
323
324        #endregion
325
326        #region IEnumerable Members
327
328        [Pure]
329        public System.Collections.IEnumerator GetEnumerator() {
330            List<object> l = new List<object>(this.Keys);
331            for (int i = 0; i < l.Count; i++) {
332                object baseVal = l[i];
333                object nullVal = l[i] = CustomStringDictionary.ObjToNull(l[i]);
334                if (baseVal != nullVal) {
335                    // we've transformed null, stop looking
336                    break;
337                }
338            }
339            return l.GetEnumerator();
340        }
341
342        #endregion
343
344        #region IDictionary Members
345
346        [Pure]
347        void IDictionary.Add(object key, object value) {
348            ((IDictionary<object, object>)this).Add(key, value);
349        }
350
351        [Pure]
352        public bool Contains(object key) {
353            object dummy;
354            return ((IDictionary<object, object>)this).TryGetValue(key, out dummy);
355        }
356
357        [Pure]
358        IDictionaryEnumerator IDictionary.GetEnumerator() {
359            List<IDictionaryEnumerator> enums = new List<IDictionaryEnumerator>();
360
361            enums.Add(new ExtraKeyEnumerator(this));
362
363            if (_data != null) enums.Add(((IDictionary)_data).GetEnumerator());
364
365            return new DictionaryUnionEnumerator(enums);
366        }
367
368        public bool IsFixedSize {
369            get { return false; }
370        }
371
372        ICollection IDictionary.Keys {
373            get {
374                return new List<object>(((IDictionary<object, object>)this).Keys);
375            }
376        }
377
378        void IDictionary.Remove(object key) {
379            Remove(key);
380        }
381
382        ICollection IDictionary.Values {
383            get {
384                return new List<object>(((IDictionary<object, object>)this).Values);
385            }
386        }
387
388        #endregion
389
390        #region IValueEquality Members
391
392        public int GetValueHashCode() {
393            throw Error.DictionaryNotHashable();
394        }
395
396        public virtual bool ValueEquals(object other) {
397            if (Object.ReferenceEquals(this, other)) return true;
398
399            IDictionary<object, object> oth = other as IDictionary<object, object>;
400            IDictionary<object, object> ths = this as IDictionary<object, object>;
401            if (oth == null) return false;
402
403            if (oth.Count != ths.Count) return false;
404
405            foreach (KeyValuePair<object, object> o in ths) {
406                object res;
407                if (!oth.TryGetValue(o.Key, out res))
408                    return false;
409#if CLR2
410                IValueEquality ve = res as IValueEquality;
411                if (ve != null) {
412                    if (!ve.ValueEquals(o.Value)) return false;
413                } else if ((ve = (o.Value as IValueEquality)) != null) {
414                    if (!ve.Equals(res)) return false;
415                } else
416#endif
417                    if (res != null) {
418                        if (!res.Equals(o.Value)) return false;
419                    } else if (o.Value != null) {
420                        if (!o.Value.Equals(res)) return false;
421                    } // else both null and are equal
422            }
423            return true;
424        }
425
426        #endregion          
427      
428        public void CopyTo(Array array, int index) {
429            throw Error.MethodOrOperatorNotImplemented();
430        }
431
432        public bool IsSynchronized {
433            get {
434                return true;
435            }
436        }
437
438        public object SyncRoot {
439            get {
440                // TODO: Sync root shouldn't be this, it should be data.
441                return this;
442            }
443        }
444
445        public static object NullToObj(object o) {
446            if (o == null) return _nullObject;
447            return o;
448        }
449
450        public static object ObjToNull(object o) {
451            if (o == _nullObject) return null;
452            return o;
453        }
454
455        public static bool IsNullObject(object o) {
456            return o == _nullObject;
457        }
458    }
459
460    [Obsolete("Derive directly from CustomStringDictionary instead")]
461    public abstract class CustomSymbolDictionary : CustomStringDictionary {
462    }
463}