2 // System.Collections.Specialized.NameObjectCollectionBase.cs
6 // Andreas Nahr (ClassDevelopment@A-SoftTech.com)
8 // (C) Ximian, Inc. http://www.ximian.com
9 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System.Collections;
33 using System.Runtime.Serialization;
35 namespace System.Collections.Specialized
38 public abstract class NameObjectCollectionBase : ICollection, IEnumerable, ISerializable, IDeserializationCallback
40 private Hashtable m_ItemsContainer;
42 /// Extends Hashtable based Items container to support storing null-key pairs
44 private _Item m_NullKeyItem;
45 private ArrayList m_ItemsArray;
46 private IHashCodeProvider m_hashprovider;
47 private IComparer m_comparer;
48 private int m_defCapacity;
49 private bool m_readonly;
50 SerializationInfo infoCopy;
51 private KeysCollection keyscoll;
52 private IEqualityComparer equality_comparer;
54 internal IEqualityComparer EqualityComparer {
55 get { return equality_comparer; }
58 internal IComparer Comparer {
59 get {return m_comparer;}
62 internal IHashCodeProvider HashCodeProvider {
63 get {return m_hashprovider;}
70 public _Item(string key, object value)
77 /// Implements IEnumerable interface for KeysCollection
80 internal class _KeysEnumerator : IEnumerator
82 private NameObjectCollectionBase m_collection;
83 private int m_position;
85 internal _KeysEnumerator(NameObjectCollectionBase collection)
87 m_collection = collection;
94 if ((m_position<m_collection.Count)||(m_position<0))
95 return m_collection.BaseGetKey(m_position);
97 throw new InvalidOperationException();
101 public bool MoveNext()
103 return ((++m_position) < m_collection.Count);
112 /// SDK: Represents a collection of the String keys of a collection.
115 public class KeysCollection : ICollection, IEnumerable
117 private NameObjectCollectionBase m_collection;
119 internal KeysCollection (NameObjectCollectionBase collection)
121 this.m_collection = collection;
124 public virtual string Get( int index )
126 return m_collection.BaseGetKey(index);
129 // ICollection methods -----------------------------------
130 void ICollection.CopyTo (Array array, int arrayIndex)
132 ArrayList items = m_collection.m_ItemsArray;
134 throw new ArgumentNullException ("array");
137 throw new ArgumentOutOfRangeException ("arrayIndex");
139 if ((array.Length > 0) && (arrayIndex >= array.Length))
140 throw new ArgumentException ("arrayIndex is equal to or greater than array.Length");
142 if (arrayIndex + items.Count > array.Length)
143 throw new ArgumentException ("Not enough room from arrayIndex to end of array for this KeysCollection");
145 if (array != null && array.Rank > 1)
146 throw new ArgumentException ("array is multidimensional");
148 object[] objArray = (object[])array;
149 for (int i = 0; i < items.Count; i++, arrayIndex++)
150 objArray [arrayIndex] = ((_Item)items [i]).key;
153 bool ICollection.IsSynchronized
159 object ICollection.SyncRoot
166 /// Gets the number of keys in the NameObjectCollectionBase.KeysCollection
171 return m_collection.Count;
175 public string this [int index] {
176 get { return Get (index); }
179 // IEnumerable methods --------------------------------
181 /// SDK: Returns an enumerator that can iterate through the NameObjectCollectionBase.KeysCollection.
183 /// <returns></returns>
184 public IEnumerator GetEnumerator()
186 return new _KeysEnumerator(m_collection);
190 //--------------- Protected Instance Constructors --------------
193 /// SDK: Initializes a new instance of the NameObjectCollectionBase class that is empty.
195 protected NameObjectCollectionBase ()
199 m_hashprovider = CaseInsensitiveHashCodeProvider.Default;
200 m_comparer = CaseInsensitiveComparer.Default;
202 m_hashprovider = CaseInsensitiveHashCodeProvider.DefaultInvariant;
203 m_comparer = CaseInsensitiveComparer.DefaultInvariant;
209 protected NameObjectCollectionBase( int capacity )
213 m_hashprovider = CaseInsensitiveHashCodeProvider.Default;
214 m_comparer = CaseInsensitiveComparer.Default;
216 m_hashprovider = CaseInsensitiveHashCodeProvider.DefaultInvariant;
217 m_comparer = CaseInsensitiveComparer.DefaultInvariant;
219 m_defCapacity = capacity;
223 internal NameObjectCollectionBase (IEqualityComparer equalityComparer, IComparer comparer, IHashCodeProvider hcp)
225 equality_comparer = equalityComparer;
226 m_comparer = comparer;
227 m_hashprovider = hcp;
233 protected NameObjectCollectionBase (IEqualityComparer equalityComparer) : this( (equalityComparer == null ? StringComparer.InvariantCultureIgnoreCase : equalityComparer), null, null)
237 [Obsolete ("Use NameObjectCollectionBase(IEqualityComparer)")]
238 protected NameObjectCollectionBase( IHashCodeProvider hashProvider, IComparer comparer )
240 m_comparer = comparer;
241 m_hashprovider = hashProvider;
247 protected NameObjectCollectionBase (SerializationInfo info, StreamingContext context)
252 protected NameObjectCollectionBase (int capacity, IEqualityComparer equalityComparer)
255 equality_comparer = (equalityComparer == null ? StringComparer.InvariantCultureIgnoreCase : equalityComparer);
256 m_defCapacity = capacity;
260 [Obsolete ("Use NameObjectCollectionBase(int,IEqualityComparer)")]
261 protected NameObjectCollectionBase( int capacity, IHashCodeProvider hashProvider, IComparer comparer )
265 m_hashprovider = hashProvider;
266 m_comparer = comparer;
267 m_defCapacity = capacity;
273 if (m_ItemsContainer != null) {
274 m_ItemsContainer.Clear ();
275 m_ItemsContainer = null;
278 if (m_ItemsArray != null) {
279 m_ItemsArray.Clear ();
282 if (equality_comparer != null)
283 m_ItemsContainer = new Hashtable (m_defCapacity, equality_comparer);
285 m_ItemsContainer = new Hashtable (m_defCapacity, m_hashprovider, m_comparer);
286 m_ItemsArray = new ArrayList();
287 m_NullKeyItem = null;
290 //--------------- Public Instance Properties -------------------
292 public virtual NameObjectCollectionBase.KeysCollection Keys {
294 if (keyscoll == null)
295 keyscoll = new KeysCollection (this);
300 //--------------- Public Instance Methods ----------------------
303 /// SDK: Returns an enumerator that can iterate through the NameObjectCollectionBase.
305 /// <remark>This enumerator returns the keys of the collection as strings.</remark>
307 /// <returns></returns>
308 public virtual IEnumerator GetEnumerator()
310 return new _KeysEnumerator(this);
314 public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
317 throw new ArgumentNullException ("info");
320 string [] keys = new string [count];
321 object [] values = new object [count];
323 foreach (_Item item in m_ItemsArray) {
325 values [i] = item.value;
329 if (equality_comparer != null) {
330 info.AddValue ("KeyComparer", equality_comparer, typeof (IEqualityComparer));
331 info.AddValue ("Version", 4, typeof (int));
333 info.AddValue ("HashProvider", m_hashprovider, typeof (IHashCodeProvider));
334 info.AddValue ("Comparer", m_comparer, typeof (IComparer));
335 info.AddValue ("Version", 2, typeof (int));
337 info.AddValue("ReadOnly", m_readonly);
338 info.AddValue("Count", count);
339 info.AddValue("Keys", keys, typeof(string[]));
340 info.AddValue("Values", values, typeof(object[]));
344 public virtual int Count
347 return m_ItemsArray.Count;
351 bool ICollection.IsSynchronized
353 get { return false; }
356 object ICollection.SyncRoot
361 void ICollection.CopyTo (Array array, int index)
363 ((ICollection)Keys).CopyTo (array, index);
366 // IDeserializationCallback
367 public virtual void OnDeserialization (object sender)
369 SerializationInfo info = infoCopy;
371 // If a subclass overrides the serialization constructor
372 // and inplements its own serialization process, infoCopy will
373 // be null and we can ignore this callback.
378 m_hashprovider = (IHashCodeProvider) info.GetValue ("HashProvider",
379 typeof (IHashCodeProvider));
380 if (m_hashprovider == null) {
381 equality_comparer = (IEqualityComparer) info.GetValue ("KeyComparer", typeof (IEqualityComparer));
383 m_comparer = (IComparer) info.GetValue ("Comparer", typeof (IComparer));
384 if (m_comparer == null)
385 throw new SerializationException ("The comparer is null");
387 m_readonly = info.GetBoolean ("ReadOnly");
388 string [] keys = (string []) info.GetValue ("Keys", typeof (string []));
390 throw new SerializationException ("keys is null");
392 object [] values = (object []) info.GetValue ("Values", typeof (object []));
394 throw new SerializationException ("values is null");
397 int count = keys.Length;
398 for (int i = 0; i < count; i++)
399 BaseAdd (keys [i], values [i]);
402 //--------------- Protected Instance Properties ----------------
404 /// SDK: Gets or sets a value indicating whether the NameObjectCollectionBase instance is read-only.
406 protected bool IsReadOnly
416 //--------------- Protected Instance Methods -------------------
418 /// Adds an Item with the specified key and value into the <see cref="NameObjectCollectionBase"/>NameObjectCollectionBase instance.
420 /// <param name="name"></param>
421 /// <param name="value"></param>
422 protected void BaseAdd( string name, object value )
425 throw new NotSupportedException("Collection is read-only");
427 _Item newitem=new _Item(name, value);
430 //todo: consider nullkey entry
431 if (m_NullKeyItem==null)
432 m_NullKeyItem = newitem;
435 if (m_ItemsContainer[name]==null){
436 m_ItemsContainer.Add(name,newitem);
438 m_ItemsArray.Add(newitem);
441 protected void BaseClear()
444 throw new NotSupportedException("Collection is read-only");
449 /// SDK: Gets the value of the entry at the specified index of the NameObjectCollectionBase instance.
451 /// <param name="index"></param>
452 /// <returns></returns>
453 protected object BaseGet( int index )
455 return ((_Item)m_ItemsArray[index]).value;
459 /// SDK: Gets the value of the first entry with the specified key from the NameObjectCollectionBase instance.
461 /// <remark>CAUTION: The BaseGet method does not distinguish between a null reference which is returned because the specified key is not found and a null reference which is returned because the value associated with the key is a null reference.</remark>
462 /// <param name="name"></param>
463 /// <returns></returns>
464 protected object BaseGet( string name )
466 _Item item = FindFirstMatchedItem(name);
467 /// CAUTION: The BaseGet method does not distinguish between a null reference which is returned because the specified key is not found and a null reference which is returned because the value associated with the key is a null reference.
475 /// SDK:Returns a String array that contains all the keys in the NameObjectCollectionBase instance.
477 /// <returns>A String array that contains all the keys in the NameObjectCollectionBase instance.</returns>
478 protected string[] BaseGetAllKeys()
480 int cnt = m_ItemsArray.Count;
481 string[] allKeys = new string[cnt];
482 for(int i=0; i<cnt; i++)
483 allKeys[i] = BaseGetKey(i);//((_Item)m_ItemsArray[i]).key;
489 /// SDK: Returns an Object array that contains all the values in the NameObjectCollectionBase instance.
491 /// <returns>An Object array that contains all the values in the NameObjectCollectionBase instance.</returns>
492 protected object[] BaseGetAllValues()
494 int cnt = m_ItemsArray.Count;
495 object[] allValues = new object[cnt];
496 for(int i=0; i<cnt; i++)
497 allValues[i] = BaseGet(i);
502 protected object[] BaseGetAllValues( Type type )
505 throw new ArgumentNullException("'type' argument can't be null");
506 int cnt = m_ItemsArray.Count;
507 object[] allValues = (object[]) Array.CreateInstance (type, cnt);
508 for(int i=0; i<cnt; i++)
509 allValues[i] = BaseGet(i);
514 protected string BaseGetKey( int index )
516 return ((_Item)m_ItemsArray[index]).key;
520 /// Gets a value indicating whether the NameObjectCollectionBase instance contains entries whose keys are not a null reference
522 /// <returns>true if the NameObjectCollectionBase instance contains entries whose keys are not a null reference otherwise, false.</returns>
523 protected bool BaseHasKeys()
525 return (m_ItemsContainer.Count>0);
528 protected void BaseRemove( string name )
533 throw new NotSupportedException("Collection is read-only");
536 m_ItemsContainer.Remove(name);
539 m_NullKeyItem = null;
542 cnt = m_ItemsArray.Count;
543 for (int i=0 ; i< cnt; ){
545 if (Equals (key, name)) {
546 m_ItemsArray.RemoveAt(i);
557 /// <param name="index"></param>
558 /// <LAME>This function implemented the way Microsoft implemented it -
559 /// item is removed from hashtable and array without considering the case when there are two items with the same key but different values in array.
561 /// hashtable is [("Key1","value1")] and array contains [("Key1","value1")("Key1","value2")] then
562 /// after RemoveAt(1) the collection will be in following state:
564 /// array: [("Key1","value1")]
565 /// It's ok only then the key is uniquely assosiated with the value
566 /// To fix it a comparsion of objects stored under the same key in the hashtable and in the arraylist should be added
568 protected void BaseRemoveAt( int index )
571 throw new NotSupportedException("Collection is read-only");
572 string key = BaseGetKey(index);
574 // TODO: see LAME description above
575 m_ItemsContainer.Remove(key);
578 m_NullKeyItem = null;
579 m_ItemsArray.RemoveAt(index);
583 /// SDK: Sets the value of the entry at the specified index of the NameObjectCollectionBase instance.
585 /// <param name="index"></param>
586 /// <param name="value"></param>
587 protected void BaseSet( int index, object value )
590 throw new NotSupportedException("Collection is read-only");
591 _Item item = (_Item)m_ItemsArray[index];
596 /// Sets the value of the first entry with the specified key in the NameObjectCollectionBase instance, if found; otherwise, adds an entry with the specified key and value into the NameObjectCollectionBase instance.
598 /// <param name="name">The String key of the entry to set. The key can be a null reference </param>
599 /// <param name="value">The Object that represents the new value of the entry to set. The value can be a null reference</param>
600 protected void BaseSet( string name, object value )
603 throw new NotSupportedException("Collection is read-only");
604 _Item item = FindFirstMatchedItem(name);
608 BaseAdd(name, value);
612 private _Item FindFirstMatchedItem(string name)
615 return (_Item)m_ItemsContainer[name];
617 //TODO: consider null key case
618 return m_NullKeyItem;
622 internal bool Equals (string s1, string s2)
624 if (m_comparer != null)
625 return (m_comparer.Compare (s1, s2) == 0);
627 return equality_comparer.Equals (s1, s2);