2003-09-01 Ben Maurer <bmaurer@users.sourceforge.net>
[mono.git] / mcs / class / System.XML / System.Xml / XmlAttributeCollection.cs
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
10 //
11
12 using System;
13 using System.Collections;
14 using Mono.Xml;
15
16 namespace System.Xml
17 {
18         public class XmlAttributeCollection : XmlNamedNodeMap, ICollection
19         {
20                 XmlElement ownerElement;
21                 XmlDocument ownerDocument;
22
23                 internal XmlAttributeCollection (XmlNode parent) : base (parent)
24                 {
25                         ownerElement = parent as XmlElement;
26                         ownerDocument = parent.OwnerDocument;
27                         if(ownerElement == null)
28                                 throw new XmlException ("invalid construction for XmlAttributeCollection.");
29                 }
30
31                 bool ICollection.IsSynchronized {
32                         get {
33                                 throw new NotImplementedException ();
34                         }
35                 }
36
37                 bool IsReadOnly {
38                         get {
39                                 return ownerElement.IsReadOnly;
40                         }
41                 }
42
43                 [System.Runtime.CompilerServices.IndexerName ("ItemOf")]
44                 public virtual XmlAttribute this [string name] {
45                         get {
46                                 return (XmlAttribute) GetNamedItem (name);
47                         }
48                 }
49
50                 [System.Runtime.CompilerServices.IndexerName ("ItemOf")]
51                 public virtual XmlAttribute this [int i] {
52                         get {
53                                 return (XmlAttribute) Nodes [i];
54                         }
55                 }
56
57                 [System.Runtime.CompilerServices.IndexerName ("ItemOf")]
58                 public virtual XmlAttribute this [string localName, string namespaceURI] {
59                         get {
60                                 return (XmlAttribute) GetNamedItem (localName, namespaceURI);
61                         }
62                 }
63
64                 object ICollection.SyncRoot {
65                         get {
66                                 throw new NotImplementedException ();
67                         }
68                 }
69
70                 
71                 public virtual XmlAttribute Append (XmlAttribute node) 
72                 {
73                         XmlNode xmlNode = this.SetNamedItem (node);
74                         return node;
75                 }       
76
77                 public void CopyTo (XmlAttribute[] array, int index)
78                 {
79                         // assuming that Nodes is a correct collection.
80                         for(int i=0; i<Nodes.Count; i++)
81                                 array [index + i] = Nodes [i] as XmlAttribute;
82                 }
83
84                 void ICollection.CopyTo (Array array, int index)
85                 {
86                         // assuming that Nodes is a correct collection.
87                         array.CopyTo (Nodes.ToArray (typeof(XmlAttribute)), index);
88                 }
89
90                 public virtual XmlAttribute InsertAfter (XmlAttribute newNode, XmlAttribute refNode)
91                 {
92                         if(newNode.OwnerDocument != this.ownerDocument)
93                                 throw new ArgumentException ("different document created this newNode.");
94
95                         ownerDocument.onNodeInserting (newNode, null);
96
97                         int pos = Nodes.Count + 1;
98                         if(refNode != null)
99                         {
100                                 for(int i=0; i<Nodes.Count; i++)
101                                 {
102                                         XmlNode n = Nodes [i] as XmlNode;
103                                         if(n == refNode)
104                                         {
105                                                 pos = i + 1;
106                                                 break;
107                                         }
108                                 }
109                                 if(pos > Nodes.Count)
110                                         throw new XmlException ("refNode not found in this collection.");
111                         }
112                         else
113                                 pos = 0;
114                         SetNamedItem (newNode, pos);
115
116                         ownerDocument.onNodeInserted (newNode, null);
117
118                         return newNode;
119                 }
120
121                 public virtual XmlAttribute InsertBefore (XmlAttribute newNode, XmlAttribute refNode)
122                 {
123                         if(newNode.OwnerDocument != ownerDocument)
124                                 throw new ArgumentException ("different document created this newNode.");
125
126                         ownerDocument.onNodeInserting (newNode, null);
127
128                         int pos = Nodes.Count;
129                         if(refNode != null)
130                         {
131                                 for(int i=0; i<Nodes.Count; i++)
132                                 {
133                                         XmlNode n = Nodes [i] as XmlNode;
134                                         if(n == refNode)
135                                         {
136                                                 pos = i;
137                                                 break;
138                                         }
139                                 }
140                                 if(pos == Nodes.Count)
141                                         throw new XmlException ("refNode not found in this collection.");
142                         }
143                         SetNamedItem (newNode, pos);
144
145                         ownerDocument.onNodeInserted (newNode, null);
146
147                         return newNode;
148                 }
149
150                 public virtual XmlAttribute Prepend (XmlAttribute node) 
151                 {
152                         return this.InsertAfter (node, null);
153                 }
154
155                 public virtual XmlAttribute Remove (XmlAttribute node) 
156                 {
157                         if (node == null)
158                                 throw new ArgumentException ("Specified node is null.");
159                         if (node.OwnerDocument != ownerDocument)
160                                 throw new ArgumentException ("Specified node is in a different document.");
161
162                         XmlAttribute retAttr = null;
163                         foreach (XmlAttribute attr in Nodes) {
164                                 if (attr == node) {
165                                         retAttr = attr;
166                                         break;
167                                 }
168                         }
169
170                         if(retAttr != null) {
171                                 ownerDocument.onNodeRemoving (node, null);
172                                 base.RemoveNamedItem (retAttr.LocalName, retAttr.NamespaceURI);
173                                 RemoveIdenticalAttribute (retAttr);
174                                 ownerDocument.onNodeRemoved (node, null);
175                         }
176                         // If it is default, then directly create new attribute.
177                         if (!retAttr.Specified) {
178                                 XmlAttribute attr = ownerDocument.CreateAttribute (retAttr.Prefix,
179                                         retAttr.LocalName, retAttr.NamespaceURI);
180                                 attr.SetDefault ();
181                                 foreach (XmlNode child in retAttr.ChildNodes)
182                                         attr.AppendChild (child);
183                                 this.SetNamedItem (attr);
184                         }
185                         return retAttr;
186                 }
187
188                 public virtual void RemoveAll () 
189                 {
190                         int current = 0;
191                         while (current < Count) {
192                                 XmlAttribute attr = this [current];
193                                 if (!attr.Specified)
194                                         current++;
195                                 // It is called for the purpose of event support.
196                                 Remove (attr);
197                         }
198                 }
199
200                 public virtual XmlAttribute RemoveAt (int i) 
201                 {
202                         if(Nodes.Count <= i)
203                                 return null;
204                         return Remove ((XmlAttribute)Nodes [i]);
205                 }
206
207                 public override XmlNode SetNamedItem (XmlNode node)
208                 {
209                         if(IsReadOnly)
210                                 throw new XmlException ("this AttributeCollection is read only.");
211
212                         return AdjustIdenticalAttributes (node as XmlAttribute, base.SetNamedItem (node, -1) as XmlAttribute);
213                 }
214
215                 internal void AddIdenticalAttribute ()
216                 {
217                         SetIdenticalAttribute (false);
218                 }
219
220                 internal void RemoveIdenticalAttribute ()
221                 {
222                         SetIdenticalAttribute (true);
223                 }
224
225                 private void SetIdenticalAttribute (bool remove)
226                 {
227                         if (ownerElement == null)
228                                 return;
229
230                         // Check if new attribute's datatype is ID.
231                         XmlDocumentType doctype = ownerDocument.DocumentType;
232                         if (doctype == null || doctype.DTD == null)
233                                 return;
234                         DTDElementDeclaration elem = doctype.DTD.ElementDecls [ownerElement.Name];
235                         foreach (XmlAttribute node in this) {
236                                 DTDAttributeDefinition attdef = elem == null ? null : elem.Attributes [node.Name];
237                                 if (attdef == null || attdef.Datatype.TokenizedType != XmlTokenizedType.ID)
238                                         continue;
239
240                                 if (remove) {
241                                         if (ownerDocument.GetIdenticalAttribute (node.Value) != null) {
242                                                 ownerDocument.RemoveIdenticalAttribute (node.Value);
243                                                 return;
244                                         }
245                                 } else {
246                                         // adding new identical attribute, but 
247                                         // MS.NET is pity for ID support, so I'm wondering how to correct it...
248                                         if (ownerDocument.GetIdenticalAttribute (node.Value) != null)
249                                                 throw new XmlException (String.Format (
250                                                         "ID value {0} already exists in this document.", node.Value));
251                                         ownerDocument.AddIdenticalAttribute (node);
252                                         return;
253                                 }
254                         }
255
256                 }
257
258                 private XmlNode AdjustIdenticalAttributes (XmlNode node, XmlNode existing)
259                 {
260                         // If owner element is not appended to the document,
261                         // ID table should not be filled.
262                         if (ownerElement == null)
263                                 return existing;
264
265                         RemoveIdenticalAttribute (existing);
266
267                         // Check if new attribute's datatype is ID.
268                         XmlDocumentType doctype = node.OwnerDocument.DocumentType;
269                         if (doctype == null || doctype.DTD == null)
270                                 return existing;
271                         DTDAttListDeclaration attList = doctype.DTD.AttListDecls [ownerElement.Name];
272                         DTDAttributeDefinition attdef = attList == null ? null : attList.Get (node.Name);
273                         if (attdef == null || attdef.Datatype.TokenizedType != XmlTokenizedType.ID)
274                                 return existing;
275
276                         // adding new identical attribute, but 
277                         // MS.NET is pity for ID support, so I'm wondering how to correct it...
278                         if (ownerDocument.GetIdenticalAttribute (node.Value) != null)
279                                 throw new XmlException (String.Format (
280                                         "ID value {0} already exists in this document.", node.Value));
281                         ownerDocument.AddIdenticalAttribute (node as XmlAttribute);
282
283                         return existing;
284                 }
285
286                 private XmlNode RemoveIdenticalAttribute (XmlNode existing)
287                 {
288                         // If owner element is not appended to the document,
289                         // ID table should not be filled.
290                         if (ownerElement == null)
291                                 return existing;
292
293                         if (existing != null) {
294                                 // remove identical attribute (if it is).
295                                 if (ownerDocument.GetIdenticalAttribute (existing.Value) != null)
296                                         ownerDocument.RemoveIdenticalAttribute (existing.Value);
297                         }
298
299                         return existing;
300                 }
301         }
302 }