2004-02-06 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / System.Xml / XmlNode.cs
index cac9cc9c9c542899481c06c02a06a288c4e78792..7e779dc28e4168b09a3366d7ca44405ab51314e0 100644 (file)
@@ -3,12 +3,16 @@
 //
 // Author:
 //   Kral Ferch <kral_ferch@hotmail.com>
+//   Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
 //
 // (C) 2002 Kral Ferch
+// (C) 2002 Atsushi Enomoto
 //
 
 using System;
 using System.Collections;
+using System.IO;
+using System.Text;
 using System.Xml.XPath;
 
 namespace System.Xml
@@ -16,25 +20,17 @@ namespace System.Xml
        public abstract class XmlNode : ICloneable, IEnumerable, IXPathNavigable
        {
                #region Fields
-               ///////////////////////////////////////////////////////////////////////
-               //
-               //      Fields
-               //
-               ///////////////////////////////////////////////////////////////////////
 
                XmlDocument ownerDocument;
                XmlNode parentNode;
+               StringBuilder tmpBuilder;
+               XmlLinkedNode lastLinkedChild;
 
                #endregion
 
                #region Constructors
-               ///////////////////////////////////////////////////////////////////////
-               //
-               //      Constructors
-               //
-               ///////////////////////////////////////////////////////////////////////
 
-               protected internal XmlNode(XmlDocument ownerDocument)
+               internal XmlNode (XmlDocument ownerDocument)
                {
                        this.ownerDocument = ownerDocument;
                }
@@ -42,26 +38,22 @@ namespace System.Xml
                #endregion
 
                #region Properties
-               ///////////////////////////////////////////////////////////////////////
-               //
-               //      Properties
-               //
-               ///////////////////////////////////////////////////////////////////////
 
-               public virtual XmlAttributeCollection Attributes
-               {
+               public virtual XmlAttributeCollection Attributes {
                        get { return null; }
                }
 
-               [MonoTODO]
-               public virtual string BaseURI
-               {
-                       get { throw new NotImplementedException (); }
+               public virtual string BaseURI {
+                       get {
+                               // Isn't it conformant to W3C XML Base Recommendation?
+                               // As far as I tested, there are not...
+                               return (ParentNode != null) ? ParentNode.BaseURI : OwnerDocument.BaseURI;
+                       }
                }
 
                public virtual XmlNodeList ChildNodes {
                        get {
-                               return new XmlNodeListChildren(LastLinkedChild);
+                               return new XmlNodeListChildren (this);
                        }
                }
 
@@ -80,33 +72,80 @@ namespace System.Xml
                        get { return LastChild != null; }
                }
 
-               [MonoTODO]
                public virtual string InnerText {
-                       get { throw new NotImplementedException (); }
-                       set { throw new NotImplementedException (); }
+                       get {
+                               StringBuilder builder = new StringBuilder ();
+                               AppendChildValues (this, builder);
+                               return builder.ToString ();
+                       }
+
+                       set { throw new InvalidOperationException ("This node is read only. Cannot be modified."); }
+               }
+
+               private void AppendChildValues (XmlNode parent, StringBuilder builder)
+               {
+                       XmlNode node = parent.FirstChild;
+
+                       while (node != null) {
+                               switch (node.NodeType) {
+                               case XmlNodeType.Text:
+                               case XmlNodeType.CDATA:
+                               case XmlNodeType.SignificantWhitespace:
+                               case XmlNodeType.Whitespace:
+                                       builder.Append (node.Value);
+                                       break;
+                               }
+                               AppendChildValues (node, builder);
+                               node = node.NextSibling;
+                       }
                }
 
-               [MonoTODO]
                public virtual string InnerXml {
-                       get { throw new NotImplementedException (); }
-                       set { throw new NotImplementedException (); }
+                       get {
+                               StringWriter sw = new StringWriter ();
+                               XmlTextWriter xtw = new XmlTextWriter (sw);
+
+                               WriteContentTo (xtw);
+
+                               return sw.GetStringBuilder ().ToString ();
+                       }
+
+                       set {
+                               throw new InvalidOperationException ("This node is readonly or doesn't have any children.");
+                       }
                }
 
-               [MonoTODO]
                public virtual bool IsReadOnly {
-                       get { throw new NotImplementedException (); }
+                       get { return false; }
                }
 
-               [MonoTODO]
                [System.Runtime.CompilerServices.IndexerName("Item")]
                public virtual XmlElement this [string name] {
-                       get { throw new NotImplementedException (); }
+                       get { 
+                               foreach (XmlNode node in ChildNodes) {
+                                       if ((node.NodeType == XmlNodeType.Element) &&
+                                           (node.Name == name)) {
+                                               return (XmlElement) node;
+                                       }
+                               }
+
+                               return null;
+                       }
                }
 
-               [MonoTODO]
                [System.Runtime.CompilerServices.IndexerName("Item")]
                public virtual XmlElement this [string localname, string ns] {
-                       get { throw new NotImplementedException (); }
+                       get { 
+                               foreach (XmlNode node in ChildNodes) {
+                                       if ((node.NodeType == XmlNodeType.Element) &&
+                                           (node.LocalName == localname) && 
+                                           (node.NamespaceURI == ns)) {
+                                               return (XmlElement) node;
+                                       }
+                               }
+
+                               return null;
+                       }
                }
 
                public virtual XmlNode LastChild {
@@ -114,31 +153,39 @@ namespace System.Xml
                }
 
                internal virtual XmlLinkedNode LastLinkedChild {
-                       get { return null; }
-                       set { }
+                       get { return lastLinkedChild; }
+                       set { lastLinkedChild = value; }
                }
 
-               [MonoTODO]
                public abstract string LocalName { get; }
 
-               [MonoTODO]
                public abstract string Name     { get; }
 
-               [MonoTODO]
                public virtual string NamespaceURI {
-                       get { throw new NotImplementedException (); }
+                       get { return String.Empty; }
                }
 
                public virtual XmlNode NextSibling {
                        get { return null; }
                }
 
-               [MonoTODO]
                public abstract XmlNodeType NodeType { get;     }
 
-               [MonoTODO]
+               internal virtual XPathNodeType XPathNodeType {
+                       get {
+                               throw new InvalidOperationException ("Can not get XPath node type from " + this.GetType ().ToString ());
+                       }
+               }
+
                public virtual string OuterXml {
-                       get { throw new NotImplementedException (); }
+                       get {
+                               StringWriter sw = new StringWriter ();
+                               XmlTextWriter xtw = new XmlTextWriter (sw);
+
+                               WriteTo (xtw);
+
+                               return sw.ToString ();
+                       }
                }
 
                public virtual XmlDocument OwnerDocument {
@@ -149,72 +196,139 @@ namespace System.Xml
                        get { return parentNode; }
                }
 
-               [MonoTODO]
                public virtual string Prefix {
-                       get { throw new NotImplementedException (); }
-                       set { throw new NotImplementedException (); }
+                       get { return String.Empty; }
+                       set {}
                }
 
                public virtual XmlNode PreviousSibling {
                        get { return null; }
                }
 
-               [MonoTODO]
                public virtual string Value {
-                       get { throw new NotImplementedException (); }
-                       set { throw new NotImplementedException (); }
+                       get { return null; }
+                       set { throw new InvalidOperationException ("This node does not have a value"); }
+               }
+
+               internal virtual string XmlLang {
+                       get {
+                               if(Attributes != null)
+                                       foreach(XmlAttribute attr in Attributes)
+                                               if(attr.Name == "xml:lang")
+                                                       return attr.Value;
+                               return (ParentNode != null) ? ParentNode.XmlLang : OwnerDocument.XmlLang;
+                       }
+               }
+
+               internal virtual XmlSpace XmlSpace {
+                       get {
+                               if(Attributes != null) {
+                                       foreach(XmlAttribute attr in Attributes) {
+                                               if(attr.Name == "xml:space") {
+                                                       switch(attr.Value) {
+                                                       case "preserve": return XmlSpace.Preserve;
+                                                       case "default": return XmlSpace.Default;
+                                                       }
+                                                       break;
+                                               }
+                                       }
+                               }
+                               return (ParentNode != null) ? ParentNode.XmlSpace : OwnerDocument.XmlSpace;
+                       }
                }
 
                #endregion
 
                #region Methods
-               ///////////////////////////////////////////////////////////////////////
-               //
-               //      Methods
-               //
-               ///////////////////////////////////////////////////////////////////////
 
                public virtual XmlNode AppendChild (XmlNode newChild)
                {
-                       if ((NodeType == XmlNodeType.Document) || (NodeType == XmlNodeType.Element)) {
-                               LastLinkedChild = (XmlLinkedNode) newChild;
-                               return LastChild;
-                       }
-                       else {
-                               throw new InvalidOperationException();
-                       }
+                       // I assume that AppendChild(n) equals to InsertAfter(n, this.LastChild) or InsertBefore(n, null)
+                       return InsertBefore (newChild, null);
                }
 
-               [MonoTODO]
                public virtual XmlNode Clone ()
                {
-                       throw new NotImplementedException ();
+                       // By MS document, it is equivalent to CloneNode(true).
+                       return this.CloneNode (true);
                }
 
                public abstract XmlNode CloneNode (bool deep);
 
-               [MonoTODO]
                public XPathNavigator CreateNavigator ()
                {
-                       throw new NotImplementedException ();
+                       XmlDocument document = this.NodeType == XmlNodeType.Document ?
+                               this as XmlDocument : this.ownerDocument;
+                       return document.CreateNavigator (this);
                }
 
-               [MonoTODO]
                public IEnumerator GetEnumerator ()
                {
-                       throw new NotImplementedException ();
+                       return new XmlNodeListChildren (this).GetEnumerator ();
                }
 
-               [MonoTODO]
                public virtual string GetNamespaceOfPrefix (string prefix)
                {
-                       throw new NotImplementedException ();
+                       if (prefix == null)
+                               throw new ArgumentNullException ("prefix");
+
+                       XmlNode node;
+                       switch (NodeType) {
+                       case XmlNodeType.Attribute:
+                               node = ((XmlAttribute) this).OwnerElement;
+                               if (node == null)
+                                       return String.Empty;
+                               break;
+                       case XmlNodeType.Element:
+                               node = this;
+                               break;
+                       default:
+                               node = ParentNode;
+                               break;
+                       }
+
+                       while (node != null) {
+                               if (node.Prefix == prefix)
+                                       return node.NamespaceURI;
+                               if (node.Attributes != null) {
+                                       int count = node.Attributes.Count;
+                                       for (int i = 0; i < count; i++) {
+                                               XmlAttribute attr = node.Attributes [i];
+                                               if (prefix == attr.LocalName && attr.Prefix == "xmlns"
+                                                       || attr.Name == "xmlns" && prefix == String.Empty)
+                                                       return attr.Value;
+                                       }
+                               }
+                               node = node.ParentNode;
+                       }
+                       return String.Empty;
                }
 
-               [MonoTODO]
                public virtual string GetPrefixOfNamespace (string namespaceURI)
                {
-                       throw new NotImplementedException ();
+                       XmlNode node;
+                       switch (NodeType) {
+                       case XmlNodeType.Attribute:
+                               node = ((XmlAttribute) this).OwnerElement;
+                               break;
+                       case XmlNodeType.Element:
+                               node = this;
+                               break;
+                       default:
+                               node = ParentNode;
+                               break;
+                       }
+
+                       while (node != null && node.Attributes != null) {
+                               foreach (XmlAttribute attr in node.Attributes) {
+                                       if (attr.Prefix == "xmlns" && attr.Value == namespaceURI)
+                                               return attr.LocalName;
+                                       else if (attr.Name == "xmlns" && attr.Value == namespaceURI)
+                                               return String.Empty;
+                               }
+                               node = node.ParentNode;
+                       }
+                       return String.Empty;
                }
 
                object ICloneable.Clone ()
@@ -227,88 +341,429 @@ namespace System.Xml
                        return GetEnumerator ();
                }
 
-               [MonoTODO]
                public virtual XmlNode InsertAfter (XmlNode newChild, XmlNode refChild)
                {
-                       throw new NotImplementedException ();
+                       // InsertAfter(n1, n2) is equivalent to InsertBefore(n1, n2.PreviousSibling).
+
+                       // I took this way because current implementation 
+                       // Calling InsertBefore() in this method is faster than
+                       // the counterpart, since NextSibling is faster than 
+                       // PreviousSibling (these children are forward-only list).
+                       XmlNode argNode = null;
+                       if (refChild != null)
+                               argNode = refChild.NextSibling;
+                       else if (ChildNodes.Count > 0)
+                               argNode = FirstChild;
+                       return InsertBefore (newChild, argNode);
                }
 
-               [MonoTODO]
                public virtual XmlNode InsertBefore (XmlNode newChild, XmlNode refChild)
                {
-                       throw new NotImplementedException ();
+                       return InsertBefore (newChild, refChild, true, true);
+               }
+
+               // check for the node to be one of node ancestors
+               internal bool IsAncestor (XmlNode newChild)
+               {
+                       XmlNode currNode = this.ParentNode;
+                       while(currNode != null)
+                       {
+                               if(currNode == newChild)
+                                       return true;
+                               currNode = currNode.ParentNode;
+                       }
+                       return false;
+               }
+
+               internal XmlNode InsertBefore (XmlNode newChild, XmlNode refChild, bool checkNodeType, bool raiseEvent)
+               {
+                       if (checkNodeType)
+                               CheckNodeInsertion (newChild, refChild);
+
+                       XmlDocument ownerDoc = (NodeType == XmlNodeType.Document) ? (XmlDocument) this : OwnerDocument;
+
+                       if (raiseEvent)
+                               ownerDoc.onNodeInserting (newChild, this);
+
+                       if (newChild.ParentNode != null)
+                               newChild.ParentNode.RemoveChild (newChild, checkNodeType);
+
+                       if (newChild.NodeType == XmlNodeType.DocumentFragment) {
+                               int x = newChild.ChildNodes.Count;
+                               for (int i = 0; i < x; i++) {
+                                       XmlNode n = newChild.ChildNodes [0];
+                                       this.InsertBefore (n, refChild);        // recursively invokes events. (It is compatible with MS implementation.)
+                               }
+                       }
+                       else {
+                               XmlLinkedNode newLinkedChild = (XmlLinkedNode) newChild;
+                               XmlLinkedNode lastLinkedChild = LastLinkedChild;
+
+                               newLinkedChild.parentNode = this;
+
+                               if (refChild == null) {
+                                       // newChild is the last child:
+                                       // * set newChild as NextSibling of the existing lastchild
+                                       // * set LastChild = newChild
+                                       // * set NextSibling of newChild as FirstChild
+                                       if (LastLinkedChild != null) {
+                                               XmlLinkedNode formerFirst = (XmlLinkedNode) FirstChild;
+                                               LastLinkedChild.NextLinkedSibling = newLinkedChild;
+                                               LastLinkedChild = newLinkedChild;
+                                               newLinkedChild.NextLinkedSibling = formerFirst;
+                                       } else {
+                                               LastLinkedChild = newLinkedChild;
+                                               LastLinkedChild.NextLinkedSibling = newLinkedChild;     // FirstChild
+                                       }
+                               } else {
+                                       // newChild is not the last child:
+                                       // * if newchild is first, then set next of lastchild is newChild.
+                                       //   otherwise, set next of previous sibling to newChild
+                                       // * set next of newChild to refChild
+                                       XmlLinkedNode prev = refChild.PreviousSibling as XmlLinkedNode;
+                                       if (prev == null)
+                                               LastLinkedChild.NextLinkedSibling = newLinkedChild;
+                                       else
+                                               prev.NextLinkedSibling = newLinkedChild;
+                                       newLinkedChild.NextLinkedSibling = refChild as XmlLinkedNode;
+                               }
+                               switch (newChild.NodeType) {
+                               case XmlNodeType.EntityReference:
+                                       ((XmlEntityReference) newChild).SetReferencedEntityContent ();
+                                       break;
+                               case XmlNodeType.Entity:
+                                       ((XmlEntity) newChild).SetEntityContent ();
+                                       break;
+                               case XmlNodeType.DocumentType:
+                                       foreach (XmlEntity ent in ((XmlDocumentType)newChild).Entities)
+                                               ent.SetEntityContent ();
+                                       break;
+                               }
+
+                               if (raiseEvent)
+                                       ownerDoc.onNodeInserted (newChild, newChild.ParentNode);
+                       }
+                       return newChild;
+               }
+
+               private void CheckNodeInsertion (XmlNode newChild, XmlNode refChild)
+               {
+                       XmlDocument ownerDoc = (NodeType == XmlNodeType.Document) ? (XmlDocument)this : OwnerDocument;
+
+                       if (NodeType != XmlNodeType.Element &&
+                           NodeType != XmlNodeType.Attribute &&
+                           NodeType != XmlNodeType.Document &&
+                           NodeType != XmlNodeType.DocumentFragment)
+                               throw new InvalidOperationException (String.Format ("current node {0} is not allowed to have any children.", NodeType));
+
+                       switch (NodeType) {
+                       case XmlNodeType.Attribute:
+                               switch (newChild.NodeType) {
+                               case XmlNodeType.Text:
+                               case XmlNodeType.EntityReference:
+                                       break;
+                               default:
+                                       throw new ArgumentException (String.Format (
+                                               "Cannot insert specified type of node {0} as a child of this node {0}.", 
+                                               newChild.NodeType, NodeType));
+                               }
+                               break;
+                       case XmlNodeType.Element:
+                               switch (newChild.NodeType) {
+                               case XmlNodeType.Attribute:
+                               case XmlNodeType.Document:
+                               case XmlNodeType.DocumentType:
+                               case XmlNodeType.Entity:
+                               case XmlNodeType.Notation:
+                               case XmlNodeType.XmlDeclaration:
+                                       throw new ArgumentException ("Cannot insert specified type of node as a child of this node.");
+                               }
+                               break;
+                       }
+
+                       if (IsReadOnly)
+                               throw new ArgumentException ("The specified node is readonly.");
+
+                       if (newChild.OwnerDocument != ownerDoc)
+                               throw new ArgumentException ("Can't append a node created by another document.");
+
+                       if (refChild != null && newChild.OwnerDocument != refChild.OwnerDocument)
+                                       throw new ArgumentException ("argument nodes are on the different documents.");
+
+                       if(this == ownerDoc && ownerDoc.DocumentElement != null && (newChild is XmlElement))
+                               throw new XmlException ("multiple document element not allowed.");
+
+                       // checking validity finished. then appending...
+
+                       
+                       if (newChild == this || IsAncestor (newChild))
+                               throw new ArgumentException("Cannot insert a node or any ancestor of that node as a child of itself.");
+
                }
 
-               [MonoTODO]
                public virtual void Normalize ()
                {
-                       throw new NotImplementedException ();
+//                     if (tmpBuilder == null)
+                               tmpBuilder = new StringBuilder ();
+//                     tmpBuilder.Length = 0;
+                       int count = this.ChildNodes.Count;
+                       int start = 0;
+                       for (int i = 0; i < count; i++) {
+                               XmlNode c = ChildNodes [i];
+                               switch (c.NodeType) {
+                               case XmlNodeType.Text:
+                               case XmlNodeType.Whitespace:
+                               case XmlNodeType.SignificantWhitespace:
+                                       tmpBuilder.Append (c.Value);
+                                       break;
+                               default:
+                                       c.Normalize ();
+                                       NormalizeRange (start, i);
+                                       // Continue to normalize from next node.
+                                       start = i + 1;
+                                       break;
+                               }
+                       }
+                       if (start < count) {
+                               NormalizeRange (start, count);
+                       }
+
+                       tmpBuilder = null;
+               }
+
+               private void NormalizeRange (int start, int i)
+               {
+                       int keepPos = -1;
+                       // If Texts and Whitespaces are mixed, Text takes precedence to remain.
+                       // i.e. Whitespace should be removed.
+                       for (int j = start; j < i; j++) {
+                               XmlNode keep = ChildNodes [j];
+                               if (keep.NodeType == XmlNodeType.Text) {
+                                       keepPos = j;
+                                       break;
+                               }
+                               else if (keep.NodeType == XmlNodeType.SignificantWhitespace)
+                                       keepPos = j;
+                                       // but don't break up to find Text nodes.
+                       }
+                       // But if no Texts and one or more Whitespaces, then the first
+                       if (keepPos < 0 && i > start)
+                                       keepPos = 0;
+
+                       if (keepPos >= 0) {
+                               for (int del = start; del < keepPos; del++)
+                                       RemoveChild (ChildNodes [del]);
+                               for (int del = keepPos + 1; del < i; del++)
+                                       RemoveChild (ChildNodes [del]);
+                       }
+
+                       ChildNodes [keepPos].Value = tmpBuilder.ToString ();
+                       tmpBuilder.Length = 0;
                }
 
-               [MonoTODO]
                public virtual XmlNode PrependChild (XmlNode newChild)
                {
-                       throw new NotImplementedException ();
+                       return InsertAfter (newChild, null);
                }
 
                public virtual void RemoveAll ()
                {
-                       LastLinkedChild = null;
+                       if (Attributes != null)
+                               Attributes.RemoveAll ();
+                       XmlNode next = null;
+                       for (XmlNode node = FirstChild; node != null; node = next) {
+                               next = node.NextSibling;
+                               RemoveChild (node);
+                       }
                }
 
-               [MonoTODO]
                public virtual XmlNode RemoveChild (XmlNode oldChild)
                {
-                       throw new NotImplementedException ();
+                       return RemoveChild (oldChild, true);
+               }
+
+               private void CheckNodeRemoval ()
+               {
+                       if (NodeType != XmlNodeType.Attribute && 
+                               NodeType != XmlNodeType.Element && 
+                               NodeType != XmlNodeType.Document && 
+                               NodeType != XmlNodeType.DocumentFragment)
+                               throw new ArgumentException (String.Format ("This {0} node cannot remove child.", NodeType));
+
+                       if (IsReadOnly)
+                               throw new ArgumentException (String.Format ("This {0} node is read only.", NodeType));
+               }
+
+               internal XmlNode RemoveChild (XmlNode oldChild, bool checkNodeType)
+               {
+                       XmlDocument ownerDoc = (NodeType == XmlNodeType.Document) ? (XmlDocument)this : OwnerDocument;
+                       if(oldChild.ParentNode != this)
+                               throw new XmlException ("specified child is not child of this node.");
+
+                       if (checkNodeType)
+                               ownerDoc.onNodeRemoving (oldChild, oldChild.ParentNode);
+
+                       if (checkNodeType)
+                               CheckNodeRemoval ();
+
+                       if (Object.ReferenceEquals (LastLinkedChild, LastLinkedChild.NextLinkedSibling) && Object.ReferenceEquals (LastLinkedChild, oldChild))
+                               // If there is only one children, simply clear.
+                               LastLinkedChild = null;
+                       else {
+                               XmlLinkedNode oldLinkedChild = (XmlLinkedNode) oldChild;
+                               XmlLinkedNode beforeLinkedChild = LastLinkedChild;
+                               XmlLinkedNode firstChild = (XmlLinkedNode) FirstChild;
+                               
+                               while (Object.ReferenceEquals (beforeLinkedChild.NextLinkedSibling, LastLinkedChild) == false && 
+                                       Object.ReferenceEquals (beforeLinkedChild.NextLinkedSibling, oldLinkedChild) == false)
+                                       beforeLinkedChild = beforeLinkedChild.NextLinkedSibling;
+
+                               if (Object.ReferenceEquals (beforeLinkedChild.NextLinkedSibling, oldLinkedChild) == false)
+                                       throw new ArgumentException ();
+
+                               beforeLinkedChild.NextLinkedSibling = oldLinkedChild.NextLinkedSibling;
+
+                               // Each derived class may have its own LastLinkedChild, so we must set it explicitly.
+                               if (oldLinkedChild.NextLinkedSibling == firstChild)
+                                       this.LastLinkedChild = beforeLinkedChild;
+
+                               oldLinkedChild.NextLinkedSibling = null;
+                               }
+
+                       if (checkNodeType)
+                               ownerDoc.onNodeRemoved (oldChild, oldChild.ParentNode);
+                       oldChild.parentNode = null;     // clear parent 'after' above logic.
+
+                       return oldChild;
                }
 
-               [MonoTODO]
                public virtual XmlNode ReplaceChild (XmlNode newChild, XmlNode oldChild)
                {
-                       throw new NotImplementedException ();
+                       if(oldChild.ParentNode != this)
+                               throw new InvalidOperationException ("oldChild is not a child of this node.");
+                       
+                       if (newChild == this || IsAncestor (newChild))
+                               throw new ArgumentException("Cannot insert a node or any ancestor of that node as a child of itself.");
+                       
+                       foreach(XmlNode n in ChildNodes) {
+                               if(n == oldChild) {
+                                       XmlNode prev = oldChild.PreviousSibling;
+                                       RemoveChild (oldChild);
+                                       InsertAfter (newChild, prev);
+                                       break;
+                               }
+                       }
+                       return oldChild;
+               }
+
+               internal void SearchDescendantElements (string name, bool matchAll, ArrayList list)
+               {
+                       foreach (XmlNode n in ChildNodes){
+                               if (n.NodeType != XmlNodeType.Element)
+                                       continue;
+                               if (matchAll || n.Name == name)
+                                       list.Add (n);
+                               n.SearchDescendantElements (name, matchAll, list);
+                       }
                }
 
-               [MonoTODO]
-               public virtual XmlNodeList SelectNodes (string xpath)
+               internal void SearchDescendantElements (string name, bool matchAllName, string ns, bool matchAllNS, ArrayList list)
                {
-                       throw new NotImplementedException ();
+                       foreach (XmlNode n in ChildNodes){
+                               if (n.NodeType != XmlNodeType.Element)
+                                       continue;
+                               if ((matchAllName || n.LocalName == name)
+                                       && (matchAllNS || n.NamespaceURI == ns))
+                                       list.Add (n);
+                               n.SearchDescendantElements (name, matchAllName, ns, matchAllNS, list);
+                       }
                }
 
-               [MonoTODO]
-               public virtual XmlNodeList SelectNodes (string xpath, XmlNamespaceManager nsmgr)
+               public XmlNodeList SelectNodes (string xpath)
                {
-                       throw new NotImplementedException ();
+                       return SelectNodes (xpath, null);
                }
 
-               [MonoTODO]
-               public virtual XmlNode SelectSingleNode (string xpath)
+               public XmlNodeList SelectNodes (string xpath, XmlNamespaceManager nsmgr)
                {
-                       throw new NotImplementedException ();
+                       XPathNavigator nav = CreateNavigator ();
+                       XPathExpression expr = nav.Compile (xpath);
+                       if (nsmgr != null)
+                               expr.SetContext (nsmgr);
+                       XPathNodeIterator iter = nav.Select (expr);
+                       ArrayList rgNodes = new ArrayList ();
+                       while (iter.MoveNext ())
+                       {
+                               rgNodes.Add (((XmlDocumentNavigator) iter.Current).Node);
+                       }
+                       return new XmlNodeArrayList (rgNodes);
                }
 
-               [MonoTODO]
-               public virtual XmlNode SelectSingleNode (string xpath, XmlNamespaceManager nsmgr)
+               public XmlNode SelectSingleNode (string xpath)
                {
-                       throw new NotImplementedException ();
+                       return SelectSingleNode (xpath, null);
                }
 
-               internal void SetParentNode (XmlNode parent)
+               public XmlNode SelectSingleNode (string xpath, XmlNamespaceManager nsmgr)
                {
-                       parentNode = parent;
+                       XPathNavigator nav = CreateNavigator ();
+                       XPathExpression expr = nav.Compile (xpath);
+                       if (nsmgr != null)
+                               expr.SetContext (nsmgr);
+                       XPathNodeIterator iter = nav.Select (expr);
+                       if (!iter.MoveNext ())
+                               return null;
+                       return ((XmlDocumentNavigator) iter.Current).Node;
                }
 
-               [MonoTODO]
                public virtual bool Supports (string feature, string version)
                {
-                       throw new NotImplementedException ();
+                       if (String.Compare (feature, "xml", true) == 0 // not case-sensitive
+                           && (String.Compare (version, "1.0", true) == 0
+                               || String.Compare (version, "2.0", true) == 0))
+                               return true;
+                       else
+                               return false;
                }
 
-               [MonoTODO]
                public abstract void WriteContentTo (XmlWriter w);
 
-               [MonoTODO]
                public abstract void WriteTo (XmlWriter w);
 
+               // It parses this and all the ancestor elements,
+               // find 'xmlns' declarations, stores and then return them.
+               internal XmlNamespaceManager ConstructNamespaceManager ()
+               {
+                       XmlDocument doc = this is XmlDocument ? (XmlDocument)this : this.OwnerDocument;
+                       XmlNamespaceManager nsmgr = new XmlNamespaceManager (doc.NameTable);
+                       XmlElement el = null;
+                       switch(this.NodeType) {
+                       case XmlNodeType.Attribute:
+                               el = ((XmlAttribute)this).OwnerElement;
+                               break;
+                       case XmlNodeType.Element:
+                               el = this as XmlElement;
+                               break;
+                       default:
+                               el = this.ParentNode as XmlElement;
+                               break;
+                       }
+
+                       while(el != null) {
+                               foreach(XmlAttribute attr in el.Attributes) {
+                                       if(attr.Prefix == "xmlns") {
+                                               if (nsmgr.LookupNamespace (attr.LocalName) != attr.Value)
+                                                       nsmgr.AddNamespace (attr.LocalName, attr.Value);
+                                       } else if(attr.Name == "xmlns") {
+                                               if(nsmgr.LookupNamespace (String.Empty) != attr.Value)
+                                                       nsmgr.AddNamespace (String.Empty, attr.Value);
+                                       }
+                               }
+                               // When reached to document, then it will set null value :)
+                               el = el.ParentNode as XmlElement;
+                       }
+                       return nsmgr;
+               }
                #endregion
        }
 }