XmlAttributeCollection.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. // System.Xml.XmlAttributeCollection.cs
  2. //
  3. // Author: Daniel Weber ([email protected])
  4. //
  5. // Implementation of abstract Xml.XmlAttributeCollection class
  6. //
  7. using System;
  8. using System.Collections;
  9. namespace System.Xml
  10. {
  11. /// <summary>
  12. /// A collection of Attributes that can be accessed by index or name(space)
  13. /// Derived from XmlNamedNodeMap
  14. /// <seealso cref="XmlNamedNodeMap"/>
  15. /// </summary>
  16. public class XmlAttributeCollection : XmlNamedNodeMap, ICollection
  17. {
  18. // ===== ICollection interface elements ===================================
  19. /// <summary>
  20. /// Private class to provide Synchronzed Access to the attribute list.
  21. /// </summary>
  22. private class SyncAttributes : XmlAttributeCollection
  23. {
  24. private XmlAttributeCollection _attributes;
  25. public SyncAttributes ( XmlAttributeCollection attributes )
  26. {
  27. _attributes = attributes;
  28. }
  29. public override bool IsSynchronized
  30. {
  31. get {return true; }
  32. }
  33. // Override all properties/methods that modify/read items
  34. // and lock them so they are thread-safe
  35. public override void CopyTo(Array array, int index)
  36. {
  37. lock (_attributes )
  38. { _attributes.CopyTo(array, index); }
  39. }
  40. public override XmlAttribute this[string name]
  41. {
  42. get {
  43. lock (_attributes ) { return _attributes[name]; }
  44. }
  45. }
  46. public override XmlAttribute this[int i]
  47. {
  48. get {
  49. lock (_attributes) { return _attributes[i]; }
  50. }
  51. }
  52. public override XmlAttribute Append( XmlAttribute node )
  53. {
  54. lock (_attributes)
  55. { return _attributes.Append( node ); }
  56. }
  57. public override void CopyTo(XmlAttribute[] array, int index)
  58. {
  59. lock (_attributes)
  60. { _attributes.CopyTo(array, index); }
  61. }
  62. public override XmlAttribute InsertAfter(
  63. XmlAttribute newNode,
  64. XmlAttribute refNode)
  65. {
  66. lock (_attributes)
  67. { return _attributes.InsertAfter( newNode, refNode ); }
  68. }
  69. public override XmlAttribute Prepend(XmlAttribute node)
  70. {
  71. lock (_attributes)
  72. { return _attributes.Prepend(node); }
  73. }
  74. public override XmlAttribute Remove(XmlAttribute node)
  75. {
  76. lock (_attributes)
  77. { return _attributes.Remove( node ); }
  78. }
  79. public override void RemoveAll()
  80. {
  81. lock (_attributes)
  82. { _attributes.RemoveAll(); }
  83. }
  84. public override XmlAttribute RemoveAt(int i)
  85. {
  86. lock (_attributes)
  87. { return _attributes.RemoveAt(i); }
  88. }
  89. public override XmlNode SetNamedItem(XmlNode node)
  90. {
  91. lock (_attributes)
  92. { return _attributes.SetNamedItem(node); }
  93. }
  94. // Even ToString, since someone could come along and blow away an
  95. // attribute while we're iterating...
  96. public override string ToString()
  97. {
  98. lock (_attributes)
  99. { return _attributes.ToString(); }
  100. }
  101. } // SynchAttributes
  102. /// <summary>
  103. /// Return true if access is synchronized (thread-safe)
  104. /// </summary>
  105. public virtual bool IsSynchronized
  106. {
  107. // This version of the class is not synchronized
  108. get
  109. {
  110. return false;
  111. }
  112. }
  113. /// <summary>
  114. /// Return object used for synchronous access to class
  115. /// </summary>
  116. public object SyncRoot
  117. {
  118. get
  119. {
  120. return this;
  121. }
  122. }
  123. /// <summary>
  124. /// Returns a thread-safe version of the attribute collection.
  125. /// </summary>
  126. /// <param name="attributes">Attribute collection to make thread-safe.</param>
  127. /// <returns>Thread-safe XmlAttributeCollection.</returns>
  128. public static XmlAttributeCollection Synchronized(XmlAttributeCollection attributes)
  129. {
  130. if (attributes == null)
  131. {
  132. throw new ArgumentNullException("Null XmlAttributeCollection passed to Synchronized()");
  133. }
  134. return new SyncAttributes(attributes);
  135. }
  136. /// <summary>
  137. /// Copy the XmlAttributeCollection into the passed array. Index is zero-based.
  138. /// </summary>
  139. /// <param name="array">Array to copy into</param>
  140. /// <param name="index">Index to start copying from</param>
  141. public virtual void CopyTo(Array array, int index)
  142. {
  143. // Let the Array handle all the errors, there's no risk to us
  144. // TODO - should we set OwnerElement to null in clone() in CopyTo(Array, int)? (yes, using setOwnerElement())
  145. int arrayIndex = 0;
  146. for (int i = index; i < FnodeList.Count; i++)
  147. {
  148. XmlAttribute e = FnodeList[i] as XmlAttribute;
  149. XmlAttribute theClone = e.Clone() as XmlAttribute;
  150. theClone.setOwnerElement(null);
  151. array.SetValue(theClone, arrayIndex);
  152. arrayIndex++;
  153. }
  154. }
  155. // XmlAttributeCollection Properties =================================
  156. /// <summary>
  157. /// Get the attribute with the specified name
  158. /// </summary>
  159. [System.Runtime.CompilerServices.IndexerName("ItemOf")]
  160. public virtual XmlAttribute this[string name]
  161. {
  162. get
  163. {
  164. return GetNamedItem(name) as XmlAttribute;
  165. }
  166. }
  167. /// <summary>
  168. /// Get the attribute at the specified index. The Collection is zero-based.
  169. /// </summary>
  170. [System.Runtime.CompilerServices.IndexerName("ItemOf")]
  171. public virtual XmlAttribute this[int i]
  172. {
  173. get
  174. {
  175. return base.Item(i) as XmlAttribute;
  176. }
  177. }
  178. /// <summary>
  179. /// Get the attribute with the specifed (localName, URI)
  180. /// </summary>
  181. [System.Runtime.CompilerServices.IndexerName("ItemOf")]
  182. public virtual XmlAttribute this[string localName, string namespaceURI]
  183. {
  184. get
  185. {
  186. return GetNamedItem(localName, namespaceURI) as XmlAttribute;
  187. }
  188. }
  189. // ============= Public methods =====================================
  190. /// <summary>
  191. /// Appends the specified node to the attribute list
  192. /// If the node is already in the list, it is moved to the end.
  193. /// If a node is in the list with the same name, the node is removed and the new node is added.
  194. /// </summary>
  195. /// <param name="node">Attribute node to append to the collection</param>
  196. /// <exception cref="ArgumentException">Node was created from a differant document or node is null</exception>
  197. /// <returns></returns>
  198. public virtual XmlAttribute Append( XmlAttribute node )
  199. {
  200. // TODO - node validation? (no)
  201. XmlAttribute retval = null;
  202. System.Diagnostics.Debug.Assert(node != null, "Null node passed to Append()");
  203. if (! FOwner.OwnerDocument.Equals(node.OwnerDocument))
  204. throw new ArgumentException("Cannot append node from another document");
  205. if (node.OwnerElement != null)
  206. throw new ArgumentException("Cannot append node from another document");
  207. foreach (XmlAttribute cur in FnodeList)
  208. {
  209. // If node is already in the collection, it is moved to the last position.
  210. if (cur.Equals(node))
  211. {
  212. retval = cur;
  213. FnodeList.Remove(cur);
  214. }
  215. //If an attribute with the same name is already present in the collection,
  216. // the original attribute is removed from the collection and
  217. // node is added to the end of the collection.
  218. if (cur.Name == node.Name)
  219. FnodeList.Remove(cur);
  220. }
  221. // add the new node to the end of the collection
  222. // set attribute owner element? (yes)
  223. node.setOwnerElement(FOwnerNode as XmlElement);
  224. FnodeList.Add(node);
  225. // return the removed item
  226. return retval;
  227. }
  228. /// <summary>
  229. /// Copies all attributes in collection into the array, starting at index.
  230. /// attribute index is zero-based.
  231. /// </summary>
  232. /// <exception cref="OverflowException">Thrown if insufficient room to copy all elements</exception>
  233. /// <param name="array">Array to copy XlmAttributes into</param>
  234. /// <param name="index">index to start copy</param>
  235. public virtual void CopyTo(XmlAttribute[] array, int index)
  236. {
  237. // Let the array handle all the errors, there's no risk to us
  238. // TODO - should we set OwnerElement to null in clone() in CopyTo(XmlAttribute[], int)? (yes, using setOwnerElement())
  239. int arrayIndex = 0;
  240. for (int i = index; i < FnodeList.Count; i++)
  241. {
  242. XmlAttribute e = FnodeList[i] as XmlAttribute;
  243. XmlAttribute theClone = e.Clone() as XmlAttribute;
  244. theClone.setOwnerElement(null);
  245. array[arrayIndex] = theClone;
  246. arrayIndex++;
  247. }
  248. }
  249. /// <summary>
  250. /// Helper function since InsertBefore/After use exact same algorithm
  251. /// </summary>
  252. /// <param name="refNode"></param>
  253. /// <param name="newNode"></param>
  254. /// <param name="offset">offset to add to Insert (0 or 1)</param>
  255. /// <returns>Deleted attribute</returns>
  256. private XmlAttribute InsertHelper(XmlAttribute newNode, XmlAttribute refNode, int offset)
  257. {
  258. // TODO - validation? (no)
  259. // TODO - null refNode is valid (== prepend)
  260. if (refNode == null)
  261. throw new ArgumentNullException("Null refNode passed to InsertAfter()");
  262. if (newNode == null)
  263. throw new ArgumentNullException("Null newNode passed to InsertAfter()");
  264. if (! newNode.OwnerDocument.Equals(FOwner.OwnerDocument) )
  265. throw new ArgumentException("Node to insert does not have same owner document as reference");
  266. // Logically, it makes no sense to insert node "A" after node "A",
  267. // since only one node "A" can be in the list - flag it as an error
  268. if (newNode.Name == refNode.Name)
  269. throw new ArgumentException("Node to insert has same name as reference node");
  270. // Other bizarre case is if refNode.Equals(newNode)
  271. // We'll flag this error after we check that refNode is in the list
  272. int refNodeIndex = -1;
  273. // Note that if newNode is in the list, then we'll get a name match
  274. int SameNameIndex = -1;
  275. for (int i = 0; i < FnodeList.Count; i++)
  276. {
  277. XmlAttribute curListNode = Item(i) as XmlAttribute;
  278. if (curListNode.Name == refNode.Name)
  279. refNodeIndex = i;
  280. if (curListNode.Name == newNode.Name)
  281. SameNameIndex = i;
  282. }
  283. if ( refNodeIndex == -1 )
  284. throw new ArgumentException("Attribute [" + refNode.Name + "] is not in Collection for InsertAfter()");
  285. // Check the obvious, InsertAfter( attr1, attr1);
  286. if (refNode.Equals( newNode ) )
  287. return newNode;
  288. XmlAttribute retval = null;
  289. if (SameNameIndex != -1)
  290. {
  291. // If this is newNode in the list, we'll insert it back in the right spot
  292. // If this is another node, just remove it
  293. retval = FnodeList[SameNameIndex] as XmlAttribute;
  294. FnodeList.RemoveAt(SameNameIndex);
  295. if ( SameNameIndex < refNodeIndex )
  296. refNodeIndex--;
  297. }
  298. FnodeList.Insert(refNodeIndex + offset, newNode);
  299. // TODO - set OwnerElement? (no)
  300. //node.setOwnerElement(FOwnerNode as XmlElement);
  301. // TODO - determine which node to return (deleted node)
  302. return retval;
  303. }
  304. /// <summary>
  305. /// Insert the specifed attribute immediately after the reference node.
  306. /// If an attribute with the same name is already in the collection, that attribute is removed and the new attribute inserted
  307. /// </summary>
  308. /// <exception cref="ArgumentException">Raised if newNode OwnerDocument differs from nodelist owner or refNode is not a member of the collection</exception>
  309. /// <param name="newNode">New attribute to insert</param>
  310. /// <param name="refNode">Reference node to insert new node after</param>
  311. /// <returns>Inserted node</returns>
  312. public virtual XmlAttribute InsertAfter( XmlAttribute newNode, XmlAttribute refNode)
  313. {
  314. return InsertHelper(newNode, refNode, 1);
  315. }
  316. /// <summary>
  317. /// Inserts newNode into the collection just before refNode.
  318. /// If a node with newNode.Name is already in the list, it is removed.
  319. /// </summary>
  320. /// <exception cref="ArgumentException">Thrown if inserted attribute created from different document, refNode not found in collection or
  321. /// refNode.Name == newNode.Name.</exception>
  322. /// <param name="newNode">Node to insert into list</param>
  323. /// <param name="refNode">Node to insert before</param>
  324. /// <returns>Deleted node, or null if no node was deleted.</returns>
  325. public virtual XmlAttribute InsertBefore(
  326. XmlAttribute newNode,
  327. XmlAttribute refNode
  328. )
  329. {
  330. return InsertHelper(newNode, refNode, 0);
  331. }
  332. /// <summary>
  333. /// Inserts the specified node as the first node in the collection
  334. /// </summary>
  335. /// <param name="node">XmlAttribute to insert</param>
  336. /// <exception cref="ArgumentException">If node is null, or owner document does not match collection.</exception>
  337. /// <returns>Node that was removed, or null if no node deleted.</returns>
  338. public virtual XmlAttribute Prepend(XmlAttribute node)
  339. {
  340. //TODO - node validation? (no)
  341. // TODO - set attribute owner element? (no)
  342. //node.setOwnerElement(FOwnerNode as XmlElement);
  343. if (FnodeList.Count > 0)
  344. {
  345. return InsertBefore(node, Item(0) as XmlAttribute);
  346. }
  347. if (node == null)
  348. throw new ArgumentException("Cannot prepend null node");
  349. if (! node.OwnerDocument.Equals(FOwner.OwnerDocument) )
  350. throw new ArgumentException("Node to prepend does not have same owner document as reference");
  351. FnodeList.Add(node);
  352. return node;
  353. }
  354. /// <summary>
  355. /// Removes the requested node from the collection.
  356. /// </summary>
  357. /// <param name="node">Node to remove</param>
  358. /// <returns>The node removed, or null if the node was not found in the collection.</returns>
  359. public virtual XmlAttribute Remove(XmlAttribute node)
  360. {
  361. for (int i = 0; i < FnodeList.Count; i++)
  362. {
  363. XmlAttribute e = FnodeList[i] as XmlAttribute;
  364. if (e.Equals(node))
  365. {
  366. FnodeList.RemoveAt(i);
  367. return node;
  368. }
  369. }
  370. // TODO - if node is a default, should we add it back with a default value? (no)
  371. return null;
  372. }
  373. /// <summary>
  374. /// Removes all attributes from the collection
  375. /// </summary>
  376. public virtual void RemoveAll()
  377. {
  378. // Can this be this easy?
  379. for (int i = FnodeList.Count - 1; i > 0; i--)
  380. {
  381. XmlAttribute e = FnodeList[i] as XmlAttribute;
  382. e.setOwnerElement(null);
  383. FnodeList.RemoveAt(i);
  384. }
  385. // TODO - Add default attributes back in in RemoveAll()? (no)
  386. }
  387. /// <summary>
  388. /// Removes the attribute at the specified index
  389. /// </summary>
  390. /// <param name="i">index of attribute to remove.</param>
  391. /// <returns>Removed node, or null if node not in collection</returns>
  392. public virtual XmlAttribute RemoveAt(int i)
  393. {
  394. if ((i < 0) | ( i >= FnodeList.Count))
  395. return null;
  396. // TODO - if default attribute removed in RemoveAt(), add it back? (no)
  397. XmlAttribute e = FnodeList[i] as XmlAttribute;
  398. FnodeList.RemoveAt(i);
  399. e.setOwnerElement(null);
  400. return e;
  401. }
  402. /// <summary>
  403. /// Adds a node to the collection using it's name. If a node with the same name exists,
  404. /// it is removed.
  405. /// </summary>
  406. /// <param name="node">XmlAttribute to add.</param>
  407. /// <returns>If a node replaces a named node, the replaced node is deleted and returned.
  408. /// Otherwise, null.</returns>
  409. public override XmlNode SetNamedItem(XmlNode node)
  410. {
  411. return base.SetNamedItem(node);
  412. }
  413. public override string ToString()
  414. {
  415. string retval = "System.Xml.XmlAttributeCollection ";
  416. if (FOwnerNode != null)
  417. retval += "OwnerElement: " + FOwnerNode.Name;
  418. foreach (XmlAttribute o in FnodeList)
  419. {
  420. retval += o.Name + "=" + o.Value;
  421. }
  422. return retval;
  423. }
  424. // ============= Constructors ========================================
  425. // TODO - change constructor to pass in IEnumerator?
  426. internal XmlAttributeCollection(XmlNode aOwner, XmlNode aOwnerNode, ArrayList nodeList) :
  427. base(aOwner, aOwnerNode, nodeList)
  428. {
  429. // Makes no sense to have namespace aware on attributes
  430. NamespaceAware = false;
  431. }
  432. internal XmlAttributeCollection ()
  433. {
  434. }
  435. }
  436. }