2 // System.Collections.Specialized.NameObjectCollectionBase.cs
\r
6 // Andreas Nahr (ClassDevelopment@A-SoftTech.com)
\r
8 // (C) Ximian, Inc. http://www.ximian.com
\r
12 // Permission is hereby granted, free of charge, to any person obtaining
\r
13 // a copy of this software and associated documentation files (the
\r
14 // "Software"), to deal in the Software without restriction, including
\r
15 // without limitation the rights to use, copy, modify, merge, publish,
\r
16 // distribute, sublicense, and/or sell copies of the Software, and to
\r
17 // permit persons to whom the Software is furnished to do so, subject to
\r
18 // the following conditions:
\r
20 // The above copyright notice and this permission notice shall be
\r
21 // included in all copies or substantial portions of the Software.
\r
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
\r
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
\r
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
\r
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
\r
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
\r
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
\r
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
33 using System.Collections;
\r
34 using System.Runtime.Serialization;
\r
36 namespace System.Collections.Specialized
\r
39 public abstract class NameObjectCollectionBase : ICollection, IEnumerable, ISerializable, IDeserializationCallback
\r
41 private Hashtable m_ItemsContainer;
\r
43 /// Extends Hashtable based Items container to support storing null-key pairs
\r
45 private _Item m_NullKeyItem;
\r
46 private ArrayList m_ItemsArray;
\r
47 private IHashCodeProvider m_hashprovider;
\r
48 private IComparer m_comparer;
\r
49 private int m_defCapacity;
\r
50 private bool m_readonly;
\r
51 SerializationInfo infoCopy;
\r
53 internal IComparer Comparer {
\r
54 get {return m_comparer;}
\r
57 internal IHashCodeProvider HashCodeProvider {
\r
58 get {return m_hashprovider;}
\r
61 internal class _Item
\r
64 public object value;
\r
65 public _Item(string key, object value)
\r
72 /// Implements IEnumerable interface for KeysCollection
\r
75 internal class _KeysEnumerator : IEnumerator
\r
77 private NameObjectCollectionBase m_collection;
\r
78 private int m_position;
\r
79 /*private*/internal _KeysEnumerator(NameObjectCollectionBase collection)
\r
81 m_collection = collection;
\r
84 public object Current
\r
88 if ((m_position<m_collection.Count)||(m_position<0))
\r
89 return m_collection.BaseGetKey(m_position);
\r
91 throw new InvalidOperationException();
\r
95 public bool MoveNext()
\r
97 return ((++m_position)<m_collection.Count)?true:false;
\r
106 /// SDK: Represents a collection of the String keys of a collection.
\r
109 public class KeysCollection : ICollection, IEnumerable
\r
111 private NameObjectCollectionBase m_collection;
\r
113 internal/*protected?*/ KeysCollection(NameObjectCollectionBase collection)
\r
115 this.m_collection = collection;
\r
117 public virtual string Get( int index )
\r
119 return m_collection.BaseGetKey(index);
\r
120 //throw new Exception("Not implemented yet");
\r
123 // ICollection methods -----------------------------------
\r
124 void ICollection.CopyTo(Array arr, int index)
\r
127 throw new ArgumentNullException("array can't be null");
\r
128 IEnumerator en = this.GetEnumerator();
\r
130 while (en.MoveNext())
\r
132 arr.SetValue(en.Current,i);
\r
137 bool ICollection.IsSynchronized
\r
143 object ICollection.SyncRoot
\r
146 return m_collection;
\r
150 /// Gets the number of keys in the NameObjectCollectionBase.KeysCollection
\r
155 return m_collection.Count;
\r
156 //throw new Exception("Not implemented yet");
\r
160 public string this [int index] {
\r
161 get { return Get (index); }
\r
164 // IEnumerable methods --------------------------------
\r
166 /// SDK: Returns an enumerator that can iterate through the NameObjectCollectionBase.KeysCollection.
\r
168 /// <returns></returns>
\r
169 public IEnumerator GetEnumerator()
\r
171 return new _KeysEnumerator(m_collection);
\r
172 // throw new Exception("Not implemented yet");
\r
178 //--------------- Protected Instance Constructors --------------
\r
181 /// SDK: Initializes a new instance of the NameObjectCollectionBase class that is empty.
\r
184 protected NameObjectCollectionBase():base()
\r
186 m_readonly = false;
\r
188 m_hashprovider = CaseInsensitiveHashCodeProvider.Default;
\r
189 m_comparer = CaseInsensitiveComparer.Default;
\r
192 /*m_ItemsContainer = new Hashtable(m_hashprovider,m_comparer);
\r
193 m_ItemsArray = new ArrayList();
\r
194 m_NullKeyItem = null;*/
\r
195 //TODO: consider common Reset() method
\r
198 protected NameObjectCollectionBase( int capacity )
\r
200 m_readonly = false;
\r
202 m_hashprovider = CaseInsensitiveHashCodeProvider.Default;
\r
203 m_comparer = CaseInsensitiveComparer.Default;
\r
204 m_defCapacity = capacity;
\r
206 /*m_ItemsContainer = new Hashtable(m_defCapacity, m_hashprovider,m_comparer);
\r
207 m_ItemsArray = new ArrayList();
\r
208 m_NullKeyItem = null; */
\r
209 //throw new Exception("Not implemented yet");
\r
211 protected NameObjectCollectionBase( IHashCodeProvider hashProvider, IComparer comparer )
\r
213 m_readonly = false;
\r
215 m_hashprovider = hashProvider;
\r
216 m_comparer = comparer;
\r
219 /*m_ItemsContainer = new Hashtable(m_hashprovider,m_comparer);
\r
220 m_ItemsArray = new ArrayList();
\r
221 m_NullKeyItem = null; */
\r
222 //throw new Exception("Not implemented yet");
\r
225 protected NameObjectCollectionBase (SerializationInfo info, StreamingContext context)
\r
230 protected NameObjectCollectionBase( int capacity, IHashCodeProvider hashProvider, IComparer comparer )
\r
232 m_readonly = false;
\r
234 m_hashprovider = hashProvider;
\r
235 m_comparer = comparer;
\r
236 m_defCapacity = capacity;
\r
238 /*m_ItemsContainer = new Hashtable(m_defCapacity,m_hashprovider,m_comparer);
\r
239 m_ItemsArray = new ArrayList();
\r
240 m_NullKeyItem = null; */
\r
242 //throw new Exception("Not implemented yet");
\r
245 private void Init(){
\r
246 m_ItemsContainer = new Hashtable(m_defCapacity,m_hashprovider,m_comparer);
\r
247 m_ItemsArray = new ArrayList();
\r
248 m_NullKeyItem = null;
\r
250 //--------------- Public Instance Properties -------------------
\r
251 public virtual NameObjectCollectionBase.KeysCollection Keys
\r
255 return new KeysCollection(this);
\r
256 //throw new Exception("Not implemented yet");
\r
260 //--------------- Public Instance Methods ----------------------
\r
263 /// SDK: Returns an enumerator that can iterate through the NameObjectCollectionBase.
\r
265 /// <remark>This enumerator returns the keys of the collection as strings.</remark>
\r
267 /// <returns></returns>
\r
268 public virtual IEnumerator GetEnumerator()
\r
270 return new _KeysEnumerator(this);
\r
275 public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
\r
278 throw new ArgumentNullException ("info");
\r
281 string [] keys = new string [count];
\r
282 object [] values = new object [count];
\r
284 foreach (_Item item in m_ItemsArray) {
\r
285 keys [i] = item.key;
\r
286 values [i] = item.value;
\r
290 info.AddValue ("m_hashprovider", m_hashprovider);
\r
291 info.AddValue ("m_comparer", m_comparer);
\r
292 info.AddValue ("m_readonly", m_readonly);
\r
293 info.AddValue ("keys", keys);
\r
294 info.AddValue ("values", values);
\r
298 public virtual int Count
\r
301 return m_ItemsArray.Count;
\r
302 //throw new Exception("Not implemented yet");
\r
305 bool ICollection.IsSynchronized
\r
307 get { return false; }
\r
309 object ICollection.SyncRoot
\r
311 get { return this; }
\r
314 void ICollection.CopyTo (Array array, int index)
\r
316 throw new NotImplementedException ();
\r
320 // IDeserializationCallback
\r
321 public virtual void OnDeserialization (object sender)
\r
323 SerializationInfo info = infoCopy;
\r
325 throw new SerializationException ("The object is not a SerializationInfo");
\r
328 m_hashprovider = (IHashCodeProvider) info.GetValue ("m_hashprovider",
\r
329 typeof (IHashCodeProvider));
\r
331 if (m_hashprovider == null)
\r
332 throw new SerializationException ("The hash provider is null");
\r
334 m_comparer = (IComparer) info.GetValue ("m_comparer", typeof (IComparer));
\r
335 if (m_comparer == null)
\r
336 throw new SerializationException ("The comparer is null");
\r
338 m_readonly = info.GetBoolean ("m_readonly");
\r
339 string [] keys = (string []) info.GetValue ("keys", typeof (string []));
\r
341 throw new SerializationException ("keys is null");
\r
343 object [] values = (object []) info.GetValue ("values", typeof (object []));
\r
344 if (values == null)
\r
345 throw new SerializationException ("values is null");
\r
348 int count = keys.Length;
\r
349 for (int i = 0; i < count; i++)
\r
350 BaseAdd (keys [i], values [i]);
\r
352 //--------------- Protected Instance Properties ----------------
\r
354 /// SDK: Gets or sets a value indicating whether the NameObjectCollectionBase instance is read-only.
\r
356 protected bool IsReadOnly
\r
366 //--------------- Protected Instance Methods -------------------
\r
368 /// Adds an Item with the specified key and value into the <see cref="NameObjectCollectionBase"/>NameObjectCollectionBase instance.
\r
370 /// <param name="name"></param>
\r
371 /// <param name="value"></param>
\r
372 protected void BaseAdd( string name, object value )
\r
374 if (this.IsReadOnly)
\r
375 throw new NotSupportedException("Collection is read-only");
\r
377 _Item newitem=new _Item(name, value);
\r
380 //todo: consider nullkey entry
\r
381 if (m_NullKeyItem==null)
\r
382 m_NullKeyItem = newitem;
\r
385 if (m_ItemsContainer[name]==null){
\r
386 m_ItemsContainer.Add(name,newitem);
\r
388 m_ItemsArray.Add(newitem);
\r
391 protected void BaseClear()
\r
393 if (this.IsReadOnly)
\r
394 throw new NotSupportedException("Collection is read-only");
\r
396 //throw new Exception("Not implemented yet");
\r
399 /// SDK: Gets the value of the entry at the specified index of the NameObjectCollectionBase instance.
\r
401 /// <param name="index"></param>
\r
402 /// <returns></returns>
\r
403 protected object BaseGet( int index )
\r
405 return ((_Item)m_ItemsArray[index]).value;
\r
406 //throw new Exception("Not implemented yet");
\r
409 /// SDK: Gets the value of the first entry with the specified key from the NameObjectCollectionBase instance.
\r
411 /// <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>
\r
412 /// <param name="name"></param>
\r
413 /// <returns></returns>
\r
414 protected object BaseGet( string name )
\r
416 _Item item = FindFirstMatchedItem(name);
\r
417 /// 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.
\r
425 /// SDK:Returns a String array that contains all the keys in the NameObjectCollectionBase instance.
\r
427 /// <returns>A String array that contains all the keys in the NameObjectCollectionBase instance.</returns>
\r
428 protected string[] BaseGetAllKeys()
\r
430 int cnt = m_ItemsArray.Count;
\r
431 string[] allKeys = new string[cnt];
\r
432 for(int i=0; i<cnt; i++)
\r
433 allKeys[i] = BaseGetKey(i);//((_Item)m_ItemsArray[i]).key;
\r
439 /// SDK: Returns an Object array that contains all the values in the NameObjectCollectionBase instance.
\r
441 /// <returns>An Object array that contains all the values in the NameObjectCollectionBase instance.</returns>
\r
442 protected object[] BaseGetAllValues()
\r
444 int cnt = m_ItemsArray.Count;
\r
445 object[] allValues = new object[cnt];
\r
446 for(int i=0; i<cnt; i++)
\r
447 allValues[i] = BaseGet(i);
\r
452 protected object[] BaseGetAllValues( Type type )
\r
455 throw new ArgumentNullException("'type' argument can't be null");
\r
456 int cnt = m_ItemsArray.Count;
\r
457 object[] allValues = (object[]) Array.CreateInstance (type, cnt);
\r
458 for(int i=0; i<cnt; i++)
\r
459 allValues[i] = BaseGet(i);
\r
464 protected string BaseGetKey( int index )
\r
466 return ((_Item)m_ItemsArray[index]).key;
\r
467 //throw new Exception("Not implemented yet");
\r
470 /// Gets a value indicating whether the NameObjectCollectionBase instance contains entries whose keys are not a null reference
\r
472 /// <returns>true if the NameObjectCollectionBase instance contains entries whose keys are not a null reference otherwise, false.</returns>
\r
473 protected bool BaseHasKeys()
\r
475 return (m_ItemsContainer.Count>0);
\r
476 // throw new Exception("Not implemented yet");
\r
479 protected void BaseRemove( string name )
\r
483 if (this.IsReadOnly)
\r
484 throw new NotSupportedException("Collection is read-only");
\r
487 m_ItemsContainer.Remove(name);
\r
490 m_NullKeyItem = null;
\r
493 cnt = m_ItemsArray.Count;
\r
494 for (int i=0 ; i< cnt; ){
\r
496 if (m_comparer.Compare (key, name) == 0) {
\r
497 m_ItemsArray.RemoveAt(i);
\r
509 /// <param name="index"></param>
\r
510 /// <LAME>This function implemented the way Microsoft implemented it -
\r
511 /// 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.
\r
513 /// hashtable is [("Key1","value1")] and array contains [("Key1","value1")("Key1","value2")] then
\r
514 /// after RemoveAt(1) the collection will be in following state:
\r
516 /// array: [("Key1","value1")]
\r
517 /// It's ok only then the key is uniquely assosiated with the value
\r
518 /// To fix it a comparsion of objects stored under the same key in the hashtable and in the arraylist should be added
\r
521 protected void BaseRemoveAt( int index )
\r
523 if (this.IsReadOnly)
\r
524 throw new NotSupportedException("Collection is read-only");
\r
525 string key = BaseGetKey(index);
\r
527 // TODO: see LAME description above
\r
528 m_ItemsContainer.Remove(key);
\r
531 m_NullKeyItem = null;
\r
532 m_ItemsArray.RemoveAt(index);
\r
533 // throw new Exception("Not implemented yet");
\r
536 /// SDK: Sets the value of the entry at the specified index of the NameObjectCollectionBase instance.
\r
538 /// <param name="index"></param>
\r
539 /// <param name="value"></param>
\r
540 protected void BaseSet( int index, object value )
\r
542 if (this.IsReadOnly)
\r
543 throw new NotSupportedException("Collection is read-only");
\r
544 _Item item = (_Item)m_ItemsArray[index];
\r
545 item.value = value;
\r
546 //throw new Exception("Not implemented yet");
\r
549 /// 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.
\r
551 /// <param name="name">The String key of the entry to set. The key can be a null reference </param>
\r
552 /// <param name="value">The Object that represents the new value of the entry to set. The value can be a null reference</param>
\r
553 protected void BaseSet( string name, object value )
\r
555 if (this.IsReadOnly)
\r
556 throw new NotSupportedException("Collection is read-only");
\r
557 _Item item = FindFirstMatchedItem(name);
\r
561 BaseAdd(name, value);
\r
563 //throw new Exception("Not implemented yet");
\r
566 private _Item FindFirstMatchedItem(string name)
\r
569 return (_Item)m_ItemsContainer[name];
\r
571 //TODO: consider null key case
\r
572 return m_NullKeyItem;
\r
573 //throw new Exception("Not implemented yet");
\r