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