ensure the file is closed in XmlWriter.Create(filename)
[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 //   Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
7 //
8 // (C) 2002 Kral Ferch
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.Globalization;
36 using System.IO;
37 using System.Text;
38 using System.Xml.XPath;
39 #if NET_2_0
40 using System.Diagnostics;
41 using System.Xml.Schema;
42 #endif
43
44 namespace System.Xml
45 {
46         public abstract class XmlNode : ICloneable, IEnumerable, IXPathNavigable
47         {
48                 static EmptyNodeList emptyList = new EmptyNodeList ();
49
50                 class EmptyNodeList : XmlNodeList
51                 {
52                         static IEnumerator emptyEnumerator = new object [0].GetEnumerator ();
53
54                         public override int Count {
55                                 get { return 0; }
56                         }
57
58                         public override IEnumerator GetEnumerator ()
59                         {
60                                 return emptyEnumerator;
61                         }
62
63                         public override XmlNode Item (int index)
64                         {
65                                 return null;
66                         }
67                 }
68
69                 #region Fields
70
71                 XmlDocument ownerDocument;
72                 XmlNode parentNode;
73                 XmlNodeListChildren childNodes;
74
75                 #endregion
76
77                 #region Constructors
78
79                 internal XmlNode (XmlDocument ownerDocument)
80                 {
81                         this.ownerDocument = ownerDocument;
82                 }
83
84                 #endregion
85
86                 #region Properties
87
88                 public virtual XmlAttributeCollection Attributes {
89                         get { return null; }
90                 }
91
92                 public virtual string BaseURI {
93                         get {
94                                 // Isn't it conformant to W3C XML Base Recommendation?
95                                 // As far as I tested, there are not...
96                                 return (ParentNode != null) ? ParentNode.ChildrenBaseURI : String.Empty;
97                         }
98                 }
99
100                 internal virtual string ChildrenBaseURI {
101                         get {
102                                 return BaseURI;
103                         }
104                 }
105
106                 public virtual XmlNodeList ChildNodes {
107                         get {
108                                 IHasXmlChildNode l = this as IHasXmlChildNode;
109                                 if (l == null)
110                                         return emptyList;
111                                 if (childNodes == null)
112                                         childNodes = new XmlNodeListChildren (l);
113                                 return childNodes;
114                         }
115                 }
116
117                 public virtual XmlNode FirstChild {
118                         get {
119                                 IHasXmlChildNode l = this as IHasXmlChildNode;
120                                 XmlLinkedNode c = (l == null) ?
121                                         null : l.LastLinkedChild;
122                                 return c == null ? null : c.NextLinkedSibling;
123                         }
124                 }
125
126                 public virtual bool HasChildNodes {
127                         get { return LastChild != null; }
128                 }
129
130                 public virtual string InnerText {
131                         get {
132                                 switch (NodeType) {
133                                 case XmlNodeType.Text:
134                                 case XmlNodeType.CDATA:
135                                 case XmlNodeType.SignificantWhitespace:
136                                 case XmlNodeType.Whitespace:
137                                         return Value;
138                                         break;
139                                 }
140                                 if (FirstChild == null)
141                                         return String.Empty;
142                                 if (FirstChild == LastChild)
143                                         return FirstChild.NodeType != XmlNodeType.Comment ?
144                                                 FirstChild.InnerText :
145                                                 String.Empty;
146
147                                 StringBuilder builder = null;
148                                 AppendChildValues (ref builder);
149                                 return builder == null ? String.Empty : builder.ToString ();
150                         }
151
152                         set { throw new InvalidOperationException ("This node is read only. Cannot be modified."); }
153                 }
154
155                 private void AppendChildValues (ref StringBuilder builder)
156                 {
157                         XmlNode node = FirstChild;
158
159                         while (node != null) {
160                                 switch (node.NodeType) {
161                                 case XmlNodeType.Text:
162                                 case XmlNodeType.CDATA:
163                                 case XmlNodeType.SignificantWhitespace:
164                                 case XmlNodeType.Whitespace:
165                                         if (builder == null)
166                                                 builder = new StringBuilder ();
167                                         builder.Append (node.Value);
168                                         break;
169                                 }
170                                 node.AppendChildValues (ref builder);
171                                 node = node.NextSibling;
172                         }
173                 }
174
175                 public virtual string InnerXml {
176                         get {
177                                 StringWriter sw = new StringWriter ();
178                                 XmlTextWriter xtw = new XmlTextWriter (sw);
179
180                                 WriteContentTo (xtw);
181
182                                 return sw.GetStringBuilder ().ToString ();
183                         }
184
185                         set {
186                                 throw new InvalidOperationException ("This node is readonly or doesn't have any children.");
187                         }
188                 }
189
190                 public virtual bool IsReadOnly {
191                         get 
192                         {
193                                 XmlNode curNode = this;
194                                 do
195                                 {
196                                         switch (curNode.NodeType) 
197                                         {
198                                                 case XmlNodeType.EntityReference:
199                                                 case XmlNodeType.Entity:
200                                                         return true;
201
202                                                 case XmlNodeType.Attribute:
203                                                         curNode = ((XmlAttribute)curNode).OwnerElement;
204                                                         break;
205
206                                                 default:
207                                                         curNode = curNode.ParentNode;
208                                                         break;
209                                         }
210                                 }
211                                 while (curNode != null) ;
212
213                                 return false;
214                         }
215                 }
216
217                 [System.Runtime.CompilerServices.IndexerName("Item")]
218                 public virtual XmlElement this [string name] {
219                         get { 
220                                 for (int i = 0; i < ChildNodes.Count; i++) {
221                                         XmlNode node = ChildNodes [i];
222                                         if ((node.NodeType == XmlNodeType.Element) &&
223                                             (node.Name == name)) {
224                                                 return (XmlElement) node;
225                                         }
226                                 }
227
228                                 return null;
229                         }
230                 }
231
232                 [System.Runtime.CompilerServices.IndexerName("Item")]
233                 public virtual XmlElement this [string localname, string ns] {
234                         get {
235                                 for (int i = 0; i < ChildNodes.Count; i++) {
236                                         XmlNode node = ChildNodes [i];
237                                         if ((node.NodeType == XmlNodeType.Element) &&
238                                             (node.LocalName == localname) && 
239                                             (node.NamespaceURI == ns)) {
240                                                 return (XmlElement) node;
241                                         }
242                                 }
243
244                                 return null;
245                         }
246                 }
247
248                 public virtual XmlNode LastChild {
249                         get {
250                                 IHasXmlChildNode l = this as IHasXmlChildNode;
251                                 return l == null ? null : l.LastLinkedChild;
252                         }
253                 }
254
255                 public abstract string LocalName { get; }
256
257                 public abstract string Name     { get; }
258
259                 public virtual string NamespaceURI {
260                         get { return String.Empty; }
261                 }
262
263                 public virtual XmlNode NextSibling {
264                         get { return null; }
265                 }
266
267                 public abstract XmlNodeType NodeType { get;     }
268
269                 internal virtual XPathNodeType XPathNodeType {
270                         get {
271                                 throw new InvalidOperationException ("Can not get XPath node type from " + this.GetType ().ToString ());
272                         }
273                 }
274
275                 public virtual string OuterXml {
276                         get {
277                                 StringWriter sw = new StringWriter ();
278                                 XmlTextWriter xtw = new XmlTextWriter (sw);
279
280                                 WriteTo (xtw);
281
282                                 return sw.ToString ();
283                         }
284                 }
285
286                 public virtual XmlDocument OwnerDocument {
287                         get { return ownerDocument; }
288                 }
289
290                 public virtual XmlNode ParentNode {
291                         get { return parentNode; }
292                 }
293
294                 public virtual string Prefix {
295                         get { return String.Empty; }
296                         set {}
297                 }
298
299                 public virtual XmlNode PreviousSibling {
300                         get { return null; }
301                 }
302
303                 public virtual string Value {
304                         get { return null; }
305                         set { throw new InvalidOperationException ("This node does not have a value"); }
306                 }
307
308                 internal virtual string XmlLang {
309                         get {
310                                 if(Attributes != null)
311                                         for (int i = 0; i < Attributes.Count; i++) {
312                                                 XmlAttribute attr = Attributes [i];
313                                                 if(attr.Name == "xml:lang")
314                                                         return attr.Value;
315                                         }
316                                 return (ParentNode != null) ? ParentNode.XmlLang : OwnerDocument.XmlLang;
317                         }
318                 }
319
320                 internal virtual XmlSpace XmlSpace {
321                         get {
322                                 if(Attributes != null) {
323                                         for (int i = 0; i < Attributes.Count; i++) {
324                                                 XmlAttribute attr = Attributes [i];
325                                                 if(attr.Name == "xml:space") {
326                                                         switch(attr.Value) {
327                                                         case "preserve": return XmlSpace.Preserve;
328                                                         case "default": return XmlSpace.Default;
329                                                         }
330                                                         break;
331                                                 }
332                                         }
333                                 }
334                                 return (ParentNode != null) ? ParentNode.XmlSpace : OwnerDocument.XmlSpace;
335                         }
336                 }
337
338 #if NET_2_0
339                 public virtual IXmlSchemaInfo SchemaInfo {
340                         get { return null; }
341                         internal set { }
342                 }
343 #endif
344
345                 #endregion
346
347                 #region Methods
348
349                 public virtual XmlNode AppendChild (XmlNode newChild)
350                 {
351                         // AppendChild(n) is equivalent to InsertBefore(n, null)
352                         return InsertBefore (newChild, null);
353                 }
354
355                 internal XmlNode AppendChild (XmlNode newChild, bool checkNodeType)
356                 {
357                         return InsertBefore (newChild, null, checkNodeType, true);
358                 }
359
360                 public virtual XmlNode Clone ()
361                 {
362                         // By MS document, it is equivalent to CloneNode(true).
363                         return this.CloneNode (true);
364                 }
365
366                 public abstract XmlNode CloneNode (bool deep);
367
368 #if NET_2_0
369                 public virtual XPathNavigator CreateNavigator ()
370                 {
371                         // XmlDocument has overriden definition, so it is safe
372                         // to use OwnerDocument here.
373                         return OwnerDocument.CreateNavigator (this);
374                 }
375 #else
376                 public XPathNavigator CreateNavigator ()
377                 {
378                         XmlDocument document = this.NodeType == XmlNodeType.Document ?
379                                 this as XmlDocument : this.ownerDocument;
380                         return document.CreateNavigator (this);
381                 }
382 #endif
383
384                 public IEnumerator GetEnumerator ()
385                 {
386                         return ChildNodes.GetEnumerator ();
387                 }
388
389                 public virtual string GetNamespaceOfPrefix (string prefix)
390                 {
391                         switch (prefix) {
392                         case null:
393                                 throw new ArgumentNullException ("prefix");
394 #if NET_2_0
395                         case "xml":
396                                 return XmlNamespaceManager.XmlnsXml;
397                         case "xmlns":
398                                 return XmlNamespaceManager.XmlnsXmlns;
399 #endif
400                         }
401
402                         XmlNode node;
403                         switch (NodeType) {
404                         case XmlNodeType.Attribute:
405                                 node = ((XmlAttribute) this).OwnerElement;
406                                 if (node == null)
407                                         return String.Empty;
408                                 break;
409                         case XmlNodeType.Element:
410                                 node = this;
411                                 break;
412                         default:
413                                 node = ParentNode;
414                                 break;
415                         }
416
417                         while (node != null) {
418                                 if (node.Prefix == prefix)
419                                         return node.NamespaceURI;
420                                 if (node.NodeType == XmlNodeType.Element &&
421                                     ((XmlElement) node).HasAttributes) {
422                                         int count = node.Attributes.Count;
423                                         for (int i = 0; i < count; i++) {
424                                                 XmlAttribute attr = node.Attributes [i];
425                                                 if (prefix == attr.LocalName && attr.Prefix == "xmlns"
426                                                         || attr.Name == "xmlns" && prefix == String.Empty)
427                                                         return attr.Value;
428                                         }
429                                 }
430                                 node = node.ParentNode;
431                         }
432                         return String.Empty;
433                 }
434
435                 public virtual string GetPrefixOfNamespace (string namespaceURI)
436                 {
437 #if NET_2_0
438                         switch (namespaceURI) {
439                         case XmlNamespaceManager.XmlnsXml:
440                                 return XmlNamespaceManager.PrefixXml;
441                         case XmlNamespaceManager.XmlnsXmlns:
442                                 return XmlNamespaceManager.PrefixXmlns;
443                         }
444 #endif
445
446                         XmlNode node;
447                         switch (NodeType) {
448                         case XmlNodeType.Attribute:
449                                 node = ((XmlAttribute) this).OwnerElement;
450                                 break;
451                         case XmlNodeType.Element:
452                                 node = this;
453                                 break;
454                         default:
455                                 node = ParentNode;
456                                 break;
457                         }
458
459                         while (node != null) {
460                                 if (node.NodeType == XmlNodeType.Element &&
461                                     ((XmlElement) node).HasAttributes) {
462                                         for (int i = 0; i < node.Attributes.Count; i++) {
463                                                 XmlAttribute attr = node.Attributes [i];
464                                                 if (attr.Prefix == "xmlns" && attr.Value == namespaceURI)
465                                                         return attr.LocalName;
466                                                 else if (attr.Name == "xmlns" && attr.Value == namespaceURI)
467                                                         return String.Empty;
468                                         }
469                                 }
470                                 node = node.ParentNode;
471                         }
472                         return String.Empty;
473                 }
474
475                 object ICloneable.Clone ()
476                 {
477                         return Clone ();
478                 }
479
480                 IEnumerator IEnumerable.GetEnumerator ()
481                 {
482                         return GetEnumerator ();
483                 }
484
485                 public virtual XmlNode InsertAfter (XmlNode newChild, XmlNode refChild)
486                 {
487                         // InsertAfter(n1, n2) is equivalent to InsertBefore(n1, n2.PreviousSibling).
488
489                         // I took this way because current implementation 
490                         // Calling InsertBefore() in this method is faster than
491                         // the counterpart, since NextSibling is faster than 
492                         // PreviousSibling (these children are forward-only list).
493                         XmlNode argNode = null;
494                         if (refChild != null)
495                                 argNode = refChild.NextSibling;
496                         else if (FirstChild != null)
497                                 argNode = FirstChild;
498                         return InsertBefore (newChild, argNode);
499                 }
500
501                 public virtual XmlNode InsertBefore (XmlNode newChild, XmlNode refChild)
502                 {
503                         return InsertBefore (newChild, refChild, true, true);
504                 }
505
506                 // check for the node to be one of node ancestors
507                 internal bool IsAncestor (XmlNode newChild)
508                 {
509                         XmlNode currNode = this.ParentNode;
510                         while(currNode != null)
511                         {
512                                 if(currNode == newChild)
513                                         return true;
514                                 currNode = currNode.ParentNode;
515                         }
516                         return false;
517                 }
518
519                 internal XmlNode InsertBefore (XmlNode newChild, XmlNode refChild, bool checkNodeType, bool raiseEvent)
520                 {
521                         if (checkNodeType)
522                                 CheckNodeInsertion (newChild, refChild);
523
524                         if (newChild == refChild)
525                                 return newChild;
526
527                         IHasXmlChildNode l = (IHasXmlChildNode) this;
528
529                         XmlDocument ownerDoc = (NodeType == XmlNodeType.Document) ? (XmlDocument) this : OwnerDocument;
530
531                         if (raiseEvent)
532                                 ownerDoc.onNodeInserting (newChild, this);
533
534                         if (newChild.ParentNode != null)
535                                 newChild.ParentNode.RemoveChild (newChild, checkNodeType);
536
537                         if (newChild.NodeType == XmlNodeType.DocumentFragment) {
538                                 // This recursively invokes events. (It is compatible with MS implementation.)
539                                 while (newChild.FirstChild != null)
540                                         this.InsertBefore (newChild.FirstChild, refChild);
541                         }
542                         else {
543                                 XmlLinkedNode newLinkedChild = (XmlLinkedNode) newChild;
544                                 newLinkedChild.parentNode = this;
545
546                                 if (refChild == null) {
547                                         // newChild is the last child:
548                                         // * set newChild as NextSibling of the existing lastchild
549                                         // * set LastChild = newChild
550                                         // * set NextSibling of newChild as FirstChild
551                                         if (l.LastLinkedChild != null) {
552                                                 XmlLinkedNode formerFirst = (XmlLinkedNode) FirstChild;
553                                                 l.LastLinkedChild.NextLinkedSibling = newLinkedChild;
554                                                 l.LastLinkedChild = newLinkedChild;
555                                                 newLinkedChild.NextLinkedSibling = formerFirst;
556                                         } else {
557                                                 l.LastLinkedChild = newLinkedChild;
558                                                 l.LastLinkedChild.NextLinkedSibling = newLinkedChild;   // FirstChild
559                                         }
560                                 } else {
561                                         // newChild is not the last child:
562                                         // * if newchild is first, then set next of lastchild is newChild.
563                                         //   otherwise, set next of previous sibling to newChild
564                                         // * set next of newChild to refChild
565                                         XmlLinkedNode prev = refChild.PreviousSibling as XmlLinkedNode;
566                                         if (prev == null)
567                                                 l.LastLinkedChild.NextLinkedSibling = newLinkedChild;
568                                         else
569                                                 prev.NextLinkedSibling = newLinkedChild;
570                                         newLinkedChild.NextLinkedSibling = refChild as XmlLinkedNode;
571                                 }
572                                 switch (newChild.NodeType) {
573                                 case XmlNodeType.EntityReference:
574                                         ((XmlEntityReference) newChild).SetReferencedEntityContent ();
575                                         break;
576                                 case XmlNodeType.Entity:
577                                         break;
578                                 case XmlNodeType.DocumentType:
579                                         break;
580                                 }
581
582                                 if (raiseEvent)
583                                         ownerDoc.onNodeInserted (newChild, newChild.ParentNode);
584                         }
585                         return newChild;
586                 }
587
588                 private void CheckNodeInsertion (XmlNode newChild, XmlNode refChild)
589                 {
590                         XmlDocument ownerDoc = (NodeType == XmlNodeType.Document) ? (XmlDocument) this : OwnerDocument;
591
592                         if (NodeType != XmlNodeType.Element &&
593                             NodeType != XmlNodeType.Attribute &&
594                             NodeType != XmlNodeType.Document &&
595                             NodeType != XmlNodeType.DocumentFragment)
596                                 throw new InvalidOperationException (String.Format ("Node cannot be appended to current node {0}.", NodeType));
597
598                         switch (NodeType) {
599                         case XmlNodeType.Attribute:
600                                 switch (newChild.NodeType) {
601                                 case XmlNodeType.Text:
602                                 case XmlNodeType.EntityReference:
603                                         break;
604                                 default:
605                                         throw new InvalidOperationException (String.Format (
606                                                 "Cannot insert specified type of node {0} as a child of this node {1}.", 
607                                                 newChild.NodeType, NodeType));
608                                 }
609                                 break;
610                         case XmlNodeType.Element:
611                                 switch (newChild.NodeType) {
612                                 case XmlNodeType.Attribute:
613                                 case XmlNodeType.Document:
614                                 case XmlNodeType.DocumentType:
615                                 case XmlNodeType.Entity:
616                                 case XmlNodeType.Notation:
617                                 case XmlNodeType.XmlDeclaration:
618                                         throw new InvalidOperationException ("Cannot insert specified type of node as a child of this node.");
619                                 }
620                                 break;
621                         }
622
623                         if (IsReadOnly)
624                                 throw new InvalidOperationException ("The node is readonly.");
625
626                         if (newChild.OwnerDocument != ownerDoc)
627                                 throw new ArgumentException ("Can't append a node created by another document.");
628
629                         if (refChild != null) {
630                                 if (refChild.ParentNode != this)
631                                         throw new ArgumentException ("The reference node is not a child of this node.");
632                         }
633
634                         if(this == ownerDoc && ownerDoc.DocumentElement != null && (newChild is XmlElement) && newChild != ownerDoc.DocumentElement)
635                                 throw new XmlException ("multiple document element not allowed.");
636
637                         // checking validity finished. then appending...
638
639                         
640                         if (newChild == this || IsAncestor (newChild))
641                                 throw new ArgumentException("Cannot insert a node or any ancestor of that node as a child of itself.");
642
643                 }
644
645                 public virtual void Normalize ()
646                 {
647                         StringBuilder tmpBuilder = new StringBuilder ();
648                         int count = this.ChildNodes.Count;
649                         int start = 0;
650                         for (int i = 0; i < count; i++) {
651                                 XmlNode c = ChildNodes [i];
652                                 switch (c.NodeType) {
653                                 case XmlNodeType.Text:
654                                 case XmlNodeType.Whitespace:
655                                 case XmlNodeType.SignificantWhitespace:
656                                         tmpBuilder.Append (c.Value);
657                                         break;
658                                 default:
659                                         c.Normalize ();
660                                         NormalizeRange (start, i, tmpBuilder);
661                                         // Continue to normalize from next node.
662                                         start = i + 1;
663                                         break;
664                                 }
665                         }
666                         if (start < count) {
667                                 NormalizeRange (start, count, tmpBuilder);
668                         }
669                 }
670
671                 private void NormalizeRange (int start, int i, StringBuilder tmpBuilder)
672                 {
673                         int keepPos = -1;
674                         // If Texts and Whitespaces are mixed, Text takes precedence to remain.
675                         // i.e. Whitespace should be removed.
676                         for (int j = start; j < i; j++) {
677                                 XmlNode keep = ChildNodes [j];
678                                 if (keep.NodeType == XmlNodeType.Text) {
679                                         keepPos = j;
680                                         break;
681                                 }
682                                 else if (keep.NodeType == XmlNodeType.SignificantWhitespace)
683                                         keepPos = j;
684                                         // but don't break up to find Text nodes.
685                         }
686
687                         if (keepPos >= 0) {
688                                 for (int del = start; del < keepPos; del++)
689                                         RemoveChild (ChildNodes [start]);
690                                 int rest = i - keepPos - 1;
691                                 for (int del = 0; del < rest; del++) {
692                                         RemoveChild (ChildNodes [start + 1]);
693 }
694                         }
695
696                         if (keepPos >= 0)
697                                 ChildNodes [start].Value = tmpBuilder.ToString ();
698                         // otherwise nothing to be normalized
699
700                         tmpBuilder.Length = 0;
701                 }
702
703                 public virtual XmlNode PrependChild (XmlNode newChild)
704                 {
705                         return InsertAfter (newChild, null);
706                 }
707
708                 public virtual void RemoveAll ()
709                 {
710                         if (Attributes != null)
711                                 Attributes.RemoveAll ();
712                         XmlNode next = null;
713                         for (XmlNode node = FirstChild; node != null; node = next) {
714                                 next = node.NextSibling;
715                                 RemoveChild (node);
716                         }
717                 }
718
719                 public virtual XmlNode RemoveChild (XmlNode oldChild)
720                 {
721                         return RemoveChild (oldChild, true);
722                 }
723
724                 private void CheckNodeRemoval ()
725                 {
726                         if (NodeType != XmlNodeType.Attribute && 
727                                 NodeType != XmlNodeType.Element && 
728                                 NodeType != XmlNodeType.Document && 
729                                 NodeType != XmlNodeType.DocumentFragment)
730                                 throw new ArgumentException (String.Format ("This {0} node cannot remove its child.", NodeType));
731
732                         if (IsReadOnly)
733                                 throw new ArgumentException (String.Format ("This {0} node is read only.", NodeType));
734                 }
735
736                 internal XmlNode RemoveChild (XmlNode oldChild, bool checkNodeType)
737                 {
738                         if (oldChild == null)
739                                 throw new NullReferenceException ();
740                         XmlDocument ownerDoc = (NodeType == XmlNodeType.Document) ? (XmlDocument)this : OwnerDocument;
741                         if(oldChild.ParentNode != this)
742                                 throw new ArgumentException ("The node to be removed is not a child of this node.");
743
744                         if (checkNodeType)
745                                 ownerDoc.onNodeRemoving (oldChild, oldChild.ParentNode);
746
747                         if (checkNodeType)
748                                 CheckNodeRemoval ();
749
750                         IHasXmlChildNode l = (IHasXmlChildNode) this;
751
752                         if (Object.ReferenceEquals (l.LastLinkedChild, l.LastLinkedChild.NextLinkedSibling) && Object.ReferenceEquals (l.LastLinkedChild, oldChild))
753                                 // If there is only one children, simply clear.
754                                 l.LastLinkedChild = null;
755                         else {
756                                 XmlLinkedNode oldLinkedChild = (XmlLinkedNode) oldChild;
757                                 XmlLinkedNode beforeLinkedChild = l.LastLinkedChild;
758                                 XmlLinkedNode firstChild = (XmlLinkedNode) FirstChild;
759                                 
760                                 while (Object.ReferenceEquals (beforeLinkedChild.NextLinkedSibling, l.LastLinkedChild) == false && 
761                                         Object.ReferenceEquals (beforeLinkedChild.NextLinkedSibling, oldLinkedChild) == false)
762                                         beforeLinkedChild = beforeLinkedChild.NextLinkedSibling;
763
764                                 if (Object.ReferenceEquals (beforeLinkedChild.NextLinkedSibling, oldLinkedChild) == false)
765                                         throw new ArgumentException ();
766
767                                 beforeLinkedChild.NextLinkedSibling = oldLinkedChild.NextLinkedSibling;
768
769                                 // Each derived class may have its own l.LastLinkedChild, so we must set it explicitly.
770                                 if (oldLinkedChild.NextLinkedSibling == firstChild)
771                                         l.LastLinkedChild = beforeLinkedChild;
772
773                                 oldLinkedChild.NextLinkedSibling = null;
774                                 }
775
776                         if (checkNodeType)
777                                 ownerDoc.onNodeRemoved (oldChild, oldChild.ParentNode);
778                         oldChild.parentNode = null;     // clear parent 'after' above logic.
779
780                         return oldChild;
781                 }
782
783                 public virtual XmlNode ReplaceChild (XmlNode newChild, XmlNode oldChild)
784                 {
785                         if(oldChild.ParentNode != this)
786                                 throw new ArgumentException ("The node to be removed is not a child of this node.");
787                         
788                         if (newChild == this || IsAncestor (newChild))
789                                 throw new InvalidOperationException("Cannot insert a node or any ancestor of that node as a child of itself.");
790                         
791                         XmlNode next = oldChild.NextSibling;
792                         RemoveChild (oldChild);
793                         InsertBefore (newChild, next);
794                         return oldChild;
795                 }
796
797                 // WARNING: don't use this member outside XmlAttribute nodes.
798                 internal XmlElement AttributeOwnerElement {
799                         get { return (XmlElement) parentNode; }
800                         set { parentNode = value; }
801                 }
802
803                 internal void SearchDescendantElements (string name, bool matchAll, ArrayList list)
804                 {
805                         for (XmlNode n = FirstChild; n != null; n = n.NextSibling) {
806                                 if (n.NodeType != XmlNodeType.Element)
807                                         continue;
808                                 if (matchAll || n.Name == name)
809                                         list.Add (n);
810                                 n.SearchDescendantElements (name, matchAll, list);
811                         }
812                 }
813
814                 internal void SearchDescendantElements (string name, bool matchAllName, string ns, bool matchAllNS, ArrayList list)
815                 {
816                         for (XmlNode n = FirstChild; n != null; n = n.NextSibling) {
817                                 if (n.NodeType != XmlNodeType.Element)
818                                         continue;
819                                 if ((matchAllName || n.LocalName == name)
820                                         && (matchAllNS || n.NamespaceURI == ns))
821                                         list.Add (n);
822                                 n.SearchDescendantElements (name, matchAllName, ns, matchAllNS, list);
823                         }
824                 }
825
826                 public XmlNodeList SelectNodes (string xpath)
827                 {
828                         return SelectNodes (xpath, null);
829                 }
830
831                 public XmlNodeList SelectNodes (string xpath, XmlNamespaceManager nsmgr)
832                 {
833                         XPathNavigator nav = CreateNavigator ();
834                         XPathExpression expr = nav.Compile (xpath);
835                         if (nsmgr != null)
836                                 expr.SetContext (nsmgr);
837                         XPathNodeIterator iter = nav.Select (expr);
838                         /*
839                         ArrayList rgNodes = new ArrayList ();
840                         while (iter.MoveNext ())
841                         {
842                                 rgNodes.Add (((IHasXmlNode) iter.Current).GetNode ());
843                         }
844                         return new XmlNodeArrayList (rgNodes);
845                         */
846                         return new XmlIteratorNodeList (iter);
847                 }
848
849                 public XmlNode SelectSingleNode (string xpath)
850                 {
851                         return SelectSingleNode (xpath, null);
852                 }
853
854                 public XmlNode SelectSingleNode (string xpath, XmlNamespaceManager nsmgr)
855                 {
856                         XPathNavigator nav = CreateNavigator ();
857                         XPathExpression expr = nav.Compile (xpath);
858                         if (nsmgr != null)
859                                 expr.SetContext (nsmgr);
860                         XPathNodeIterator iter = nav.Select (expr);
861                         if (!iter.MoveNext ())
862                                 return null;
863                         return ((IHasXmlNode) iter.Current).GetNode ();
864                 }
865
866                 public virtual bool Supports (string feature, string version)
867                 {
868                         if (String.Compare (feature, "xml", true, CultureInfo.InvariantCulture) == 0 // not case-sensitive
869                             && (String.Compare (version, "1.0", true, CultureInfo.InvariantCulture) == 0
870                                 || String.Compare (version, "2.0", true, CultureInfo.InvariantCulture) == 0))
871                                 return true;
872                         else
873                                 return false;
874                 }
875
876                 public abstract void WriteContentTo (XmlWriter w);
877
878                 public abstract void WriteTo (XmlWriter w);
879
880                 // It parses this and all the ancestor elements,
881                 // find 'xmlns' declarations, stores and then return them.
882                 internal XmlNamespaceManager ConstructNamespaceManager ()
883                 {
884                         XmlDocument doc = this is XmlDocument ? (XmlDocument)this : this.OwnerDocument;
885                         XmlNamespaceManager nsmgr = new XmlNamespaceManager (doc.NameTable);
886                         XmlElement el = null;
887                         switch(this.NodeType) {
888                         case XmlNodeType.Attribute:
889                                 el = ((XmlAttribute)this).OwnerElement;
890                                 break;
891                         case XmlNodeType.Element:
892                                 el = this as XmlElement;
893                                 break;
894                         default:
895                                 el = this.ParentNode as XmlElement;
896                                 break;
897                         }
898
899                         while (el != null) {
900                                 for (int i = 0; i < el.Attributes.Count; i++) {
901                                         XmlAttribute attr = el.Attributes [i];
902                                         if(attr.Prefix == "xmlns") {
903                                                 if (nsmgr.LookupNamespace (attr.LocalName) != attr.Value)
904                                                         nsmgr.AddNamespace (attr.LocalName, attr.Value);
905                                         } else if(attr.Name == "xmlns") {
906                                                 if(nsmgr.LookupNamespace (String.Empty) != attr.Value)
907                                                         nsmgr.AddNamespace (String.Empty, attr.Value);
908                                         }
909                                 }
910                                 // When reached to document, then it will set null value :)
911                                 el = el.ParentNode as XmlElement;
912                         }
913                         return nsmgr;
914                 }
915                 #endregion
916         }
917 }