cleol
[mono.git] / mcs / class / Mono.Cecil / Mono.Cecil / NameObjectCollectionBase.cs
1 using System;
2 using System.Collections;
3 using System.Runtime.Serialization;
4
5 #if NO_SYSTEM_DLL
6 namespace System.Collections.Specialized
7 {
8         [Serializable]
9         public abstract class NameObjectCollectionBase : ICollection, IEnumerable, ISerializable, IDeserializationCallback
10         {
11                 private Hashtable m_ItemsContainer;
12                 /// <summary>
13                 /// Extends Hashtable based Items container to support storing null-key pairs
14                 /// </summary>
15                 private _Item m_NullKeyItem;
16                 private ArrayList m_ItemsArray;
17                 private IHashCodeProvider m_hashprovider;
18                 private IComparer m_comparer;
19                 private int m_defCapacity;
20                 private bool m_readonly;
21                 SerializationInfo infoCopy;
22                 private KeysCollection keyscoll;
23 #if NET_2_0
24                 private IEqualityComparer equality_comparer;
25
26                 internal IEqualityComparer EqualityComparer {
27                         get { return equality_comparer; }
28                 }
29 #endif
30                 internal IComparer Comparer
31                 {
32                         get { return m_comparer; }
33                 }
34
35                 internal IHashCodeProvider HashCodeProvider
36                 {
37                         get { return m_hashprovider; }
38                 }
39
40                 internal class _Item
41                 {
42                         public string key;
43                         public object value;
44                         public _Item(string key, object value)
45                         {
46                                 this.key = key;
47                                 this.value = value;
48                         }
49                 }
50                 /// <summary>
51                 /// Implements IEnumerable interface for KeysCollection
52                 /// </summary>
53                 [Serializable]
54                 internal class _KeysEnumerator : IEnumerator
55                 {
56                         private NameObjectCollectionBase m_collection;
57                         private int m_position;
58
59                         internal _KeysEnumerator(NameObjectCollectionBase collection)
60                         {
61                                 m_collection = collection;
62                                 Reset();
63                         }
64                         public object Current
65                         {
66
67                                 get
68                                 {
69                                         if ((m_position < m_collection.Count) || (m_position < 0))
70                                                 return m_collection.BaseGetKey(m_position);
71                                         else
72                                                 throw new InvalidOperationException();
73                                 }
74
75                         }
76                         public bool MoveNext()
77                         {
78                                 return ((++m_position) < m_collection.Count);
79                         }
80                         public void Reset()
81                         {
82                                 m_position = -1;
83                         }
84                 }
85
86                 /// <summary>
87                 /// SDK: Represents a collection of the String keys of a collection.
88                 /// </summary>
89                 [Serializable]
90                 public class KeysCollection : ICollection, IEnumerable
91                 {
92                         private NameObjectCollectionBase m_collection;
93
94                         internal KeysCollection(NameObjectCollectionBase collection)
95                         {
96                                 this.m_collection = collection;
97                         }
98
99                         public virtual string Get(int index)
100                         {
101                                 return m_collection.BaseGetKey(index);
102                         }
103
104                         // ICollection methods -----------------------------------
105                         void ICollection.CopyTo(Array array, int arrayIndex)
106                         {
107                                 ArrayList items = m_collection.m_ItemsArray;
108 #if NET_2_0
109                                 if (null == array)
110                                         throw new ArgumentNullException ("array");
111
112                                 if (arrayIndex < 0)
113                                         throw new ArgumentOutOfRangeException ("arrayIndex");
114
115                                 if ((array.Length > 0) && (arrayIndex >= array.Length))
116                                         throw new ArgumentException ("arrayIndex is equal to or greater than array.Length");
117
118                                 if (arrayIndex + items.Count > array.Length)
119                                         throw new ArgumentException ("Not enough room from arrayIndex to end of array for this KeysCollection");
120 #endif
121
122                                 if (array != null && array.Rank > 1)
123                                         throw new ArgumentException("array is multidimensional");
124
125                                 object[] objArray = (object[])array;
126                                 for (int i = 0; i < items.Count; i++, arrayIndex++)
127                                         objArray[arrayIndex] = ((_Item)items[i]).key;
128                         }
129
130                         bool ICollection.IsSynchronized
131                         {
132                                 get
133                                 {
134                                         return false;
135                                 }
136                         }
137                         object ICollection.SyncRoot
138                         {
139                                 get
140                                 {
141                                         return m_collection;
142                                 }
143                         }
144                         /// <summary>
145                         /// Gets the number of keys in the NameObjectCollectionBase.KeysCollection
146                         /// </summary>
147                         public int Count
148                         {
149                                 get
150                                 {
151                                         return m_collection.Count;
152                                 }
153                         }
154
155                         public string this[int index]
156                         {
157                                 get { return Get(index); }
158                         }
159
160                         // IEnumerable methods --------------------------------
161                         /// <summary>
162                         /// SDK: Returns an enumerator that can iterate through the NameObjectCollectionBase.KeysCollection.
163                         /// </summary>
164                         /// <returns></returns>
165                         public IEnumerator GetEnumerator()
166                         {
167                                 return new _KeysEnumerator(m_collection);
168                         }
169                 }
170
171                 //--------------- Protected Instance Constructors --------------
172
173                 /// <summary>
174                 /// SDK: Initializes a new instance of the NameObjectCollectionBase class that is empty.
175                 /// </summary>
176                 protected NameObjectCollectionBase()
177                 {
178                         m_readonly = false;
179 #if NET_1_0
180                         m_hashprovider = CaseInsensitiveHashCodeProvider.Default;
181                         m_comparer = CaseInsensitiveComparer.Default;
182 #else
183                         m_hashprovider = CaseInsensitiveHashCodeProvider.DefaultInvariant;
184                         m_comparer = CaseInsensitiveComparer.DefaultInvariant;
185 #endif
186                         m_defCapacity = 0;
187                         Init();
188                 }
189
190                 protected NameObjectCollectionBase(int capacity)
191                 {
192                         m_readonly = false;
193 #if NET_1_0
194                         m_hashprovider = CaseInsensitiveHashCodeProvider.Default;
195                         m_comparer = CaseInsensitiveComparer.Default;
196 #else
197                         m_hashprovider = CaseInsensitiveHashCodeProvider.DefaultInvariant;
198                         m_comparer = CaseInsensitiveComparer.DefaultInvariant;
199 #endif
200                         m_defCapacity = capacity;
201                         Init();
202                 }
203
204 #if NET_2_0
205
206                 internal NameObjectCollectionBase (IEqualityComparer equalityComparer, IComparer comparer, IHashCodeProvider hcp)
207                 {
208                         equality_comparer = equalityComparer;
209                         m_comparer = comparer;
210                         m_hashprovider = hcp;
211                         m_readonly = false;
212                         m_defCapacity = 0;
213                         Init ();
214                 }
215
216                 protected NameObjectCollectionBase (IEqualityComparer equalityComparer) : this( (equalityComparer == null ? StringComparer.InvariantCultureIgnoreCase : equalityComparer), null, null)
217                 {
218                 }
219
220                 [Obsolete ("Use NameObjectCollectionBase(IEqualityComparer)")]
221 #endif
222                 protected NameObjectCollectionBase(IHashCodeProvider hashProvider, IComparer comparer)
223                 {
224                         m_comparer = comparer;
225                         m_hashprovider = hashProvider;
226                         m_readonly = false;
227                         m_defCapacity = 0;
228                         Init();
229                 }
230
231                 protected NameObjectCollectionBase(SerializationInfo info, StreamingContext context)
232                 {
233                         infoCopy = info;
234                 }
235
236 #if NET_2_0
237                 protected NameObjectCollectionBase (int capacity, IEqualityComparer equalityComparer)
238                 {
239                         m_readonly = false;
240                         equality_comparer = (equalityComparer == null ? StringComparer.InvariantCultureIgnoreCase : equalityComparer);
241                         m_defCapacity = capacity;
242                         Init();
243                 }
244
245                 [Obsolete ("Use NameObjectCollectionBase(int,IEqualityComparer)")]
246 #endif
247                 protected NameObjectCollectionBase(int capacity, IHashCodeProvider hashProvider, IComparer comparer)
248                 {
249                         m_readonly = false;
250
251                         m_hashprovider = hashProvider;
252                         m_comparer = comparer;
253                         m_defCapacity = capacity;
254                         Init();
255                 }
256
257                 private void Init()
258                 {
259 #if NET_2_0
260                         if (equality_comparer != null)
261                                 m_ItemsContainer = new Hashtable (m_defCapacity, equality_comparer);
262                         else
263                                 m_ItemsContainer = new Hashtable (m_defCapacity, m_hashprovider, m_comparer);
264 #else
265                         m_ItemsContainer = new Hashtable(m_defCapacity, m_hashprovider, m_comparer);
266 #endif
267                         m_ItemsArray = new ArrayList();
268                         m_NullKeyItem = null;
269                 }
270
271                 //--------------- Public Instance Properties -------------------
272
273                 public virtual NameObjectCollectionBase.KeysCollection Keys
274                 {
275                         get
276                         {
277                                 if (keyscoll == null)
278                                         keyscoll = new KeysCollection(this);
279                                 return keyscoll;
280                         }
281                 }
282
283                 //--------------- Public Instance Methods ----------------------
284                 //
285                 /// <summary>
286                 /// SDK: Returns an enumerator that can iterate through the NameObjectCollectionBase.
287                 ///
288                 /// <remark>This enumerator returns the keys of the collection as strings.</remark>
289                 /// </summary>
290                 /// <returns></returns>
291                 public
292 #if NET_2_0
293                 virtual
294 #endif
295  IEnumerator GetEnumerator()
296                 {
297                         return new _KeysEnumerator(this);
298                 }
299
300                 // ISerializable
301                 public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
302                 {
303                         if (info == null)
304                                 throw new ArgumentNullException("info");
305
306                         int count = Count;
307                         string[] keys = new string[count];
308                         object[] values = new object[count];
309                         int i = 0;
310                         foreach (_Item item in m_ItemsArray)
311                         {
312                                 keys[i] = item.key;
313                                 values[i] = item.value;
314                                 i++;
315                         }
316
317 #if NET_2_0
318                         if (equality_comparer != null) {
319                                 info.AddValue ("KeyComparer", equality_comparer, typeof (IEqualityComparer));
320                                 info.AddValue ("Version", 4, typeof (int));
321                         } else {
322                                 info.AddValue ("HashProvider", m_hashprovider, typeof (IHashCodeProvider));
323                                 info.AddValue ("Comparer", m_comparer, typeof (IComparer));
324                                 info.AddValue ("Version", 2, typeof (int));
325                         }
326 #else
327                         info.AddValue("HashProvider", m_hashprovider, typeof(IHashCodeProvider));
328                         info.AddValue("Comparer", m_comparer, typeof(IComparer));
329 #endif
330                         info.AddValue("ReadOnly", m_readonly);
331                         info.AddValue("Count", count);
332                         info.AddValue("Keys", keys, typeof(string[]));
333                         info.AddValue("Values", values, typeof(object[]));
334                 }
335
336                 // ICollection
337                 public virtual int Count
338                 {
339                         get
340                         {
341                                 return m_ItemsArray.Count;
342                         }
343                 }
344
345                 bool ICollection.IsSynchronized
346                 {
347                         get { return false; }
348                 }
349
350                 object ICollection.SyncRoot
351                 {
352                         get { return this; }
353                 }
354
355                 void ICollection.CopyTo(Array array, int index)
356                 {
357                         ((ICollection)Keys).CopyTo(array, index);
358                 }
359
360                 // IDeserializationCallback
361                 public virtual void OnDeserialization(object sender)
362                 {
363                         SerializationInfo info = infoCopy;
364
365                         // If a subclass overrides the serialization constructor
366                         // and inplements its own serialization process, infoCopy will
367                         // be null and we can ignore this callback.
368                         if (info == null)
369                                 return;
370
371                         infoCopy = null;
372                         m_hashprovider = (IHashCodeProvider)info.GetValue("HashProvider",
373                                                                                 typeof(IHashCodeProvider));
374 #if NET_2_0
375                         if (m_hashprovider == null) {
376                                 equality_comparer = (IEqualityComparer) info.GetValue ("KeyComparer", typeof (IEqualityComparer));
377                         } else {
378                                 m_comparer = (IComparer) info.GetValue ("Comparer", typeof (IComparer));
379                                 if (m_comparer == null)
380                                         throw new SerializationException ("The comparer is null");
381                         }
382 #else
383                         if (m_hashprovider == null)
384                                 throw new SerializationException("The hash provider is null");
385
386                         m_comparer = (IComparer)info.GetValue("Comparer", typeof(IComparer));
387                         if (m_comparer == null)
388                                 throw new SerializationException("The comparer is null");
389 #endif
390                         m_readonly = info.GetBoolean("ReadOnly");
391                         string[] keys = (string[])info.GetValue("Keys", typeof(string[]));
392                         if (keys == null)
393                                 throw new SerializationException("keys is null");
394
395                         object[] values = (object[])info.GetValue("Values", typeof(object[]));
396                         if (values == null)
397                                 throw new SerializationException("values is null");
398
399                         Init();
400                         int count = keys.Length;
401                         for (int i = 0; i < count; i++)
402                                 BaseAdd(keys[i], values[i]);
403                 }
404
405                 //--------------- Protected Instance Properties ----------------
406                 /// <summary>
407                 /// SDK: Gets or sets a value indicating whether the NameObjectCollectionBase instance is read-only.
408                 /// </summary>
409                 protected bool IsReadOnly
410                 {
411                         get
412                         {
413                                 return m_readonly;
414                         }
415                         set
416                         {
417                                 m_readonly = value;
418                         }
419                 }
420
421                 //--------------- Protected Instance Methods -------------------
422                 /// <summary>
423                 /// Adds an Item with the specified key and value into the <see cref="NameObjectCollectionBase"/>NameObjectCollectionBase instance.
424                 /// </summary>
425                 /// <param name="name"></param>
426                 /// <param name="value"></param>
427                 protected void BaseAdd(string name, object value)
428                 {
429                         if (this.IsReadOnly)
430                                 throw new NotSupportedException("Collection is read-only");
431
432                         _Item newitem = new _Item(name, value);
433
434                         if (name == null)
435                         {
436                                 //todo: consider nullkey entry
437                                 if (m_NullKeyItem == null)
438                                         m_NullKeyItem = newitem;
439                         }
440                         else
441                                 if (m_ItemsContainer[name] == null)
442                                 {
443                                         m_ItemsContainer.Add(name, newitem);
444                                 }
445                         m_ItemsArray.Add(newitem);
446                 }
447
448                 protected void BaseClear()
449                 {
450                         if (this.IsReadOnly)
451                                 throw new NotSupportedException("Collection is read-only");
452                         Init();
453                 }
454
455                 /// <summary>
456                 /// SDK: Gets the value of the entry at the specified index of the NameObjectCollectionBase instance.
457                 /// </summary>
458                 /// <param name="index"></param>
459                 /// <returns></returns>
460                 protected object BaseGet(int index)
461                 {
462                         return ((_Item)m_ItemsArray[index]).value;
463                 }
464
465                 /// <summary>
466                 /// SDK: Gets the value of the first entry with the specified key from the NameObjectCollectionBase instance.
467                 /// </summary>
468                 /// <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>
469                 /// <param name="name"></param>
470                 /// <returns></returns>
471                 protected object BaseGet(string name)
472                 {
473                         _Item item = FindFirstMatchedItem(name);
474                         /// 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                         if (item == null)
476                                 return null;
477                         else
478                                 return item.value;
479                 }
480
481                 /// <summary>
482                 /// SDK:Returns a String array that contains all the keys in the NameObjectCollectionBase instance.
483                 /// </summary>
484                 /// <returns>A String array that contains all the keys in the NameObjectCollectionBase instance.</returns>
485                 protected string[] BaseGetAllKeys()
486                 {
487                         int cnt = m_ItemsArray.Count;
488                         string[] allKeys = new string[cnt];
489                         for (int i = 0; i < cnt; i++)
490                                 allKeys[i] = BaseGetKey(i);//((_Item)m_ItemsArray[i]).key;
491
492                         return allKeys;
493                 }
494
495                 /// <summary>
496                 /// SDK: Returns an Object array that contains all the values in the NameObjectCollectionBase instance.
497                 /// </summary>
498                 /// <returns>An Object array that contains all the values in the NameObjectCollectionBase instance.</returns>
499                 protected object[] BaseGetAllValues()
500                 {
501                         int cnt = m_ItemsArray.Count;
502                         object[] allValues = new object[cnt];
503                         for (int i = 0; i < cnt; i++)
504                                 allValues[i] = BaseGet(i);
505
506                         return allValues;
507                 }
508
509                 protected object[] BaseGetAllValues(Type type)
510                 {
511                         if (type == null)
512                                 throw new ArgumentNullException("'type' argument can't be null");
513                         int cnt = m_ItemsArray.Count;
514                         object[] allValues = (object[])Array.CreateInstance(type, cnt);
515                         for (int i = 0; i < cnt; i++)
516                                 allValues[i] = BaseGet(i);
517
518                         return allValues;
519                 }
520
521                 protected string BaseGetKey(int index)
522                 {
523                         return ((_Item)m_ItemsArray[index]).key;
524                 }
525
526                 /// <summary>
527                 /// Gets a value indicating whether the NameObjectCollectionBase instance contains entries whose keys are not a null reference
528                 /// </summary>
529                 /// <returns>true if the NameObjectCollectionBase instance contains entries whose keys are not a null reference otherwise, false.</returns>
530                 protected bool BaseHasKeys()
531                 {
532                         return (m_ItemsContainer.Count > 0);
533                 }
534
535                 protected void BaseRemove(string name)
536                 {
537                         int cnt = 0;
538                         String key;
539                         if (this.IsReadOnly)
540                                 throw new NotSupportedException("Collection is read-only");
541                         if (name != null)
542                         {
543                                 m_ItemsContainer.Remove(name);
544                         }
545                         else
546                         {
547                                 m_NullKeyItem = null;
548                         }
549
550                         cnt = m_ItemsArray.Count;
551                         for (int i = 0; i < cnt; )
552                         {
553                                 key = BaseGetKey(i);
554                                 if (Equals(key, name))
555                                 {
556                                         m_ItemsArray.RemoveAt(i);
557                                         cnt--;
558                                 }
559                                 else
560                                         i++;
561                         }
562                 }
563
564                 /// <summary>
565                 ///
566                 /// </summary>
567                 /// <param name="index"></param>
568                 /// <LAME>This function implemented the way Microsoft implemented it -
569                 /// 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.
570                 /// E.g. if
571                 /// hashtable is [("Key1","value1")] and array contains [("Key1","value1")("Key1","value2")] then
572                 /// after RemoveAt(1) the collection will be in following state:
573                 /// hashtable:[]
574                 /// array: [("Key1","value1")]
575                 /// It's ok only then the key is uniquely assosiated with the value
576                 /// To fix it a comparsion of objects stored under the same key in the hashtable and in the arraylist should be added
577                 /// </LAME>>
578                 protected void BaseRemoveAt(int index)
579                 {
580                         if (this.IsReadOnly)
581                                 throw new NotSupportedException("Collection is read-only");
582                         string key = BaseGetKey(index);
583                         if (key != null)
584                         {
585                                 // TODO: see LAME description above
586                                 m_ItemsContainer.Remove(key);
587                         }
588                         else
589                                 m_NullKeyItem = null;
590                         m_ItemsArray.RemoveAt(index);
591                 }
592
593                 /// <summary>
594                 /// SDK: Sets the value of the entry at the specified index of the NameObjectCollectionBase instance.
595                 /// </summary>
596                 /// <param name="index"></param>
597                 /// <param name="value"></param>
598                 protected void BaseSet(int index, object value)
599                 {
600 #if NET_2_0
601                         if (this.IsReadOnly)
602                                 throw new NotSupportedException("Collection is read-only");
603 #endif
604                         _Item item = (_Item)m_ItemsArray[index];
605                         item.value = value;
606                 }
607
608                 /// <summary>
609                 /// 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.
610                 /// </summary>
611                 /// <param name="name">The String key of the entry to set. The key can be a null reference </param>
612                 /// <param name="value">The Object that represents the new value of the entry to set. The value can be a null reference</param>
613                 protected void BaseSet(string name, object value)
614                 {
615 #if NET_2_0
616                         if (this.IsReadOnly)
617                                 throw new NotSupportedException("Collection is read-only");
618 #endif
619                         _Item item = FindFirstMatchedItem(name);
620                         if (item != null)
621                                 item.value = value;
622                         else
623                                 BaseAdd(name, value);
624                 }
625
626                 private _Item FindFirstMatchedItem(string name)
627                 {
628                         if (name != null)
629                                 return (_Item)m_ItemsContainer[name];
630                         else
631                         {
632                                 //TODO: consider null key case
633                                 return m_NullKeyItem;
634                         }
635                 }
636
637                 internal bool Equals(string s1, string s2)
638                 {
639 #if NET_2_0
640                         if (m_comparer != null)
641                                 return (m_comparer.Compare (s1, s2) == 0);
642                         else
643                                 return equality_comparer.Equals (s1, s2);
644 #else
645                         return (m_comparer.Compare(s1, s2) == 0);
646 #endif
647                 }
648         }
649 }
650 #endif