The Labs \ Source Viewer \ SSCLI \ System.Collections.Specialized \ OrderedDictionaryEnumerator

  1. //------------------------------------------------------------------------------
  2. // <copyright file="OrderedDictionary.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. namespace System.Collections.Specialized
  16. {
  17.    
  18.     using System;
  19.     using System.Collections;
  20.     using System.Runtime.Serialization;
  21.     using System.Security.Permissions;
  22.    
  23.     /// <devdoc>
  24.     /// <para>
  25.     /// OrderedDictionary offers IDictionary syntax with ordering. Objects
  26.     /// added or inserted in an IOrderedDictionary must have both a key and an index, and
  27.     /// can be retrieved by either.
  28.     /// OrderedDictionary is used by the ParameterCollection because MSAccess relies on ordering of
  29.     /// parameters, while almost all other DBs do not. DataKeyArray also uses it so
  30.     /// DataKeys can be retrieved by either their name or their index.
  31.     ///
  32.     /// OrderedDictionary implements IDeserializationCallback because it needs to have the
  33.     /// contained ArrayList and Hashtable deserialized before it tries to get its count and objects.
  34.     /// </para>
  35.     /// </devdoc>
  36.     [Serializable()]
  37.     public class OrderedDictionary : IOrderedDictionary, ISerializable, IDeserializationCallback
  38.     {
  39.        
  40.         private ArrayList _objectsArray;
  41.         private Hashtable _objectsTable;
  42.         private int _initialCapacity;
  43.         private IEqualityComparer _comparer;
  44.         private bool _readOnly;
  45.         private object _syncRoot;
  46.         private SerializationInfo _siInfo;
  47.         //A temporary variable which we need during deserialization.
  48.         private const string KeyComparerName = "KeyComparer";
  49.         private const string ArrayListName = "ArrayList";
  50.         private const string ReadOnlyName = "ReadOnly";
  51.         private const string InitCapacityName = "InitialCapacity";
  52.        
  53.         public OrderedDictionary() : this(0)
  54.         {
  55.         }
  56.        
  57.         public OrderedDictionary(int capacity) : this(capacity, null)
  58.         {
  59.         }
  60.        
  61.         public OrderedDictionary(IEqualityComparer comparer) : this(0, comparer)
  62.         {
  63.         }
  64.        
  65.         public OrderedDictionary(int capacity, IEqualityComparer comparer)
  66.         {
  67.             _initialCapacity = capacity;
  68.             _comparer = comparer;
  69.         }
  70.        
  71.         private OrderedDictionary(OrderedDictionary dictionary)
  72.         {
  73.             if (dictionary == null) {
  74.                 throw new ArgumentNullException("dictionary");
  75.             }
  76.            
  77.             _readOnly = true;
  78.             _objectsArray = dictionary._objectsArray;
  79.             _objectsTable = dictionary._objectsTable;
  80.             _comparer = dictionary._comparer;
  81.             _initialCapacity = dictionary._initialCapacity;
  82.         }
  83.        
  84.         protected OrderedDictionary(SerializationInfo info, StreamingContext context)
  85.         {
  86.             // We can't do anything with the keys and values until the entire graph has been deserialized
  87.             // and getting Counts and objects won't fail. For the time being, we'll just cache this.
  88.             // The graph is not valid until OnDeserialization has been called.
  89.             _siInfo = info;
  90.         }
  91.        
  92.         /// <devdoc>
  93.         /// Gets the size of the table.
  94.         /// </devdoc>
  95.         public int Count {
  96.             get { return objectsArray.Count; }
  97.         }
  98.        
  99.         /// <devdoc>
  100.         /// Indicates that the collection can grow.
  101.         /// </devdoc>
  102.         bool IDictionary.IsFixedSize {
  103.             get { return _readOnly; }
  104.         }
  105.        
  106.         /// <devdoc>
  107.         /// Indicates that the collection is not read-only
  108.         /// </devdoc>
  109.         public bool IsReadOnly {
  110.             get { return _readOnly; }
  111.         }
  112.        
  113.         /// <devdoc>
  114.         /// Indicates that this class is not synchronized
  115.         /// </devdoc>
  116.         bool ICollection.IsSynchronized {
  117.             get { return false; }
  118.         }
  119.        
  120.         /// <devdoc>
  121.         /// Gets the collection of keys in the table in order.
  122.         /// </devdoc>
  123.         public ICollection Keys {
  124.             get { return new OrderedDictionaryKeyValueCollection(objectsArray, true); }
  125.         }
  126.        
  127.         private ArrayList objectsArray {
  128.             get {
  129.                 if (_objectsArray == null) {
  130.                     _objectsArray = new ArrayList(_initialCapacity);
  131.                 }
  132.                 return _objectsArray;
  133.             }
  134.         }
  135.        
  136.         private Hashtable objectsTable {
  137.             get {
  138.                 if (_objectsTable == null) {
  139.                     _objectsTable = new Hashtable(_initialCapacity, _comparer);
  140.                 }
  141.                 return _objectsTable;
  142.             }
  143.         }
  144.        
  145.         /// <devdoc>
  146.         /// The SyncRoot object. Not used because IsSynchronized is false
  147.         /// </devdoc>
  148.         object ICollection.SyncRoot {
  149.             get {
  150.                 if (_syncRoot == null) {
  151.                     System.Threading.Interlocked.CompareExchange(ref _syncRoot, new object(), null);
  152.                 }
  153.                 return _syncRoot;
  154.             }
  155.         }
  156.        
  157.         /// <devdoc>
  158.         /// Gets or sets the object at the specified index
  159.         /// </devdoc>
  160.         public object this[int index]
  161.         {
  162.             get { return ((DictionaryEntry)objectsArray[index]).Value; }
  163.             set {
  164.                 if (_readOnly) {
  165.                     throw new NotSupportedException(SR.GetString(SR.OrderedDictionary_ReadOnly));
  166.                 }
  167.                 if (index < 0 || index >= objectsArray.Count) {
  168.                     throw new ArgumentOutOfRangeException("index");
  169.                 }
  170.                 object key = ((DictionaryEntry)objectsArray[index]).Key;
  171.                 objectsArray[index] = new DictionaryEntry(key, value);
  172.                 objectsTable[key] = value;
  173.             }
  174.         }
  175.        
  176.         /// <devdoc>
  177.         /// Gets or sets the object with the specified key
  178.         /// </devdoc>
  179.         public object this[object key]
  180.         {
  181.             get { return objectsTable[key]; }
  182.             set {
  183.                 if (_readOnly) {
  184.                     throw new NotSupportedException(SR.GetString(SR.OrderedDictionary_ReadOnly));
  185.                 }
  186.                 if (objectsTable.Contains(key)) {
  187.                     objectsTable[key] = value;
  188.                     objectsArray[IndexOfKey(key)] = new DictionaryEntry(key, value);
  189.                 }
  190.                 else {
  191.                     Add(key, value);
  192.                 }
  193.             }
  194.         }
  195.        
  196.         /// <devdoc>
  197.         /// Returns an arrayList of the values in the table
  198.         /// </devdoc>
  199.         public ICollection Values {
  200.             get { return new OrderedDictionaryKeyValueCollection(objectsArray, false); }
  201.         }
  202.        
  203.         /// <devdoc>
  204.         /// Adds a new entry to the table with the lowest-available index.
  205.         /// </devdoc>
  206.         public void Add(object key, object value)
  207.         {
  208.             if (_readOnly) {
  209.                 throw new NotSupportedException(SR.GetString(SR.OrderedDictionary_ReadOnly));
  210.             }
  211.             objectsTable.Add(key, value);
  212.             objectsArray.Add(new DictionaryEntry(key, value));
  213.         }
  214.        
  215.         /// <devdoc>
  216.         /// Clears all elements in the table.
  217.         /// </devdoc>
  218.         public void Clear()
  219.         {
  220.             if (_readOnly) {
  221.                 throw new NotSupportedException(SR.GetString(SR.OrderedDictionary_ReadOnly));
  222.             }
  223.             objectsTable.Clear();
  224.             objectsArray.Clear();
  225.         }
  226.        
  227.         /// <devdoc>
  228.         /// Returns a readonly OrderedDictionary for the given OrderedDictionary.
  229.         /// </devdoc>
  230.         public OrderedDictionary AsReadOnly()
  231.         {
  232.             return new OrderedDictionary(this);
  233.         }
  234.        
  235.         /// <devdoc>
  236.         /// Returns true if the key exists in the table, false otherwise.
  237.         /// </devdoc>
  238.         public bool Contains(object key)
  239.         {
  240.             return objectsTable.Contains(key);
  241.         }
  242.        
  243.         /// <devdoc>
  244.         /// Copies the table to an array. This will not preserve order.
  245.         /// </devdoc>
  246.         public void CopyTo(Array array, int index)
  247.         {
  248.             objectsTable.CopyTo(array, index);
  249.         }
  250.        
  251.         private int IndexOfKey(object key)
  252.         {
  253.             for (int i = 0; i < objectsArray.Count; i++) {
  254.                 object o = ((DictionaryEntry)objectsArray[i]).Key;
  255.                 if (_comparer != null) {
  256.                     if (_comparer.Equals(o, key)) {
  257.                         return i;
  258.                     }
  259.                 }
  260.                 else {
  261.                     if (o.Equals(key)) {
  262.                         return i;
  263.                     }
  264.                 }
  265.             }
  266.             return -1;
  267.         }
  268.        
  269.         /// <devdoc>
  270.         /// Inserts a new object at the given index with the given key.
  271.         /// </devdoc>
  272.         public void Insert(int index, object key, object value)
  273.         {
  274.             if (_readOnly) {
  275.                 throw new NotSupportedException(SR.GetString(SR.OrderedDictionary_ReadOnly));
  276.             }
  277.             if (index > Count || index < 0) {
  278.                 throw new ArgumentOutOfRangeException("index");
  279.             }
  280.             objectsTable.Add(key, value);
  281.             objectsArray.Insert(index, new DictionaryEntry(key, value));
  282.         }
  283.        
  284.         /// <devdoc>
  285.         /// Handles the deserization event. This method can be overridden.
  286.         /// </devdoc>
  287.         protected virtual void OnDeserialization(object sender)
  288.         {
  289.             if (_siInfo == null) {
  290.                 throw new SerializationException(SR.GetString(SR.Serialization_InvalidOnDeser));
  291.             }
  292.             _comparer = (IEqualityComparer)_siInfo.GetValue(KeyComparerName, typeof(IEqualityComparer));
  293.             _readOnly = _siInfo.GetBoolean(ReadOnlyName);
  294.             _initialCapacity = _siInfo.GetInt32(InitCapacityName);
  295.            
  296.             object[] serArray = (object[])_siInfo.GetValue(ArrayListName, typeof(object[]));
  297.            
  298.             if (serArray != null) {
  299.                 foreach (object o in serArray) {
  300.                     DictionaryEntry entry;
  301.                     try {
  302.                         // DictionaryEntry is a value type, so it can only be casted.
  303.                         entry = (DictionaryEntry)o;
  304.                     }
  305.                     catch {
  306.                         throw new SerializationException(SR.GetString(SR.OrderedDictionary_SerializationMismatch));
  307.                     }
  308.                     objectsArray.Add(entry);
  309.                     objectsTable.Add(entry.Key, entry.Value);
  310.                 }
  311.             }
  312.         }
  313.        
  314.         /// <devdoc>
  315.         /// Removes the entry at the given index.
  316.         /// </devdoc>
  317.         public void RemoveAt(int index)
  318.         {
  319.             if (_readOnly) {
  320.                 throw new NotSupportedException(SR.GetString(SR.OrderedDictionary_ReadOnly));
  321.             }
  322.             if (index >= Count || index < 0) {
  323.                 throw new ArgumentOutOfRangeException("index");
  324.             }
  325.             object key = ((DictionaryEntry)objectsArray[index]).Key;
  326.             objectsArray.RemoveAt(index);
  327.             objectsTable.Remove(key);
  328.         }
  329.        
  330.         /// <devdoc>
  331.         /// Removes the entry with the given key.
  332.         /// </devdoc>
  333.         public void Remove(object key)
  334.         {
  335.             if (_readOnly) {
  336.                 throw new NotSupportedException(SR.GetString(SR.OrderedDictionary_ReadOnly));
  337.             }
  338.             if (key == null) {
  339.                 throw new ArgumentNullException("key");
  340.             }
  341.            
  342.             int index = IndexOfKey(key);
  343.             if (index < 0) {
  344.                 return;
  345.             }
  346.            
  347.             objectsTable.Remove(key);
  348.             objectsArray.RemoveAt(index);
  349.         }
  350.        
  351.         #region IDictionary implementation
  352.         /// <internalonly/>
  353.         public virtual IDictionaryEnumerator GetEnumerator()
  354.         {
  355.             return new OrderedDictionaryEnumerator(objectsArray, OrderedDictionaryEnumerator.DictionaryEntry);
  356.         }
  357.         #endregion
  358.        
  359.         #region IEnumerable implementation
  360.         /// <internalonly/>
  361.         IEnumerator IEnumerable.GetEnumerator()
  362.         {
  363.             return new OrderedDictionaryEnumerator(objectsArray, OrderedDictionaryEnumerator.DictionaryEntry);
  364.         }
  365.         #endregion
  366.        
  367.         #region ISerialization implementation
  368.         [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
  369.         public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
  370.         {
  371.             if (info == null) {
  372.                 throw new ArgumentNullException("info");
  373.             }
  374.             info.AddValue(KeyComparerName, _comparer, typeof(IEqualityComparer));
  375.             info.AddValue(ReadOnlyName, _readOnly);
  376.             info.AddValue(InitCapacityName, _initialCapacity);
  377.             object[] serArray = new object[Count];
  378.             _objectsArray.CopyTo(serArray);
  379.             info.AddValue(ArrayListName, serArray);
  380.         }
  381.         #endregion
  382.        
  383.         #region IDeserializationCallback implementation
  384.         void IDeserializationCallback.OnDeserialization(object sender)
  385.         {
  386.             OnDeserialization(sender);
  387.         }
  388.         #endregion
  389.        
  390.         /// <devdoc>
  391.         /// OrderedDictionaryEnumerator works just like any other IDictionaryEnumerator, but it retrieves DictionaryEntries
  392.         /// in the order by index.
  393.         /// </devdoc>
  394.         private class OrderedDictionaryEnumerator : IDictionaryEnumerator
  395.         {
  396.            
  397.             private int _index = -1;
  398.             private ArrayList _objects;
  399.             private int _objectReturnType;
  400.             internal const int Keys = 1;
  401.             internal const int Values = 2;
  402.             internal const int DictionaryEntry = 3;
  403.            
  404.             internal OrderedDictionaryEnumerator(ArrayList array, int objectReturnType)
  405.             {
  406.                 _objects = array;
  407.                 _objectReturnType = objectReturnType;
  408.             }
  409.            
  410.             /// <devdoc>
  411.             /// Retrieves the current DictionaryEntry. This is the same as Entry, but not strongly-typed.
  412.             /// </devdoc>
  413.             public object Current {
  414.                 get {
  415.                     if (_objectReturnType == Keys) {
  416.                         return ((DictionaryEntry)_objects[_index]).Key;
  417.                     }
  418.                     if (_objectReturnType == Values) {
  419.                         return ((DictionaryEntry)_objects[_index]).Value;
  420.                     }
  421.                     return Entry;
  422.                 }
  423.             }
  424.            
  425.             /// <devdoc>
  426.             /// Retrieves the current DictionaryEntry
  427.             /// </devdoc>
  428.             public DictionaryEntry Entry {
  429.                 get {
  430.                     if (_index < 0 || _index >= _objects.Count) {
  431.                         throw new InvalidOperationException();
  432.                     }
  433.                     return new DictionaryEntry(((DictionaryEntry)_objects[_index]).Key, ((DictionaryEntry)_objects[_index]).Value);
  434.                 }
  435.             }
  436.            
  437.             /// <devdoc>
  438.             /// Retrieves the key of the current DictionaryEntry
  439.             /// </devdoc>
  440.             public object Key {
  441.                 get {
  442.                     if (_index < 0 || _index >= _objects.Count) {
  443.                         throw new InvalidOperationException();
  444.                     }
  445.                     return ((DictionaryEntry)_objects[_index]).Key;
  446.                 }
  447.             }
  448.            
  449.             /// <devdoc>
  450.             /// Retrieves the value of the current DictionaryEntry
  451.             /// </devdoc>
  452.             public object Value {
  453.                 get {
  454.                     if (_index < 0 || _index >= _objects.Count) {
  455.                         throw new InvalidOperationException();
  456.                     }
  457.                     return ((DictionaryEntry)_objects[_index]).Value;
  458.                 }
  459.             }
  460.            
  461.             /// <devdoc>
  462.             /// Moves the enumerator pointer to the next member
  463.             /// </devdoc>
  464.             public bool MoveNext()
  465.             {
  466.                 _index++;
  467.                 if (_index >= _objects.Count) {
  468.                     return false;
  469.                 }
  470.                 return true;
  471.             }
  472.            
  473.             /// <devdoc>
  474.             /// Resets the enumerator pointer to the beginning.
  475.             /// </devdoc>
  476.             public void Reset()
  477.             {
  478.                 _index = -1;
  479.             }
  480.         }
  481.        
  482.         /// <devdoc>
  483.         /// OrderedDictionaryKeyValueCollection implements a collection for the Values and Keys properties
  484.         /// that is "live"- it will reflect changes to the OrderedDictionary on the collection made after the getter
  485.         /// was called.
  486.         /// </devdoc>
  487.         private class OrderedDictionaryKeyValueCollection : ICollection
  488.         {
  489.             private ArrayList _objects;
  490.             bool isKeys;
  491.            
  492.             public OrderedDictionaryKeyValueCollection(ArrayList array, bool isKeys)
  493.             {
  494.                 this._objects = array;
  495.                 this.isKeys = isKeys;
  496.             }
  497.            
  498.             void ICollection.CopyTo(Array array, int index)
  499.             {
  500.                 if (array == null)
  501.                     throw new ArgumentNullException("array");
  502.                 if (index < 0)
  503.                     throw new ArgumentOutOfRangeException("index");
  504.                 foreach (object o in _objects) {
  505.                     array.SetValue(isKeys ? ((DictionaryEntry)o).Key : ((DictionaryEntry)o).Value, index);
  506.                     index++;
  507.                 }
  508.             }
  509.            
  510.             int ICollection.Count {
  511.                 get { return _objects.Count; }
  512.             }
  513.            
  514.             bool ICollection.IsSynchronized {
  515.                 get { return false; }
  516.             }
  517.            
  518.             object ICollection.SyncRoot {
  519.                 get { return _objects.SyncRoot; }
  520.             }
  521.            
  522.             IEnumerator IEnumerable.GetEnumerator()
  523.             {
  524.                 return new OrderedDictionaryEnumerator(_objects, isKeys == true ? OrderedDictionaryEnumerator.Keys : OrderedDictionaryEnumerator.Values);
  525.             }
  526.         }
  527.     }
  528. }

Developer Fusion