cleol
[mcs.git] / class / Mono.Cecil / Mono.Cecil / NameObjectCollectionBase.cs
blob80b0c11d519f8033d30d522b43f96df175c559f5
1 using System;
2 using System.Collections;
3 using System.Runtime.Serialization;
5 #if NO_SYSTEM_DLL
6 namespace System.Collections.Specialized
8 [Serializable]
9 public abstract class NameObjectCollectionBase : ICollection, IEnumerable, ISerializable, IDeserializationCallback
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;
26 internal IEqualityComparer EqualityComparer {
27 get { return equality_comparer; }
29 #endif
30 internal IComparer Comparer
32 get { return m_comparer; }
35 internal IHashCodeProvider HashCodeProvider
37 get { return m_hashprovider; }
40 internal class _Item
42 public string key;
43 public object value;
44 public _Item(string key, object value)
46 this.key = key;
47 this.value = value;
50 /// <summary>
51 /// Implements IEnumerable interface for KeysCollection
52 /// </summary>
53 [Serializable]
54 internal class _KeysEnumerator : IEnumerator
56 private NameObjectCollectionBase m_collection;
57 private int m_position;
59 internal _KeysEnumerator(NameObjectCollectionBase collection)
61 m_collection = collection;
62 Reset();
64 public object Current
67 get
69 if ((m_position < m_collection.Count) || (m_position < 0))
70 return m_collection.BaseGetKey(m_position);
71 else
72 throw new InvalidOperationException();
76 public bool MoveNext()
78 return ((++m_position) < m_collection.Count);
80 public void Reset()
82 m_position = -1;
86 /// <summary>
87 /// SDK: Represents a collection of the String keys of a collection.
88 /// </summary>
89 [Serializable]
90 public class KeysCollection : ICollection, IEnumerable
92 private NameObjectCollectionBase m_collection;
94 internal KeysCollection(NameObjectCollectionBase collection)
96 this.m_collection = collection;
99 public virtual string Get(int index)
101 return m_collection.BaseGetKey(index);
104 // ICollection methods -----------------------------------
105 void ICollection.CopyTo(Array array, int arrayIndex)
107 ArrayList items = m_collection.m_ItemsArray;
108 #if NET_2_0
109 if (null == array)
110 throw new ArgumentNullException ("array");
112 if (arrayIndex < 0)
113 throw new ArgumentOutOfRangeException ("arrayIndex");
115 if ((array.Length > 0) && (arrayIndex >= array.Length))
116 throw new ArgumentException ("arrayIndex is equal to or greater than array.Length");
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
122 if (array != null && array.Rank > 1)
123 throw new ArgumentException("array is multidimensional");
125 object[] objArray = (object[])array;
126 for (int i = 0; i < items.Count; i++, arrayIndex++)
127 objArray[arrayIndex] = ((_Item)items[i]).key;
130 bool ICollection.IsSynchronized
134 return false;
137 object ICollection.SyncRoot
141 return m_collection;
144 /// <summary>
145 /// Gets the number of keys in the NameObjectCollectionBase.KeysCollection
146 /// </summary>
147 public int Count
151 return m_collection.Count;
155 public string this[int index]
157 get { return Get(index); }
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()
167 return new _KeysEnumerator(m_collection);
171 //--------------- Protected Instance Constructors --------------
173 /// <summary>
174 /// SDK: Initializes a new instance of the NameObjectCollectionBase class that is empty.
175 /// </summary>
176 protected NameObjectCollectionBase()
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();
190 protected NameObjectCollectionBase(int capacity)
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();
204 #if NET_2_0
206 internal NameObjectCollectionBase (IEqualityComparer equalityComparer, IComparer comparer, IHashCodeProvider hcp)
208 equality_comparer = equalityComparer;
209 m_comparer = comparer;
210 m_hashprovider = hcp;
211 m_readonly = false;
212 m_defCapacity = 0;
213 Init ();
216 protected NameObjectCollectionBase (IEqualityComparer equalityComparer) : this( (equalityComparer == null ? StringComparer.InvariantCultureIgnoreCase : equalityComparer), null, null)
220 [Obsolete ("Use NameObjectCollectionBase(IEqualityComparer)")]
221 #endif
222 protected NameObjectCollectionBase(IHashCodeProvider hashProvider, IComparer comparer)
224 m_comparer = comparer;
225 m_hashprovider = hashProvider;
226 m_readonly = false;
227 m_defCapacity = 0;
228 Init();
231 protected NameObjectCollectionBase(SerializationInfo info, StreamingContext context)
233 infoCopy = info;
236 #if NET_2_0
237 protected NameObjectCollectionBase (int capacity, IEqualityComparer equalityComparer)
239 m_readonly = false;
240 equality_comparer = (equalityComparer == null ? StringComparer.InvariantCultureIgnoreCase : equalityComparer);
241 m_defCapacity = capacity;
242 Init();
245 [Obsolete ("Use NameObjectCollectionBase(int,IEqualityComparer)")]
246 #endif
247 protected NameObjectCollectionBase(int capacity, IHashCodeProvider hashProvider, IComparer comparer)
249 m_readonly = false;
251 m_hashprovider = hashProvider;
252 m_comparer = comparer;
253 m_defCapacity = capacity;
254 Init();
257 private void Init()
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;
271 //--------------- Public Instance Properties -------------------
273 public virtual NameObjectCollectionBase.KeysCollection Keys
277 if (keyscoll == null)
278 keyscoll = new KeysCollection(this);
279 return keyscoll;
283 //--------------- Public Instance Methods ----------------------
285 /// <summary>
286 /// SDK: Returns an enumerator that can iterate through the NameObjectCollectionBase.
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()
297 return new _KeysEnumerator(this);
300 // ISerializable
301 public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
303 if (info == null)
304 throw new ArgumentNullException("info");
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)
312 keys[i] = item.key;
313 values[i] = item.value;
314 i++;
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));
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[]));
336 // ICollection
337 public virtual int Count
341 return m_ItemsArray.Count;
345 bool ICollection.IsSynchronized
347 get { return false; }
350 object ICollection.SyncRoot
352 get { return this; }
355 void ICollection.CopyTo(Array array, int index)
357 ((ICollection)Keys).CopyTo(array, index);
360 // IDeserializationCallback
361 public virtual void OnDeserialization(object sender)
363 SerializationInfo info = infoCopy;
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;
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");
382 #else
383 if (m_hashprovider == null)
384 throw new SerializationException("The hash provider is null");
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");
395 object[] values = (object[])info.GetValue("Values", typeof(object[]));
396 if (values == null)
397 throw new SerializationException("values is null");
399 Init();
400 int count = keys.Length;
401 for (int i = 0; i < count; i++)
402 BaseAdd(keys[i], values[i]);
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
413 return m_readonly;
417 m_readonly = value;
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)
429 if (this.IsReadOnly)
430 throw new NotSupportedException("Collection is read-only");
432 _Item newitem = new _Item(name, value);
434 if (name == null)
436 //todo: consider nullkey entry
437 if (m_NullKeyItem == null)
438 m_NullKeyItem = newitem;
440 else
441 if (m_ItemsContainer[name] == null)
443 m_ItemsContainer.Add(name, newitem);
445 m_ItemsArray.Add(newitem);
448 protected void BaseClear()
450 if (this.IsReadOnly)
451 throw new NotSupportedException("Collection is read-only");
452 Init();
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)
462 return ((_Item)m_ItemsArray[index]).value;
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)
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;
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()
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;
492 return allKeys;
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()
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);
506 return allValues;
509 protected object[] BaseGetAllValues(Type type)
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);
518 return allValues;
521 protected string BaseGetKey(int index)
523 return ((_Item)m_ItemsArray[index]).key;
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()
532 return (m_ItemsContainer.Count > 0);
535 protected void BaseRemove(string name)
537 int cnt = 0;
538 String key;
539 if (this.IsReadOnly)
540 throw new NotSupportedException("Collection is read-only");
541 if (name != null)
543 m_ItemsContainer.Remove(name);
545 else
547 m_NullKeyItem = null;
550 cnt = m_ItemsArray.Count;
551 for (int i = 0; i < cnt; )
553 key = BaseGetKey(i);
554 if (Equals(key, name))
556 m_ItemsArray.RemoveAt(i);
557 cnt--;
559 else
560 i++;
564 /// <summary>
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)
580 if (this.IsReadOnly)
581 throw new NotSupportedException("Collection is read-only");
582 string key = BaseGetKey(index);
583 if (key != null)
585 // TODO: see LAME description above
586 m_ItemsContainer.Remove(key);
588 else
589 m_NullKeyItem = null;
590 m_ItemsArray.RemoveAt(index);
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)
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;
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)
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);
626 private _Item FindFirstMatchedItem(string name)
628 if (name != null)
629 return (_Item)m_ItemsContainer[name];
630 else
632 //TODO: consider null key case
633 return m_NullKeyItem;
637 internal bool Equals(string s1, string s2)
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
650 #endif