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
;
53 private IEqualityComparer equality_comparer
;
55 internal IEqualityComparer EqualityComparer
{
56 get { return equality_comparer; }
59 internal IComparer Comparer
{
60 get {return m_comparer;}
63 internal IHashCodeProvider HashCodeProvider
{
64 get {return m_hashprovider;}
71 public _Item(string key
, object value)
78 /// Implements IEnumerable interface for KeysCollection
81 internal class _KeysEnumerator
: IEnumerator
83 private NameObjectCollectionBase m_collection
;
84 private int m_position
;
86 internal _KeysEnumerator(NameObjectCollectionBase collection
)
88 m_collection
= collection
;
95 if ((m_position
<m_collection
.Count
)||(m_position
<0))
96 return m_collection
.BaseGetKey(m_position
);
98 throw new InvalidOperationException();
102 public bool MoveNext()
104 return ((++m_position
) < m_collection
.Count
);
113 /// SDK: Represents a collection of the String keys of a collection.
116 public class KeysCollection
: ICollection
, IEnumerable
118 private NameObjectCollectionBase m_collection
;
120 internal KeysCollection (NameObjectCollectionBase collection
)
122 this.m_collection
= collection
;
125 public virtual string Get( int index
)
127 return m_collection
.BaseGetKey(index
);
130 // ICollection methods -----------------------------------
131 void ICollection
.CopyTo (Array array
, int arrayIndex
)
133 ArrayList items
= m_collection
.m_ItemsArray
;
136 throw new ArgumentNullException ("array");
139 throw new ArgumentOutOfRangeException ("arrayIndex");
141 if ((array
.Length
> 0) && (arrayIndex
>= array
.Length
))
142 throw new ArgumentException ("arrayIndex is equal to or greater than array.Length");
144 if (arrayIndex
+ items
.Count
> array
.Length
)
145 throw new ArgumentException ("Not enough room from arrayIndex to end of array for this KeysCollection");
148 if (array
!= null && array
.Rank
> 1)
149 throw new ArgumentException ("array is multidimensional");
151 object[] objArray
= (object[])array
;
152 for (int i
= 0; i
< items
.Count
; i
++, arrayIndex
++)
153 objArray
[arrayIndex
] = ((_Item
)items
[i
]).key
;
156 bool ICollection
.IsSynchronized
162 object ICollection
.SyncRoot
169 /// Gets the number of keys in the NameObjectCollectionBase.KeysCollection
174 return m_collection
.Count
;
178 public string this [int index
] {
179 get { return Get (index); }
182 // IEnumerable methods --------------------------------
184 /// SDK: Returns an enumerator that can iterate through the NameObjectCollectionBase.KeysCollection.
186 /// <returns></returns>
187 public IEnumerator
GetEnumerator()
189 return new _KeysEnumerator(m_collection
);
193 //--------------- Protected Instance Constructors --------------
196 /// SDK: Initializes a new instance of the NameObjectCollectionBase class that is empty.
198 protected NameObjectCollectionBase ()
202 m_hashprovider
= CaseInsensitiveHashCodeProvider
.Default
;
203 m_comparer
= CaseInsensitiveComparer
.Default
;
205 m_hashprovider
= CaseInsensitiveHashCodeProvider
.DefaultInvariant
;
206 m_comparer
= CaseInsensitiveComparer
.DefaultInvariant
;
212 protected NameObjectCollectionBase( int capacity
)
216 m_hashprovider
= CaseInsensitiveHashCodeProvider
.Default
;
217 m_comparer
= CaseInsensitiveComparer
.Default
;
219 m_hashprovider
= CaseInsensitiveHashCodeProvider
.DefaultInvariant
;
220 m_comparer
= CaseInsensitiveComparer
.DefaultInvariant
;
222 m_defCapacity
= capacity
;
228 internal NameObjectCollectionBase (IEqualityComparer equalityComparer
, IComparer comparer
, IHashCodeProvider hcp
)
230 equality_comparer
= equalityComparer
;
231 m_comparer
= comparer
;
232 m_hashprovider
= hcp
;
238 protected NameObjectCollectionBase (IEqualityComparer equalityComparer
) : this( (equalityComparer
== null ? StringComparer
.InvariantCultureIgnoreCase
: equalityComparer
), null, null)
242 [Obsolete ("Use NameObjectCollectionBase(IEqualityComparer)")]
244 protected NameObjectCollectionBase( IHashCodeProvider hashProvider
, IComparer comparer
)
246 m_comparer
= comparer
;
247 m_hashprovider
= hashProvider
;
253 protected NameObjectCollectionBase (SerializationInfo info
, StreamingContext context
)
259 protected NameObjectCollectionBase (int capacity
, IEqualityComparer equalityComparer
)
262 equality_comparer
= (equalityComparer
== null ? StringComparer
.InvariantCultureIgnoreCase
: equalityComparer
);
263 m_defCapacity
= capacity
;
267 [Obsolete ("Use NameObjectCollectionBase(int,IEqualityComparer)")]
269 protected NameObjectCollectionBase( int capacity
, IHashCodeProvider hashProvider
, IComparer comparer
)
273 m_hashprovider
= hashProvider
;
274 m_comparer
= comparer
;
275 m_defCapacity
= capacity
;
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
);
287 m_ItemsContainer
= new Hashtable (m_defCapacity
, m_hashprovider
, m_comparer
);
289 m_ItemsArray
= new ArrayList();
290 m_NullKeyItem
= null;
293 //--------------- Public Instance Properties -------------------
295 public virtual NameObjectCollectionBase
.KeysCollection Keys
{
297 if (keyscoll
== null)
298 keyscoll
= new KeysCollection (this);
303 //--------------- Public Instance Methods ----------------------
306 /// SDK: Returns an enumerator that can iterate through the NameObjectCollectionBase.
308 /// <remark>This enumerator returns the keys of the collection as strings.</remark>
310 /// <returns></returns>
315 IEnumerator
GetEnumerator()
317 return new _KeysEnumerator(this);
321 public virtual void GetObjectData (SerializationInfo info
, StreamingContext context
)
324 throw new ArgumentNullException ("info");
327 string [] keys
= new string [count
];
328 object [] values
= new object [count
];
330 foreach (_Item item
in m_ItemsArray
) {
332 values
[i
] = item
.value;
337 if (equality_comparer
!= null) {
338 info
.AddValue ("KeyComparer", equality_comparer
, typeof (IEqualityComparer
));
339 info
.AddValue ("Version", 4, typeof (int));
341 info
.AddValue ("HashProvider", m_hashprovider
, typeof (IHashCodeProvider
));
342 info
.AddValue ("Comparer", m_comparer
, typeof (IComparer
));
343 info
.AddValue ("Version", 2, typeof (int));
346 info
.AddValue ("HashProvider", m_hashprovider
, typeof (IHashCodeProvider
));
347 info
.AddValue ("Comparer", m_comparer
, typeof (IComparer
));
349 info
.AddValue("ReadOnly", m_readonly
);
350 info
.AddValue("Count", count
);
351 info
.AddValue("Keys", keys
, typeof(string[]));
352 info
.AddValue("Values", values
, typeof(object[]));
356 public virtual int Count
359 return m_ItemsArray
.Count
;
363 bool ICollection
.IsSynchronized
365 get { return false; }
368 object ICollection
.SyncRoot
373 void ICollection
.CopyTo (Array array
, int index
)
375 ((ICollection
)Keys
).CopyTo (array
, index
);
378 // IDeserializationCallback
379 public virtual void OnDeserialization (object sender
)
381 SerializationInfo info
= infoCopy
;
383 // If a subclass overrides the serialization constructor
384 // and inplements its own serialization process, infoCopy will
385 // be null and we can ignore this callback.
390 m_hashprovider
= (IHashCodeProvider
) info
.GetValue ("HashProvider",
391 typeof (IHashCodeProvider
));
393 if (m_hashprovider
== null) {
394 equality_comparer
= (IEqualityComparer
) info
.GetValue ("KeyComparer", typeof (IEqualityComparer
));
396 m_comparer
= (IComparer
) info
.GetValue ("Comparer", typeof (IComparer
));
397 if (m_comparer
== null)
398 throw new SerializationException ("The comparer is null");
401 if (m_hashprovider
== null)
402 throw new SerializationException ("The hash provider is null");
404 m_comparer
= (IComparer
) info
.GetValue ("Comparer", typeof (IComparer
));
405 if (m_comparer
== null)
406 throw new SerializationException ("The comparer is null");
408 m_readonly
= info
.GetBoolean ("ReadOnly");
409 string [] keys
= (string []) info
.GetValue ("Keys", typeof (string []));
411 throw new SerializationException ("keys is null");
413 object [] values
= (object []) info
.GetValue ("Values", typeof (object []));
415 throw new SerializationException ("values is null");
418 int count
= keys
.Length
;
419 for (int i
= 0; i
< count
; i
++)
420 BaseAdd (keys
[i
], values
[i
]);
423 //--------------- Protected Instance Properties ----------------
425 /// SDK: Gets or sets a value indicating whether the NameObjectCollectionBase instance is read-only.
427 protected bool IsReadOnly
437 //--------------- Protected Instance Methods -------------------
439 /// Adds an Item with the specified key and value into the <see cref="NameObjectCollectionBase"/>NameObjectCollectionBase instance.
441 /// <param name="name"></param>
442 /// <param name="value"></param>
443 protected void BaseAdd( string name
, object value )
446 throw new NotSupportedException("Collection is read-only");
448 _Item newitem
=new _Item(name
, value);
451 //todo: consider nullkey entry
452 if (m_NullKeyItem
==null)
453 m_NullKeyItem
= newitem
;
456 if (m_ItemsContainer
[name
]==null){
457 m_ItemsContainer
.Add(name
,newitem
);
459 m_ItemsArray
.Add(newitem
);
462 protected void BaseClear()
465 throw new NotSupportedException("Collection is read-only");
470 /// SDK: Gets the value of the entry at the specified index of the NameObjectCollectionBase instance.
472 /// <param name="index"></param>
473 /// <returns></returns>
474 protected object BaseGet( int index
)
476 return ((_Item
)m_ItemsArray
[index
]).value;
480 /// SDK: Gets the value of the first entry with the specified key from the NameObjectCollectionBase instance.
482 /// <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>
483 /// <param name="name"></param>
484 /// <returns></returns>
485 protected object BaseGet( string name
)
487 _Item item
= FindFirstMatchedItem(name
);
488 /// 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.
496 /// SDK:Returns a String array that contains all the keys in the NameObjectCollectionBase instance.
498 /// <returns>A String array that contains all the keys in the NameObjectCollectionBase instance.</returns>
499 protected string[] BaseGetAllKeys()
501 int cnt
= m_ItemsArray
.Count
;
502 string[] allKeys
= new string[cnt
];
503 for(int i
=0; i
<cnt
; i
++)
504 allKeys
[i
] = BaseGetKey(i
);//((_Item)m_ItemsArray[i]).key;
510 /// SDK: Returns an Object array that contains all the values in the NameObjectCollectionBase instance.
512 /// <returns>An Object array that contains all the values in the NameObjectCollectionBase instance.</returns>
513 protected object[] BaseGetAllValues()
515 int cnt
= m_ItemsArray
.Count
;
516 object[] allValues
= new object[cnt
];
517 for(int i
=0; i
<cnt
; i
++)
518 allValues
[i
] = BaseGet(i
);
523 protected object[] BaseGetAllValues( Type type
)
526 throw new ArgumentNullException("'type' argument can't be null");
527 int cnt
= m_ItemsArray
.Count
;
528 object[] allValues
= (object[]) Array
.CreateInstance (type
, cnt
);
529 for(int i
=0; i
<cnt
; i
++)
530 allValues
[i
] = BaseGet(i
);
535 protected string BaseGetKey( int index
)
537 return ((_Item
)m_ItemsArray
[index
]).key
;
541 /// Gets a value indicating whether the NameObjectCollectionBase instance contains entries whose keys are not a null reference
543 /// <returns>true if the NameObjectCollectionBase instance contains entries whose keys are not a null reference otherwise, false.</returns>
544 protected bool BaseHasKeys()
546 return (m_ItemsContainer
.Count
>0);
549 protected void BaseRemove( string name
)
554 throw new NotSupportedException("Collection is read-only");
557 m_ItemsContainer
.Remove(name
);
560 m_NullKeyItem
= null;
563 cnt
= m_ItemsArray
.Count
;
564 for (int i
=0 ; i
< cnt
; ){
566 if (Equals (key
, name
)) {
567 m_ItemsArray
.RemoveAt(i
);
578 /// <param name="index"></param>
579 /// <LAME>This function implemented the way Microsoft implemented it -
580 /// 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.
582 /// hashtable is [("Key1","value1")] and array contains [("Key1","value1")("Key1","value2")] then
583 /// after RemoveAt(1) the collection will be in following state:
585 /// array: [("Key1","value1")]
586 /// It's ok only then the key is uniquely assosiated with the value
587 /// To fix it a comparsion of objects stored under the same key in the hashtable and in the arraylist should be added
589 protected void BaseRemoveAt( int index
)
592 throw new NotSupportedException("Collection is read-only");
593 string key
= BaseGetKey(index
);
595 // TODO: see LAME description above
596 m_ItemsContainer
.Remove(key
);
599 m_NullKeyItem
= null;
600 m_ItemsArray
.RemoveAt(index
);
604 /// SDK: Sets the value of the entry at the specified index of the NameObjectCollectionBase instance.
606 /// <param name="index"></param>
607 /// <param name="value"></param>
608 protected void BaseSet( int index
, object value )
612 throw new NotSupportedException("Collection is read-only");
614 _Item item
= (_Item
)m_ItemsArray
[index
];
619 /// 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.
621 /// <param name="name">The String key of the entry to set. The key can be a null reference </param>
622 /// <param name="value">The Object that represents the new value of the entry to set. The value can be a null reference</param>
623 protected void BaseSet( string name
, object value )
627 throw new NotSupportedException("Collection is read-only");
629 _Item item
= FindFirstMatchedItem(name
);
633 BaseAdd(name
, value);
637 private _Item
FindFirstMatchedItem(string name
)
640 return (_Item
)m_ItemsContainer
[name
];
642 //TODO: consider null key case
643 return m_NullKeyItem
;
647 internal bool Equals (string s1
, string s2
)
650 if (m_comparer
!= null)
651 return (m_comparer
.Compare (s1
, s2
) == 0);
653 return equality_comparer
.Equals (s1
, s2
);
655 return (m_comparer
.Compare (s1
, s2
) == 0);