2007-10-30 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / System.Xml / XmlElement.cs
1 //
2 // System.Xml.XmlElement
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 //
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.
31 //
32
33 using System;
34 using System.Collections;
35 using System.Xml.XPath;
36 using System.IO;
37 using System.Text;
38 using Mono.Xml;
39 #if NET_2_0
40 using System.Xml.Schema;
41 #endif
42
43 namespace System.Xml
44 {
45         public class XmlElement : XmlLinkedNode, IHasXmlChildNode
46         {
47                 #region Fields
48
49                 private XmlAttributeCollection attributes;
50                 private XmlNameEntry name;
51                 XmlLinkedNode lastLinkedChild;
52
53                 private bool isNotEmpty;
54 #if NET_2_0
55                 IXmlSchemaInfo schemaInfo;
56 #endif
57
58                 #endregion
59
60                 #region Constructor
61
62                 protected internal XmlElement (
63                         string prefix, 
64                         string localName, 
65                         string namespaceURI, 
66                         XmlDocument doc) : this (prefix, localName, namespaceURI, doc, false)
67                 {
68                 }
69
70                 internal XmlElement (
71                         string prefix, 
72                         string localName, 
73                         string namespaceURI, 
74                         XmlDocument doc,
75                         bool atomizedNames) : base (doc)
76                 {
77                         if (!atomizedNames) {
78                                 if (prefix == null)
79                                         prefix = String.Empty;
80                                 if (namespaceURI == null)
81                                         namespaceURI = String.Empty;
82
83                                 XmlConvert.VerifyName (localName);
84
85                                 prefix = doc.NameTable.Add (prefix);
86                                 localName = doc.NameTable.Add (localName);
87                                 namespaceURI = doc.NameTable.Add (namespaceURI);
88                         }
89                         name = doc.NameCache.Add (prefix, localName, namespaceURI, true);
90
91                         if(doc.DocumentType != null)
92                         {
93                                 DTDAttListDeclaration attlist = doc.DocumentType.DTD.AttListDecls [localName];
94                                 if (attlist != null) {
95                                         for (int i = 0; i < attlist.Definitions.Count; i++) {
96                                                 DTDAttributeDefinition def = attlist [i];
97                                                 if (def.DefaultValue != null) {
98                                                         SetAttribute (def.Name, def.DefaultValue);
99                                                         Attributes [def.Name].SetDefault ();
100                                                 }
101                                         }
102                                 }
103                         }
104                 }
105
106                 #endregion
107
108                 #region Properties
109
110                 XmlLinkedNode IHasXmlChildNode.LastLinkedChild {
111                         get { return lastLinkedChild; }
112                         set { lastLinkedChild = value; }
113                 }
114
115                 public override XmlAttributeCollection Attributes {
116                         get {
117                                 if (attributes == null)
118                                         attributes = new XmlAttributeCollection (this);
119                                 return attributes;
120                         }
121                 }
122
123                 public virtual bool HasAttributes {
124                         get { return attributes != null && attributes.Count > 0; }
125                 }
126
127                 public override string InnerText {
128                         get {
129                                 return base.InnerText;
130                         }
131                         set {
132                                 // Why its behavior (of MS FCL) is different from InnerXml...?
133                                 if (FirstChild != null && FirstChild.NextSibling == null && FirstChild.NodeType == XmlNodeType.Text)
134                                         FirstChild.Value = value;
135                                 else {
136                                         while (FirstChild != null)
137                                                 this.RemoveChild (FirstChild);
138                                         // creates new Text node
139                                         AppendChild (OwnerDocument.CreateTextNode (value), false);
140                                 }
141                         }
142                 }
143
144                 public override string InnerXml {
145                         get {
146                                 return base.InnerXml;
147                         }
148                         set {
149                                 while (FirstChild != null)
150                                         this.RemoveChild (FirstChild);
151
152                                 XmlNamespaceManager nsmgr = this.ConstructNamespaceManager ();
153                                 XmlParserContext ctx = new XmlParserContext (OwnerDocument.NameTable, nsmgr,
154                                         OwnerDocument.DocumentType != null ? OwnerDocument.DocumentType.DTD : null,
155                                         BaseURI, XmlLang, XmlSpace, null);
156                                 XmlTextReader xmlReader = new XmlTextReader (value, XmlNodeType.Element, ctx);
157                                 xmlReader.XmlResolver = OwnerDocument.Resolver;
158
159                                 do {
160                                         XmlNode n = OwnerDocument.ReadNode (xmlReader);
161                                         if(n == null) break;
162                                         AppendChild (n);
163                                 } while (true);
164                         }
165                 }
166
167                 public bool IsEmpty {
168                         get {
169                                 return !isNotEmpty && (FirstChild == null);
170                         }
171
172                         set {
173                                 isNotEmpty = !value;
174                                 if(value) {
175                                         while (FirstChild != null)
176                                                 RemoveChild (FirstChild);
177                                 }
178                         }
179                 }
180
181                 public override string LocalName {
182                         get { return name.LocalName; }
183                 }
184
185                 public override string Name {
186                         get { return name.GetPrefixedName (OwnerDocument.NameCache); }
187                 }
188
189                 public override string NamespaceURI {
190                         get { return name.NS; }
191                 }
192
193                 public override XmlNode NextSibling {
194                         get { return ParentNode == null || ((IHasXmlChildNode) ParentNode).LastLinkedChild == this ? null : NextLinkedSibling; }
195                 }
196
197                 public override XmlNodeType NodeType {
198                         get { 
199                                 return XmlNodeType.Element; 
200                         }
201                 }
202
203                 internal override XPathNodeType XPathNodeType {
204                         get {
205                                 return XPathNodeType.Element;
206                         }
207                 }
208
209                 public override XmlDocument OwnerDocument {
210                         get { 
211                                 return base.OwnerDocument; 
212                         }
213                 }
214
215                 public override string Prefix {
216                         get { return name.Prefix; }
217                         set {
218                                 if (IsReadOnly)
219                                         throw new ArgumentException ("This node is readonly.");
220                                 if (value == null) {
221 #if NET_2_0
222                                         value = string.Empty;
223 #else
224                                         throw new ArgumentNullException ("Prefix value is null.");
225 #endif
226                                 }
227                                 if ((!String.Empty.Equals(value))&&(!XmlChar.IsNCName (value)))
228                                         throw new ArgumentException ("Specified name is not a valid NCName: " + value);
229
230                                 value = OwnerDocument.NameTable.Add (value);
231                                 name = OwnerDocument.NameCache.Add (value,
232                                         name.LocalName, name.NS, true);
233                         }
234                 }
235
236 #if NET_2_0
237                 public override XmlNode ParentNode {
238                         get { return base.ParentNode; }
239                 }
240
241                 public override IXmlSchemaInfo SchemaInfo {
242                         get { return schemaInfo; }
243                         internal set { schemaInfo = value; }
244                 }
245 #endif
246
247                 #endregion
248
249                 #region Methods
250                 
251                 public override XmlNode CloneNode (bool deep)
252                 {
253                         XmlElement node = new XmlElement (
254                                 name.Prefix, name.LocalName, name.NS, OwnerDocument, true);
255
256                         for (int i = 0; i < Attributes.Count; i++)
257                                 node.SetAttributeNode ((XmlAttribute) 
258                                         Attributes [i].CloneNode (true));
259
260                         if (deep) {
261                                 for (int i = 0; i < ChildNodes.Count; i++)
262                                         node.AppendChild (ChildNodes [i].CloneNode (true), false);
263                         }
264
265                         return node;
266                 }
267
268                 public virtual string GetAttribute (string name)
269                 {
270                         XmlNode attributeNode = Attributes.GetNamedItem (name);
271                         return attributeNode != null ? attributeNode.Value : String.Empty;
272                 }
273
274                 public virtual string GetAttribute (string localName, string namespaceURI)
275                 {
276                         XmlNode attributeNode = Attributes.GetNamedItem (localName, namespaceURI);
277                         return attributeNode != null ? attributeNode.Value : String.Empty;
278                 }
279
280                 public virtual XmlAttribute GetAttributeNode (string name)
281                 {
282                         XmlNode attributeNode = Attributes.GetNamedItem (name);
283                         return attributeNode != null ? attributeNode as XmlAttribute : null;
284                 }
285
286                 public virtual XmlAttribute GetAttributeNode (string localName, string namespaceURI)
287                 {
288                         XmlNode attributeNode = Attributes.GetNamedItem (localName, namespaceURI);
289                         return attributeNode != null ? attributeNode as XmlAttribute : null;
290                 }
291
292                 public virtual XmlNodeList GetElementsByTagName (string name)
293                 {
294                         ArrayList nodeArrayList = new ArrayList ();
295                         this.SearchDescendantElements (name, name == "*", nodeArrayList);
296                         return new XmlNodeArrayList (nodeArrayList);
297                 }
298
299                 public virtual XmlNodeList GetElementsByTagName (string localName, string namespaceURI)
300                 {
301                         ArrayList nodeArrayList = new ArrayList ();
302                         this.SearchDescendantElements (localName, localName == "*", namespaceURI, namespaceURI == "*", nodeArrayList);
303                         return new XmlNodeArrayList (nodeArrayList);
304                 }
305
306                 public virtual bool HasAttribute (string name)
307                 {
308                         XmlNode attributeNode = Attributes.GetNamedItem (name);
309                         return attributeNode != null;
310                 }
311
312                 public virtual bool HasAttribute (string localName, string namespaceURI)
313                 {
314                         XmlNode attributeNode = Attributes.GetNamedItem (localName, namespaceURI);
315                         return attributeNode != null;
316                 }
317
318                 public override void RemoveAll ()
319                 {
320                         // Remove all attributes and child nodes.
321                         base.RemoveAll ();
322                 }
323
324                 public virtual void RemoveAllAttributes ()
325                 {
326                         if (attributes != null)
327                                 attributes.RemoveAll ();
328                 }
329
330                 public virtual void RemoveAttribute (string name)
331                 {
332                         if (attributes == null)
333                                 return;
334                         XmlAttribute attr = Attributes.GetNamedItem (name) as XmlAttribute;
335                         if (attr != null)
336                                 Attributes.Remove(attr);
337                 }
338
339                 public virtual void RemoveAttribute (string localName, string namespaceURI)
340                 {
341                         if (attributes == null)
342                                 return;
343
344                         XmlAttribute attr = attributes.GetNamedItem(localName, namespaceURI) as XmlAttribute;
345                         if (attr != null)
346                                 Attributes.Remove(attr);
347                 }
348
349                 public virtual XmlNode RemoveAttributeAt (int i)
350                 {
351                         if (attributes == null || attributes.Count <= i)
352                                 return null;
353                         return Attributes.RemoveAt (i);
354                 }
355
356                 public virtual XmlAttribute RemoveAttributeNode (XmlAttribute oldAttr)
357                 {
358                         if (attributes == null)
359                                 return null;
360                         return Attributes.Remove (oldAttr);
361                 }
362
363                 public virtual XmlAttribute RemoveAttributeNode (string localName, string namespaceURI)
364                 {
365                         if (attributes == null)
366                                 return null;
367                         return Attributes.Remove (attributes [localName, namespaceURI]);
368                 }
369
370                 public virtual void SetAttribute (string name, string value)
371                 {
372                         XmlAttribute attribute = OwnerDocument.CreateAttribute (name);
373                         attribute.Value = value;
374                         Attributes.SetNamedItem (attribute);
375                 }
376
377                 public virtual string SetAttribute (string localName, string namespaceURI, string value)
378                 {
379                         XmlAttribute attr = Attributes [localName, namespaceURI];
380                         if (attr == null) {
381                                 attr = OwnerDocument.CreateAttribute (localName, namespaceURI);
382                                 attr.Value = value;
383                                 Attributes.SetNamedItem (attr);
384                         }
385                         else
386                                 attr.Value = value;
387                         return attr.Value;
388                 }
389
390                 public virtual XmlAttribute SetAttributeNode (XmlAttribute newAttr)
391                 {
392                         if (newAttr.OwnerElement != null)
393                                 throw new InvalidOperationException (
394                                         "Specified attribute is already an attribute of another element.");
395
396                         XmlAttribute ret = Attributes.SetNamedItem (newAttr) as XmlAttribute;
397                         return ret == newAttr ? null : ret;
398                 }
399
400                 public virtual XmlAttribute SetAttributeNode (string localName, string namespaceURI)
401                 {
402                         // Note that this constraint is only for this method.
403                         // SetAttribute() allows prefixed name.
404                         XmlConvert.VerifyNCName (localName);
405
406                         return Attributes.Append (OwnerDocument.CreateAttribute (String.Empty, localName, namespaceURI, false, true));
407                 }
408
409                 public override void WriteContentTo (XmlWriter w)
410                 {
411                         for (XmlNode n = FirstChild; n != null; n = n.NextSibling)
412                                 n.WriteTo (w);
413                 }
414
415                 public override void WriteTo (XmlWriter w)
416                 {
417                         w.WriteStartElement (
418                                 name.NS == null || name.NS.Length == 0 ? String.Empty : name.Prefix,
419                                 name.LocalName,
420                                 name.NS);
421
422                         if (HasAttributes)
423                                 for (int i = 0; i < Attributes.Count; i++)
424                                         Attributes [i].WriteTo (w);
425
426                         WriteContentTo (w);
427
428                         if (IsEmpty)
429                                 w.WriteEndElement ();
430                         else
431                                 w.WriteFullEndElement ();
432                 }
433
434                 #endregion
435         }
436 }