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