**** Merged from MCS ****
[mono-project.git] / mcs / class / System.XML / System.Xml / XmlAttributeCollection.cs
blobed80175d277fb8799d6ffc6d942325cad6f5e2ec
1 //
2 // System.Xml.XmlAttributeCollection
3 //
4 // Author:
5 // Jason Diamond (jason@injektilo.org)
6 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
7 //
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:
20 //
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 //
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.
33 using System;
34 using System.Collections;
35 using Mono.Xml;
37 namespace System.Xml
39 #if NET_2_0
40 public sealed class XmlAttributeCollection : XmlNamedNodeMap, ICollection
41 #else
42 public class XmlAttributeCollection : XmlNamedNodeMap, ICollection
43 #endif
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 {
57 get { return false; }
60 bool IsReadOnly {
61 get { return ownerElement.IsReadOnly; }
64 [System.Runtime.CompilerServices.IndexerName ("ItemOf")]
65 #if NET_2_0
66 public XmlAttribute this [string name] {
67 #else
68 public virtual XmlAttribute this [string name] {
69 #endif
70 get {
71 return (XmlAttribute) GetNamedItem (name);
75 [System.Runtime.CompilerServices.IndexerName ("ItemOf")]
76 #if NET_2_0
77 public XmlAttribute this [int i] {
78 #else
79 public virtual XmlAttribute this [int i] {
80 #endif
81 get {
82 return (XmlAttribute) Nodes [i];
86 [System.Runtime.CompilerServices.IndexerName ("ItemOf")]
87 #if NET_2_0
88 public XmlAttribute this [string localName, string namespaceURI] {
89 #else
90 public virtual XmlAttribute this [string localName, string namespaceURI] {
91 #endif
92 get {
93 return (XmlAttribute) GetNamedItem (localName, namespaceURI);
97 object ICollection.SyncRoot {
98 get { return this; }
101 #if NET_2_0
102 public XmlAttribute Append (XmlAttribute node)
103 #else
104 public virtual XmlAttribute Append (XmlAttribute node)
105 #endif
107 XmlNode xmlNode = this.SetNamedItem (node);
108 return node;
111 public void CopyTo (XmlAttribute[] array, int index)
113 // assuming that Nodes is a correct collection.
114 for(int i=0; i<Nodes.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);
124 #if NET_2_0
125 public XmlAttribute InsertAfter (XmlAttribute newNode, XmlAttribute refNode)
126 #else
127 public virtual XmlAttribute InsertAfter (XmlAttribute newNode, XmlAttribute refNode)
128 #endif
130 if (refNode == null) {
131 if (Nodes.Count == 0)
132 return InsertBefore (newNode, null);
133 else
134 return InsertBefore (newNode, this [0]);
136 for (int i = 0; i < Nodes.Count; i++)
137 if (refNode == Nodes [i])
138 return InsertBefore (newNode, Nodes.Count == i + 1 ? null : this [i + 1]);
140 throw new ArgumentException ("refNode not found in this collection.");
143 #if NET_2_0
144 public XmlAttribute InsertBefore (XmlAttribute newNode, XmlAttribute refNode)
145 #else
146 public virtual XmlAttribute InsertBefore (XmlAttribute newNode, XmlAttribute refNode)
147 #endif
149 if (newNode.OwnerDocument != ownerDocument)
150 throw new ArgumentException ("different document created this newNode.");
152 ownerDocument.onNodeInserting (newNode, null);
154 int pos = Nodes.Count;
155 if (refNode != null) {
156 for (int i = 0; i < Nodes.Count; i++) {
157 XmlNode n = Nodes [i] as XmlNode;
158 if (n == refNode) {
159 pos = i;
160 break;
163 if (pos == Nodes.Count)
164 throw new ArgumentException ("refNode not found in this collection.");
166 SetNamedItem (newNode, pos, false);
168 ownerDocument.onNodeInserted (newNode, null);
170 return newNode;
173 #if NET_2_0
174 public XmlAttribute Prepend (XmlAttribute node)
175 #else
176 public virtual XmlAttribute Prepend (XmlAttribute node)
177 #endif
179 return this.InsertAfter (node, null);
182 #if NET_2_0
183 public XmlAttribute Remove (XmlAttribute node)
184 #else
185 public virtual XmlAttribute Remove (XmlAttribute node)
186 #endif
188 if (IsReadOnly)
189 throw new ArgumentException ("This attribute collection is read-only.");
190 if (node == null)
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 < Nodes.Count; i++) {
199 XmlAttribute attr = (XmlAttribute) Nodes [i];
200 if (attr == node) {
201 retAttr = attr;
202 break;
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);
217 attr.Value = def.DefaultValue;
218 attr.SetDefault ();
219 this.SetNamedItem (attr);
221 retAttr.SetOwnerElement (null);
222 return retAttr;
225 #if NET_2_0
226 public void RemoveAll ()
227 #else
228 public virtual void RemoveAll ()
229 #endif
231 int current = 0;
232 while (current < Count) {
233 XmlAttribute attr = this [current];
234 if (!attr.Specified)
235 current++;
236 // It is called for the purpose of event support.
237 Remove (attr);
241 #if NET_2_0
242 public XmlAttribute RemoveAt (int i)
243 #else
244 public virtual XmlAttribute RemoveAt (int i)
245 #endif
247 if(Nodes.Count <= i)
248 return null;
249 return Remove ((XmlAttribute)Nodes [i]);
252 public override XmlNode SetNamedItem (XmlNode node)
254 if(IsReadOnly)
255 throw new ArgumentException ("this AttributeCollection is read only.");
257 XmlAttribute attr = node as XmlAttribute;
258 if (attr.OwnerElement != null)
259 throw new ArgumentException ("This attribute is already set to another element.");
261 ownerElement.OwnerDocument.onNodeInserting (node, ownerElement);
263 attr.SetOwnerElement (ownerElement);
264 XmlNode n = AdjustIdenticalAttributes (node as XmlAttribute, base.SetNamedItem (node, -1, false));
266 ownerElement.OwnerDocument.onNodeInserted (node, ownerElement);
268 return n as XmlAttribute;
271 internal void AddIdenticalAttribute ()
273 SetIdenticalAttribute (false);
276 internal void RemoveIdenticalAttribute ()
278 SetIdenticalAttribute (true);
281 private void SetIdenticalAttribute (bool remove)
283 if (ownerElement == null)
284 return;
286 // Check if new attribute's datatype is ID.
287 XmlDocumentType doctype = ownerDocument.DocumentType;
288 if (doctype == null || doctype.DTD == null)
289 return;
290 DTDElementDeclaration elem = doctype.DTD.ElementDecls [ownerElement.Name];
291 for (int i = 0; i < Nodes.Count; i++) {
292 XmlAttribute node = (XmlAttribute) Nodes [i];
293 DTDAttributeDefinition attdef = elem == null ? null : elem.Attributes [node.Name];
294 if (attdef == null || attdef.Datatype.TokenizedType != XmlTokenizedType.ID)
295 continue;
297 if (remove) {
298 if (ownerDocument.GetIdenticalAttribute (node.Value) != null) {
299 ownerDocument.RemoveIdenticalAttribute (node.Value);
300 return;
302 } else {
303 // adding new identical attribute, but
304 // MS.NET is pity for ID support, so I'm wondering how to correct it...
305 if (ownerDocument.GetIdenticalAttribute (node.Value) != null)
306 throw new XmlException (String.Format (
307 "ID value {0} already exists in this document.", node.Value));
308 ownerDocument.AddIdenticalAttribute (node);
309 return;
315 private XmlNode AdjustIdenticalAttributes (XmlAttribute node, XmlNode existing)
317 // If owner element is not appended to the document,
318 // ID table should not be filled.
319 if (ownerElement == null)
320 return existing;
322 RemoveIdenticalAttribute (existing);
324 // Check if new attribute's datatype is ID.
325 XmlDocumentType doctype = node.OwnerDocument.DocumentType;
326 if (doctype == null || doctype.DTD == null)
327 return existing;
328 DTDAttListDeclaration attList = doctype.DTD.AttListDecls [ownerElement.Name];
329 DTDAttributeDefinition attdef = attList == null ? null : attList.Get (node.Name);
330 if (attdef == null || attdef.Datatype.TokenizedType != XmlTokenizedType.ID)
331 return existing;
333 // adding new identical attribute, but
334 // MS.NET is pity for ID support, so I'm wondering how to correct it...
335 if (ownerDocument.GetIdenticalAttribute (node.Value) != null)
336 throw new XmlException (String.Format (
337 "ID value {0} already exists in this document.", node.Value));
338 ownerDocument.AddIdenticalAttribute (node);
340 return existing;
343 private XmlNode RemoveIdenticalAttribute (XmlNode existing)
345 // If owner element is not appended to the document,
346 // ID table should not be filled.
347 if (ownerElement == null)
348 return existing;
350 if (existing != null) {
351 // remove identical attribute (if it is).
352 if (ownerDocument.GetIdenticalAttribute (existing.Value) != null)
353 ownerDocument.RemoveIdenticalAttribute (existing.Value);
356 return existing;