2 // System.Xml.XmlAttributeCollection
5 // Jason Diamond (jason@injektilo.org)
6 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
8 // (C) 2002 Jason Diamond http://injektilo.org/
9 // (C) 2002 Atsushi Enomoto
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Collections;
40 public sealed class XmlAttributeCollection : XmlNamedNodeMap, ICollection
42 public class XmlAttributeCollection : XmlNamedNodeMap, ICollection
45 XmlElement ownerElement;
46 XmlDocument ownerDocument;
48 internal XmlAttributeCollection (XmlNode parent) : base (parent)
50 ownerElement = parent as XmlElement;
51 ownerDocument = parent.OwnerDocument;
52 if(ownerElement == null)
53 throw new XmlException ("invalid construction for XmlAttributeCollection.");
56 bool ICollection.IsSynchronized {
61 get { return ownerElement.IsReadOnly; }
64 [System.Runtime.CompilerServices.IndexerName ("ItemOf")]
66 public XmlAttribute this [string name] {
68 public virtual XmlAttribute this [string name] {
71 return (XmlAttribute) GetNamedItem (name);
75 [System.Runtime.CompilerServices.IndexerName ("ItemOf")]
77 public XmlAttribute this [int i] {
79 public virtual XmlAttribute this [int i] {
82 return (XmlAttribute) Nodes [i];
86 [System.Runtime.CompilerServices.IndexerName ("ItemOf")]
88 public XmlAttribute this [string localName, string namespaceURI] {
90 public virtual XmlAttribute this [string localName, string namespaceURI] {
93 return (XmlAttribute) GetNamedItem (localName, namespaceURI);
97 object ICollection.SyncRoot {
102 public XmlAttribute Append (XmlAttribute node)
104 public virtual XmlAttribute Append (XmlAttribute node)
111 public void CopyTo (XmlAttribute[] array, int index)
113 // assuming that Nodes is a correct collection.
114 for(int i=0; i<Count; i++)
115 array [index + i] = Nodes [i] as XmlAttribute;
118 void ICollection.CopyTo (Array array, int index)
120 // assuming that Nodes is a correct collection.
121 array.CopyTo (Nodes.ToArray (typeof(XmlAttribute)), index);
125 public XmlAttribute InsertAfter (XmlAttribute newNode, XmlAttribute refNode)
127 public virtual XmlAttribute InsertAfter (XmlAttribute newNode, XmlAttribute refNode)
130 if (refNode == null) {
132 return InsertBefore (newNode, null);
134 return InsertBefore (newNode, this [0]);
136 for (int i = 0; i < Count; i++)
137 if (refNode == Nodes [i])
138 return InsertBefore (newNode, Count == i + 1 ? null : this [i + 1]);
140 throw new ArgumentException ("refNode not found in this collection.");
144 public XmlAttribute InsertBefore (XmlAttribute newNode, XmlAttribute refNode)
146 public virtual XmlAttribute InsertBefore (XmlAttribute newNode, XmlAttribute refNode)
149 if (newNode.OwnerDocument != ownerDocument)
150 throw new ArgumentException ("different document created this newNode.");
152 ownerDocument.onNodeInserting (newNode, null);
155 if (refNode != null) {
156 for (int i = 0; i < Count; i++) {
157 XmlNode n = Nodes [i] as XmlNode;
164 throw new ArgumentException ("refNode not found in this collection.");
166 SetNamedItem (newNode, pos, false);
168 ownerDocument.onNodeInserted (newNode, null);
174 public XmlAttribute Prepend (XmlAttribute node)
176 public virtual XmlAttribute Prepend (XmlAttribute node)
179 return this.InsertAfter (node, null);
183 public XmlAttribute Remove (XmlAttribute node)
185 public virtual XmlAttribute Remove (XmlAttribute node)
189 throw new ArgumentException ("This attribute collection is read-only.");
191 throw new ArgumentException ("Specified node is null.");
192 if (node.OwnerDocument != ownerDocument)
193 throw new ArgumentException ("Specified node is in a different document.");
194 if (node.OwnerElement != this.ownerElement)
195 throw new ArgumentException ("The specified attribute is not contained in the element.");
197 XmlAttribute retAttr = null;
198 for (int i = 0; i < Count; i++) {
199 XmlAttribute attr = (XmlAttribute) Nodes [i];
206 if(retAttr != null) {
207 ownerDocument.onNodeRemoving (node, ownerElement);
208 base.RemoveNamedItem (retAttr.LocalName, retAttr.NamespaceURI);
209 RemoveIdenticalAttribute (retAttr);
210 ownerDocument.onNodeRemoved (node, ownerElement);
212 // If it is default, then directly create new attribute.
213 DTDAttributeDefinition def = retAttr.GetAttributeDefinition ();
214 if (def != null && def.DefaultValue != null) {
215 XmlAttribute attr = ownerDocument.CreateAttribute (
216 retAttr.Prefix, retAttr.LocalName, retAttr.NamespaceURI, true, false);
217 attr.Value = def.DefaultValue;
219 this.SetNamedItem (attr);
221 retAttr.AttributeOwnerElement = null;
226 public void RemoveAll ()
228 public virtual void RemoveAll ()
232 while (current < Count) {
233 XmlAttribute attr = this [current];
236 // It is called for the purpose of event support.
242 public XmlAttribute RemoveAt (int i)
244 public virtual XmlAttribute RemoveAt (int i)
249 return Remove ((XmlAttribute)Nodes [i]);
252 public override XmlNode SetNamedItem (XmlNode node)
255 throw new ArgumentException ("this AttributeCollection is read only.");
257 XmlAttribute attr = node as XmlAttribute;
258 if (attr.OwnerElement == ownerElement)
259 return node; // do nothing
260 if (attr.OwnerElement != null)
261 throw new ArgumentException ("This attribute is already set to another element.");
263 ownerElement.OwnerDocument.onNodeInserting (node, ownerElement);
265 attr.AttributeOwnerElement = ownerElement;
266 XmlNode n = base.SetNamedItem (node, -1, false);
267 AdjustIdenticalAttributes (node as XmlAttribute, n == node ? null : n);
269 ownerElement.OwnerDocument.onNodeInserted (node, ownerElement);
271 return n as XmlAttribute;
274 internal void AddIdenticalAttribute ()
276 SetIdenticalAttribute (false);
279 internal void RemoveIdenticalAttribute ()
281 SetIdenticalAttribute (true);
284 private void SetIdenticalAttribute (bool remove)
286 if (ownerElement == null)
289 // Check if new attribute's datatype is ID.
290 XmlDocumentType doctype = ownerDocument.DocumentType;
291 if (doctype == null || doctype.DTD == null)
293 DTDElementDeclaration elem = doctype.DTD.ElementDecls [ownerElement.Name];
294 for (int i = 0; i < Count; i++) {
295 XmlAttribute node = (XmlAttribute) Nodes [i];
296 DTDAttributeDefinition attdef = elem == null ? null : elem.Attributes [node.Name];
297 if (attdef == null || attdef.Datatype.TokenizedType != XmlTokenizedType.ID)
301 if (ownerDocument.GetIdenticalAttribute (node.Value) != null) {
302 ownerDocument.RemoveIdenticalAttribute (node.Value);
306 // adding new identical attribute, but
307 // MS.NET is pity for ID support, so I'm wondering how to correct it...
308 if (ownerDocument.GetIdenticalAttribute (node.Value) != null)
309 throw new XmlException (String.Format (
310 "ID value {0} already exists in this document.", node.Value));
311 ownerDocument.AddIdenticalAttribute (node);
318 private void AdjustIdenticalAttributes (XmlAttribute node, XmlNode existing)
320 // If owner element is not appended to the document,
321 // ID table should not be filled.
322 if (ownerElement == null)
325 if (existing != null)
326 RemoveIdenticalAttribute (existing);
328 // Check if new attribute's datatype is ID.
329 XmlDocumentType doctype = node.OwnerDocument.DocumentType;
330 if (doctype == null || doctype.DTD == null)
332 DTDAttListDeclaration attList = doctype.DTD.AttListDecls [ownerElement.Name];
333 DTDAttributeDefinition attdef = attList == null ? null : attList.Get (node.Name);
334 if (attdef == null || attdef.Datatype.TokenizedType != XmlTokenizedType.ID)
337 ownerDocument.AddIdenticalAttribute (node);
340 private XmlNode RemoveIdenticalAttribute (XmlNode existing)
342 // If owner element is not appended to the document,
343 // ID table should not be filled.
344 if (ownerElement == null)
347 if (existing != null) {
348 // remove identical attribute (if it is).
349 if (ownerDocument.GetIdenticalAttribute (existing.Value) != null)
350 ownerDocument.RemoveIdenticalAttribute (existing.Value);