NameObjectCollectionBase.cs 18 KB

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