* XmlDeclaration.cs : Fixed ParseInput() more parse strictly.
[mono.git] / mcs / class / System.XML / System.Xml / XmlNode.cs
1 //
2 // System.Xml.XmlNode
3 //
4 // Author:
5 //   Kral Ferch <kral_ferch@hotmail.com>
6 //
7 // (C) 2002 Kral Ferch
8 //
9
10 using System;
11 using System.Collections;
12 using System.IO;
13 using System.Text;
14 using System.Xml.XPath;
15
16 namespace System.Xml
17 {
18         public abstract class XmlNode : ICloneable, IEnumerable, IXPathNavigable
19         {
20                 #region Fields
21
22                 XmlDocument ownerDocument;
23                 XmlNode parentNode;
24
25                 #endregion
26
27                 #region Constructors
28
29                 internal XmlNode (XmlDocument ownerDocument)
30                 {
31                         this.ownerDocument = ownerDocument;
32                 }
33
34                 #endregion
35
36                 #region Properties
37
38                 public virtual XmlAttributeCollection Attributes
39                 {
40                         get { return null; }
41                 }
42
43                 public virtual string BaseURI
44                 {
45                         get { return ParentNode.BaseURI; }
46                 }
47
48                 public virtual XmlNodeList ChildNodes {
49                         get {
50                                 return new XmlNodeListChildren(this);
51                         }
52                 }
53
54                 public virtual XmlNode FirstChild {
55                         get {
56                                 if (LastChild != null) {
57                                         return LastLinkedChild.NextLinkedSibling;
58                                 }
59                                 else {
60                                         return null;
61                                 }
62                         }
63                 }
64
65                 public virtual bool HasChildNodes {
66                         get { return LastChild != null; }
67                 }
68
69                 [MonoTODO]
70                 public virtual string InnerText {
71                         get {
72                                 StringBuilder builder = new StringBuilder ();
73                                 AppendChildValues (this, builder);
74                                 return builder.ToString ();
75                         }
76
77                         set { throw new NotImplementedException (); }
78                 }
79
80                 private void AppendChildValues (XmlNode parent, StringBuilder builder)
81                 {
82                         XmlNode node = parent.FirstChild;
83
84                         while (node != null) {
85                                 if (node.NodeType == XmlNodeType.Text)
86                                         builder.Append (node.Value);
87                                 AppendChildValues (node, builder);
88                                 node = node.NextSibling;
89                         }
90                 }
91
92                 [MonoTODO("Setter.")]
93                 public virtual string InnerXml {
94                         get {
95                                 StringWriter sw = new StringWriter ();
96                                 XmlTextWriter xtw = new XmlTextWriter (sw);
97
98                                 WriteContentTo(xtw);
99
100                                 return sw.GetStringBuilder().ToString();
101                         }
102
103                         set { throw new NotImplementedException (); }
104                 }
105
106                 public virtual bool IsReadOnly {
107                         get { return false; }
108                 }
109
110                 [System.Runtime.CompilerServices.IndexerName("Item")]
111                 public virtual XmlElement this [string name] {
112                         get { 
113                                 foreach (XmlNode node in ChildNodes) {
114                                         if ((node.NodeType == XmlNodeType.Element) &&
115                                             (node.Name == name)) {
116                                                 return (XmlElement) node;
117                                         }
118                                 }
119
120                                 return null;
121                         }
122                 }
123
124                 [System.Runtime.CompilerServices.IndexerName("Item")]
125                 public virtual XmlElement this [string localname, string ns] {
126                         get { 
127                                 foreach (XmlNode node in ChildNodes) {
128                                         if ((node.NodeType == XmlNodeType.Element) &&
129                                             (node.LocalName == localname) && 
130                                             (node.NamespaceURI == ns)) {
131                                                 return (XmlElement) node;
132                                         }
133                                 }
134
135                                 return null;
136                         }
137                 }
138
139                 public virtual XmlNode LastChild {
140                         get { return LastLinkedChild; }
141                 }
142
143                 internal virtual XmlLinkedNode LastLinkedChild {
144                         get { return null; }
145                         set { }
146                 }
147
148                 public abstract string LocalName { get; }
149
150                 public abstract string Name     { get; }
151
152                 public virtual string NamespaceURI {
153                         get { return String.Empty; }
154                 }
155
156                 public virtual XmlNode NextSibling {
157                         get { return null; }
158                 }
159
160                 public abstract XmlNodeType NodeType { get;     }
161
162                 public virtual string OuterXml {
163                         get {
164                                 StringWriter sw = new StringWriter ();
165                                 XmlTextWriter xtw = new XmlTextWriter (sw);
166
167                                 WriteTo(xtw);
168
169                                 return sw.GetStringBuilder().ToString();
170                         }
171                 }
172
173                 public virtual XmlDocument OwnerDocument {
174                         get { return ownerDocument; }
175                 }
176
177                 public virtual XmlNode ParentNode {
178                         get { return parentNode; }
179                 }
180
181                 public virtual string Prefix {
182                         get { return String.Empty; }
183                         set {}
184                 }
185
186                 public virtual XmlNode PreviousSibling {
187                         get { return null; }
188                 }
189
190                 public virtual string Value {
191                         get { return null; }
192                         set { throw new InvalidOperationException ("This node does not have a value"); }
193                 }
194
195                 #endregion
196
197                 #region Methods
198
199                 public virtual XmlNode AppendChild (XmlNode newChild)
200                 {
201                         XmlDocument ownerDoc = (NodeType == XmlNodeType.Document) ? (XmlDocument)this : OwnerDocument;
202
203                         ownerDoc.onNodeInserting (newChild, this);
204
205                         if (NodeType == XmlNodeType.Document || NodeType == XmlNodeType.Element || NodeType == XmlNodeType.Attribute) {
206                                 
207                                 if (newChild.OwnerDocument != ownerDoc)
208                                         throw new ArgumentException ("Can't append a node created by another document.");
209
210                                 XmlLinkedNode newLinkedChild = (XmlLinkedNode) newChild;
211                                 XmlLinkedNode lastLinkedChild = LastLinkedChild;
212
213                                 newLinkedChild.parentNode = this;
214                                 
215                                 if (lastLinkedChild != null) {
216                                         newLinkedChild.NextLinkedSibling = lastLinkedChild.NextLinkedSibling;
217                                         lastLinkedChild.NextLinkedSibling = newLinkedChild;
218                                 } else
219                                         newLinkedChild.NextLinkedSibling = newLinkedChild;
220                                 
221                                 LastLinkedChild = newLinkedChild;
222
223                                 ownerDoc.onNodeInserted (newChild, newChild.ParentNode);
224
225                                 return newChild;
226                         } else
227                                 throw new InvalidOperationException();
228                 }
229
230                 [MonoTODO]
231                 public virtual XmlNode Clone ()
232                 {
233                         throw new NotImplementedException ();
234                 }
235
236                 public abstract XmlNode CloneNode (bool deep);
237
238                 [MonoTODO]
239                 public XPathNavigator CreateNavigator ()
240                 {
241                         return new XmlDocumentNavigator(this);
242                 }
243
244                 public IEnumerator GetEnumerator ()
245                 {
246                         return new XmlNodeListChildren(this).GetEnumerator();
247                 }
248
249                 [MonoTODO]
250                 public virtual string GetNamespaceOfPrefix (string prefix)
251                 {
252                         throw new NotImplementedException ();
253                 }
254
255                 [MonoTODO]
256                 public virtual string GetPrefixOfNamespace (string namespaceURI)
257                 {
258                         throw new NotImplementedException ();
259                 }
260
261                 object ICloneable.Clone ()
262                 {
263                         return Clone ();
264                 }
265
266                 IEnumerator IEnumerable.GetEnumerator ()
267                 {
268                         return GetEnumerator ();
269                 }
270
271                 [MonoTODO]
272                 public virtual XmlNode InsertAfter (XmlNode newChild, XmlNode refChild)
273                 {
274                         throw new NotImplementedException ();
275                 }
276
277                 [MonoTODO]
278                 public virtual XmlNode InsertBefore (XmlNode newChild, XmlNode refChild)
279                 {
280                         throw new NotImplementedException ();
281                 }
282
283                 [MonoTODO]
284                 public virtual void Normalize ()
285                 {
286                         throw new NotImplementedException ();
287                 }
288
289                 [MonoTODO]
290                 public virtual XmlNode PrependChild (XmlNode newChild)
291                 {
292                         throw new NotImplementedException ();
293                 }
294
295                 public virtual void RemoveAll ()
296                 {
297                         XmlDocument ownerDoc = (NodeType == XmlNodeType.Document) ? (XmlDocument)this : OwnerDocument;
298
299                         ownerDoc.onNodeRemoving (this, this.ParentNode);
300                         LastLinkedChild = null;
301                         ownerDoc.onNodeRemoved (this, this.ParentNode);
302                 }
303
304                 public virtual XmlNode RemoveChild (XmlNode oldChild)
305                 {
306                         OwnerDocument.onNodeRemoving (oldChild, oldChild.ParentNode);
307
308                         if (NodeType == XmlNodeType.Document || NodeType == XmlNodeType.Element || NodeType == XmlNodeType.Attribute) 
309                         {
310                                 if (IsReadOnly)
311                                         throw new ArgumentException();
312
313                                 if (Object.ReferenceEquals(LastLinkedChild, LastLinkedChild.NextLinkedSibling) && Object.ReferenceEquals(LastLinkedChild, oldChild))
314                                         LastLinkedChild = null;
315                                 else {
316                                         XmlLinkedNode oldLinkedChild = (XmlLinkedNode)oldChild;
317                                         XmlLinkedNode beforeLinkedChild = LastLinkedChild;
318                                         
319                                         while (!Object.ReferenceEquals(beforeLinkedChild.NextLinkedSibling, LastLinkedChild) && !Object.ReferenceEquals(beforeLinkedChild.NextLinkedSibling, oldLinkedChild))
320                                                 beforeLinkedChild = beforeLinkedChild.NextLinkedSibling;
321
322                                         if (!Object.ReferenceEquals(beforeLinkedChild.NextLinkedSibling, oldLinkedChild))
323                                                 throw new ArgumentException();
324
325                                         beforeLinkedChild.NextLinkedSibling = oldLinkedChild.NextLinkedSibling;
326                                         oldLinkedChild.NextLinkedSibling = null;
327                                  }
328
329                                 OwnerDocument.onNodeRemoved (oldChild, oldChild.ParentNode);
330
331                                 return oldChild;
332                         } 
333                         else
334                                 throw new ArgumentException();
335                 }
336
337                 [MonoTODO]
338                 public virtual XmlNode ReplaceChild (XmlNode newChild, XmlNode oldChild)
339                 {
340                         throw new NotImplementedException ();
341                 }
342
343                 public XmlNodeList SelectNodes (string xpath)
344                 {
345                         return SelectNodes (xpath, null);
346                 }
347
348                 [MonoTODO]
349                 public XmlNodeList SelectNodes (string xpath, XmlNamespaceManager nsmgr)
350                 {
351                         XPathNavigator nav = CreateNavigator ();
352                         XPathExpression expr = nav.Compile (xpath);
353                         if (nsmgr != null)
354                                 expr.SetContext (nsmgr);
355                         XPathNodeIterator iter = nav.Select (expr);
356                         ArrayList rgNodes = new ArrayList ();
357                         while (iter.MoveNext ())
358                         {
359                                 rgNodes.Add (((XmlDocumentNavigator) iter.Current).Node);
360                         }
361                         return new XmlNodeArrayList (rgNodes);
362                 }
363
364                 public XmlNode SelectSingleNode (string xpath)
365                 {
366                         return SelectSingleNode (xpath, null);
367                 }
368
369                 [MonoTODO]
370                 public XmlNode SelectSingleNode (string xpath, XmlNamespaceManager nsmgr)
371                 {
372                         XPathNavigator nav = CreateNavigator ();
373                         XPathExpression expr = nav.Compile (xpath);
374                         if (nsmgr != null)
375                                 expr.SetContext (nsmgr);
376                         XPathNodeIterator iter = nav.Select (expr);
377                         if (!iter.MoveNext ())
378                                 return null;
379                         return ((XmlDocumentNavigator) iter.Current).Node;
380                 }
381
382                 internal void SetParentNode (XmlNode parent)
383                 {
384                         parentNode = parent;
385                 }
386
387                 [MonoTODO]
388                 public virtual bool Supports (string feature, string version)
389                 {
390                         throw new NotImplementedException ();
391                 }
392
393                 public abstract void WriteContentTo (XmlWriter w);
394
395                 public abstract void WriteTo (XmlWriter w);
396
397                 // It parses with XmlReader and then construct DOM of the parsed contents.
398                 internal protected void ConstructDOM(XmlReader xmlReader, XmlNode currentNode)
399                 {
400                         // I am not confident whether this method should be placed in this class or not...
401                         // Please verify its validity and then erase this comment;-)
402                         XmlNode newNode;
403                         XmlDocument doc = currentNode is XmlDocument ? (XmlDocument)currentNode : currentNode.OwnerDocument;
404                         // Below are 'almost' copied from XmlDocument.Load(XmlReader xmlReader)
405                         while (xmlReader.Read ()) 
406                         {
407                                 switch (xmlReader.NodeType) \r
408                                 {
409
410                                         case XmlNodeType.CDATA:
411                                                 newNode = doc.CreateCDataSection(xmlReader.Value);
412                                                 currentNode.AppendChild (newNode);
413                                                 break;
414
415                                         case XmlNodeType.Comment:
416                                                 newNode = doc.CreateComment (xmlReader.Value);
417                                                 currentNode.AppendChild (newNode);
418                                                 break;
419
420                                         case XmlNodeType.Element:
421                                                 XmlElement element = doc.CreateElement (xmlReader.Prefix, xmlReader.LocalName, xmlReader.NamespaceURI);
422                                                 currentNode.AppendChild (element);
423
424                                                 // set the element's attributes.
425                                                 while (xmlReader.MoveToNextAttribute ()) \r
426                                                 {
427                                                         XmlAttribute attribute = doc.CreateAttribute (xmlReader.Prefix, xmlReader.LocalName, xmlReader.NamespaceURI);
428                                                         attribute.Value = xmlReader.Value;
429                                                         element.SetAttributeNode (attribute);
430                                                 }
431
432                                                 xmlReader.MoveToElement ();
433
434                                                 // if this element isn't empty, push it onto our "stack".
435                                                 if (!xmlReader.IsEmptyElement)
436                                                         currentNode = element;
437
438                                                 break;
439
440                                         case XmlNodeType.EndElement:
441                                                 currentNode = currentNode.ParentNode;
442                                                 break;
443
444                                         case XmlNodeType.ProcessingInstruction:
445                                                 newNode = doc.CreateProcessingInstruction (xmlReader.Name, xmlReader.Value);
446                                                 currentNode.AppendChild (newNode);
447                                                 break;
448
449                                         case XmlNodeType.Text:
450                                                 newNode = doc.CreateTextNode (xmlReader.Value);
451                                                 currentNode.AppendChild (newNode);
452                                                 break;
453
454                                         case XmlNodeType.XmlDeclaration:\r
455                                                 // String Empties are dummy, then gives over setting value contents to setter.\r
456                                                 newNode = doc.CreateNode(XmlNodeType.XmlDeclaration, String.Empty, String.Empty);\r
457                                                 ((XmlDeclaration)newNode).Value = xmlReader.Value;\r
458                                                 this.AppendChild(newNode);\r
459                                                 break;\r
460                                 }
461                         }
462                 }
463
464                 // It parses this and all the ancestor elements,
465                 // find 'xmlns' declarations, stores and then return them.
466                 // TODO: tests
467                 internal protected XmlNamespaceManager ConstructNamespaceManager()
468                 {
469                         XmlDocument doc = this is XmlDocument ? (XmlDocument)this : this.OwnerDocument;
470                         XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
471                         XmlElement el = null;
472                         switch(this.NodeType)
473                         {
474                                 case XmlNodeType.Attribute:
475                                         el = ((XmlAttribute)this).OwnerElement;
476                                         break;
477                                 case XmlNodeType.Element:
478                                         el = this as XmlElement;
479                                         break;
480                                 default:
481                                         el = this.ParentNode as XmlElement;
482                                         break;
483                         }
484
485                         while(el != null)
486                         {                       
487                                 foreach(XmlAttribute attr in el.Attributes)
488                                 {
489                                         if(attr.Prefix == "xmlns")
490                                         {
491                                                 if(nsmgr.LookupNamespace(attr.LocalName) == null )
492                                                 {
493                                                         nsmgr.AddNamespace(attr.LocalName, attr.Value);
494                                                 }
495                                         }
496                                 }
497                                 // When reached to document, then it will set null value :)
498                                 el = el.ParentNode as XmlElement;
499                         }
500                         return nsmgr;
501                 }
502                 #endregion
503         }
504 }