add ISafeSerializationData
[mcs.git] / class / System / System.Collections.Specialized / NameObjectCollectionBase.cs
blobc934a328c053dd2bc49099fedcbb28e6471c9547
1 //
2 // System.Collections.Specialized.NameObjectCollectionBase.cs
3 //
4 // Author:
5 // Gleb Novodran
6 // Andreas Nahr (ClassDevelopment@A-SoftTech.com)
7 //
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:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
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.
31 using System;
32 using System.Collections;
33 using System.Runtime.Serialization;
35 namespace System.Collections.Specialized
37 [Serializable]
38 public abstract class NameObjectCollectionBase : ICollection, IEnumerable, ISerializable, IDeserializationCallback
40 private Hashtable m_ItemsContainer;
41 /// <summary>
42 /// Extends Hashtable based Items container to support storing null-key pairs
43 /// </summary>
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 #if NET_2_0
53 private IEqualityComparer equality_comparer;
55 internal IEqualityComparer EqualityComparer {
56 get { return equality_comparer; }
58 #endif
59 internal IComparer Comparer {
60 get {return m_comparer;}
63 internal IHashCodeProvider HashCodeProvider {
64 get {return m_hashprovider;}
67 internal class _Item
69 public string key;
70 public object value;
71 public _Item(string key, object value)
73 this.key = key;
74 this.value = value;
77 /// <summary>
78 /// Implements IEnumerable interface for KeysCollection
79 /// </summary>
80 [Serializable]
81 internal class _KeysEnumerator : IEnumerator
83 private NameObjectCollectionBase m_collection;
84 private int m_position;
86 internal _KeysEnumerator(NameObjectCollectionBase collection)
88 m_collection = collection;
89 Reset();
91 public object Current
94 get{
95 if ((m_position<m_collection.Count)||(m_position<0))
96 return m_collection.BaseGetKey(m_position);
97 else
98 throw new InvalidOperationException();
102 public bool MoveNext()
104 return ((++m_position) < m_collection.Count);
106 public void Reset()
108 m_position = -1;
112 /// <summary>
113 /// SDK: Represents a collection of the String keys of a collection.
114 /// </summary>
115 [Serializable]
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;
134 #if NET_2_0
135 if (null == array)
136 throw new ArgumentNullException ("array");
138 if (arrayIndex < 0)
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");
146 #endif
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
158 get{
159 return false;
162 object ICollection.SyncRoot
164 get{
165 return m_collection;
168 /// <summary>
169 /// Gets the number of keys in the NameObjectCollectionBase.KeysCollection
170 /// </summary>
171 public int Count
173 get{
174 return m_collection.Count;
178 public string this [int index] {
179 get { return Get (index); }
182 // IEnumerable methods --------------------------------
183 /// <summary>
184 /// SDK: Returns an enumerator that can iterate through the NameObjectCollectionBase.KeysCollection.
185 /// </summary>
186 /// <returns></returns>
187 public IEnumerator GetEnumerator()
189 return new _KeysEnumerator(m_collection);
193 //--------------- Protected Instance Constructors --------------
195 /// <summary>
196 /// SDK: Initializes a new instance of the NameObjectCollectionBase class that is empty.
197 /// </summary>
198 protected NameObjectCollectionBase ()
200 m_readonly = false;
201 #if NET_1_0
202 m_hashprovider = CaseInsensitiveHashCodeProvider.Default;
203 m_comparer = CaseInsensitiveComparer.Default;
204 #else
205 m_hashprovider = CaseInsensitiveHashCodeProvider.DefaultInvariant;
206 m_comparer = CaseInsensitiveComparer.DefaultInvariant;
207 #endif
208 m_defCapacity = 0;
209 Init();
212 protected NameObjectCollectionBase( int capacity )
214 m_readonly = false;
215 #if NET_1_0
216 m_hashprovider = CaseInsensitiveHashCodeProvider.Default;
217 m_comparer = CaseInsensitiveComparer.Default;
218 #else
219 m_hashprovider = CaseInsensitiveHashCodeProvider.DefaultInvariant;
220 m_comparer = CaseInsensitiveComparer.DefaultInvariant;
221 #endif
222 m_defCapacity = capacity;
223 Init();
226 #if NET_2_0
228 internal NameObjectCollectionBase (IEqualityComparer equalityComparer, IComparer comparer, IHashCodeProvider hcp)
230 equality_comparer = equalityComparer;
231 m_comparer = comparer;
232 m_hashprovider = hcp;
233 m_readonly = false;
234 m_defCapacity = 0;
235 Init ();
238 protected NameObjectCollectionBase (IEqualityComparer equalityComparer) : this( (equalityComparer == null ? StringComparer.InvariantCultureIgnoreCase : equalityComparer), null, null)
242 [Obsolete ("Use NameObjectCollectionBase(IEqualityComparer)")]
243 #endif
244 protected NameObjectCollectionBase( IHashCodeProvider hashProvider, IComparer comparer )
246 m_comparer = comparer;
247 m_hashprovider = hashProvider;
248 m_readonly = false;
249 m_defCapacity = 0;
250 Init ();
253 protected NameObjectCollectionBase (SerializationInfo info, StreamingContext context)
255 infoCopy = info;
258 #if NET_2_0
259 protected NameObjectCollectionBase (int capacity, IEqualityComparer equalityComparer)
261 m_readonly = false;
262 equality_comparer = (equalityComparer == null ? StringComparer.InvariantCultureIgnoreCase : equalityComparer);
263 m_defCapacity = capacity;
264 Init();
267 [Obsolete ("Use NameObjectCollectionBase(int,IEqualityComparer)")]
268 #endif
269 protected NameObjectCollectionBase( int capacity, IHashCodeProvider hashProvider, IComparer comparer )
271 m_readonly = false;
273 m_hashprovider = hashProvider;
274 m_comparer = comparer;
275 m_defCapacity = capacity;
276 Init();
279 private void Init ()
281 #if NET_2_0
282 if (equality_comparer != null)
283 m_ItemsContainer = new Hashtable (m_defCapacity, equality_comparer);
284 else
285 m_ItemsContainer = new Hashtable (m_defCapacity, m_hashprovider, m_comparer);
286 #else
287 m_ItemsContainer = new Hashtable (m_defCapacity, m_hashprovider, m_comparer);
288 #endif
289 m_ItemsArray = new ArrayList();
290 m_NullKeyItem = null;
293 //--------------- Public Instance Properties -------------------
295 public virtual NameObjectCollectionBase.KeysCollection Keys {
296 get {
297 if (keyscoll == null)
298 keyscoll = new KeysCollection (this);
299 return keyscoll;
303 //--------------- Public Instance Methods ----------------------
305 /// <summary>
306 /// SDK: Returns an enumerator that can iterate through the NameObjectCollectionBase.
307 ///
308 /// <remark>This enumerator returns the keys of the collection as strings.</remark>
309 /// </summary>
310 /// <returns></returns>
311 public
312 #if NET_2_0
313 virtual
314 #endif
315 IEnumerator GetEnumerator()
317 return new _KeysEnumerator(this);
320 // ISerializable
321 public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
323 if (info == null)
324 throw new ArgumentNullException ("info");
326 int count = Count;
327 string [] keys = new string [count];
328 object [] values = new object [count];
329 int i = 0;
330 foreach (_Item item in m_ItemsArray) {
331 keys [i] = item.key;
332 values [i] = item.value;
333 i++;
336 #if NET_2_0
337 if (equality_comparer != null) {
338 info.AddValue ("KeyComparer", equality_comparer, typeof (IEqualityComparer));
339 info.AddValue ("Version", 4, typeof (int));
340 } else {
341 info.AddValue ("HashProvider", m_hashprovider, typeof (IHashCodeProvider));
342 info.AddValue ("Comparer", m_comparer, typeof (IComparer));
343 info.AddValue ("Version", 2, typeof (int));
345 #else
346 info.AddValue ("HashProvider", m_hashprovider, typeof (IHashCodeProvider));
347 info.AddValue ("Comparer", m_comparer, typeof (IComparer));
348 #endif
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[]));
355 // ICollection
356 public virtual int Count
358 get{
359 return m_ItemsArray.Count;
363 bool ICollection.IsSynchronized
365 get { return false; }
368 object ICollection.SyncRoot
370 get { return this; }
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.
386 if (info == null)
387 return;
389 infoCopy = null;
390 m_hashprovider = (IHashCodeProvider) info.GetValue ("HashProvider",
391 typeof (IHashCodeProvider));
392 #if NET_2_0
393 if (m_hashprovider == null) {
394 equality_comparer = (IEqualityComparer) info.GetValue ("KeyComparer", typeof (IEqualityComparer));
395 } else {
396 m_comparer = (IComparer) info.GetValue ("Comparer", typeof (IComparer));
397 if (m_comparer == null)
398 throw new SerializationException ("The comparer is null");
400 #else
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");
407 #endif
408 m_readonly = info.GetBoolean ("ReadOnly");
409 string [] keys = (string []) info.GetValue ("Keys", typeof (string []));
410 if (keys == null)
411 throw new SerializationException ("keys is null");
413 object [] values = (object []) info.GetValue ("Values", typeof (object []));
414 if (values == null)
415 throw new SerializationException ("values is null");
417 Init ();
418 int count = keys.Length;
419 for (int i = 0; i < count; i++)
420 BaseAdd (keys [i], values [i]);
423 //--------------- Protected Instance Properties ----------------
424 /// <summary>
425 /// SDK: Gets or sets a value indicating whether the NameObjectCollectionBase instance is read-only.
426 /// </summary>
427 protected bool IsReadOnly
429 get{
430 return m_readonly;
432 set{
433 m_readonly=value;
437 //--------------- Protected Instance Methods -------------------
438 /// <summary>
439 /// Adds an Item with the specified key and value into the <see cref="NameObjectCollectionBase"/>NameObjectCollectionBase instance.
440 /// </summary>
441 /// <param name="name"></param>
442 /// <param name="value"></param>
443 protected void BaseAdd( string name, object value )
445 if (this.IsReadOnly)
446 throw new NotSupportedException("Collection is read-only");
448 _Item newitem=new _Item(name, value);
450 if (name==null){
451 //todo: consider nullkey entry
452 if (m_NullKeyItem==null)
453 m_NullKeyItem = newitem;
455 else
456 if (m_ItemsContainer[name]==null){
457 m_ItemsContainer.Add(name,newitem);
459 m_ItemsArray.Add(newitem);
462 protected void BaseClear()
464 if (this.IsReadOnly)
465 throw new NotSupportedException("Collection is read-only");
466 Init();
469 /// <summary>
470 /// SDK: Gets the value of the entry at the specified index of the NameObjectCollectionBase instance.
471 /// </summary>
472 /// <param name="index"></param>
473 /// <returns></returns>
474 protected object BaseGet( int index )
476 return ((_Item)m_ItemsArray[index]).value;
479 /// <summary>
480 /// SDK: Gets the value of the first entry with the specified key from the NameObjectCollectionBase instance.
481 /// </summary>
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.
489 if (item==null)
490 return null;
491 else
492 return item.value;
495 /// <summary>
496 /// SDK:Returns a String array that contains all the keys in the NameObjectCollectionBase instance.
497 /// </summary>
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;
506 return allKeys;
509 /// <summary>
510 /// SDK: Returns an Object array that contains all the values in the NameObjectCollectionBase instance.
511 /// </summary>
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);
520 return allValues;
523 protected object[] BaseGetAllValues( Type type )
525 if (type == null)
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);
532 return allValues;
535 protected string BaseGetKey( int index )
537 return ((_Item)m_ItemsArray[index]).key;
540 /// <summary>
541 /// Gets a value indicating whether the NameObjectCollectionBase instance contains entries whose keys are not a null reference
542 /// </summary>
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 )
551 int cnt = 0;
552 String key;
553 if (this.IsReadOnly)
554 throw new NotSupportedException("Collection is read-only");
555 if (name!=null)
557 m_ItemsContainer.Remove(name);
559 else {
560 m_NullKeyItem = null;
563 cnt = m_ItemsArray.Count;
564 for (int i=0 ; i< cnt; ){
565 key=BaseGetKey(i);
566 if (Equals (key, name)) {
567 m_ItemsArray.RemoveAt(i);
568 cnt--;
570 else
571 i++;
575 /// <summary>
576 ///
577 /// </summary>
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.
581 /// E.g. if
582 /// hashtable is [("Key1","value1")] and array contains [("Key1","value1")("Key1","value2")] then
583 /// after RemoveAt(1) the collection will be in following state:
584 /// hashtable:[]
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
588 /// </LAME>>
589 protected void BaseRemoveAt( int index )
591 if (this.IsReadOnly)
592 throw new NotSupportedException("Collection is read-only");
593 string key = BaseGetKey(index);
594 if (key!=null){
595 // TODO: see LAME description above
596 m_ItemsContainer.Remove(key);
598 else
599 m_NullKeyItem = null;
600 m_ItemsArray.RemoveAt(index);
603 /// <summary>
604 /// SDK: Sets the value of the entry at the specified index of the NameObjectCollectionBase instance.
605 /// </summary>
606 /// <param name="index"></param>
607 /// <param name="value"></param>
608 protected void BaseSet( int index, object value )
610 #if NET_2_0
611 if (this.IsReadOnly)
612 throw new NotSupportedException("Collection is read-only");
613 #endif
614 _Item item = (_Item)m_ItemsArray[index];
615 item.value = value;
618 /// <summary>
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.
620 /// </summary>
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 )
625 #if NET_2_0
626 if (this.IsReadOnly)
627 throw new NotSupportedException("Collection is read-only");
628 #endif
629 _Item item = FindFirstMatchedItem(name);
630 if (item!=null)
631 item.value=value;
632 else
633 BaseAdd(name, value);
636 [MonoTODO]
637 private _Item FindFirstMatchedItem(string name)
639 if (name!=null)
640 return (_Item)m_ItemsContainer[name];
641 else {
642 //TODO: consider null key case
643 return m_NullKeyItem;
647 internal bool Equals (string s1, string s2)
649 #if NET_2_0
650 if (m_comparer != null)
651 return (m_comparer.Compare (s1, s2) == 0);
652 else
653 return equality_comparer.Equals (s1, s2);
654 #else
655 return (m_comparer.Compare (s1, s2) == 0);
656 #endif