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