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