2003-01-26 Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
[mono.git] / mcs / class / System.XML / System.Xml / XmlNode.cs
index 419f41ca19a4c463b1e3557036e9ae346102e998..29914148f4f677693c77fc37f8ba02ee3ae75565 100644 (file)
@@ -3,8 +3,10 @@
 //
 // 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;
@@ -35,19 +37,21 @@ namespace System.Xml
 
                #region Properties
 
-               public virtual XmlAttributeCollection Attributes
-               {
+               public virtual XmlAttributeCollection Attributes {
                        get { return null; }
                }
 
-               public virtual string BaseURI
-               {
-                       get { return ParentNode.BaseURI; }
+               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(this);
+                               return new XmlNodeListChildren (this);
                        }
                }
 
@@ -66,7 +70,7 @@ namespace System.Xml
                        get { return LastChild != null; }
                }
 
-               [MonoTODO]
+               [MonoTODO("confirm whether this way is right for each not-overriden types.")]
                public virtual string InnerText {
                        get {
                                StringBuilder builder = new StringBuilder ();
@@ -95,9 +99,9 @@ namespace System.Xml
                                StringWriter sw = new StringWriter ();
                                XmlTextWriter xtw = new XmlTextWriter (sw);
 
-                               WriteContentTo(xtw);
+                               WriteContentTo (xtw);
 
-                               return sw.GetStringBuilder().ToString();
+                               return sw.GetStringBuilder ().ToString ();
                        }
 
                        set { throw new NotImplementedException (); }
@@ -170,9 +174,9 @@ namespace System.Xml
                                StringWriter sw = new StringWriter ();
                                XmlTextWriter xtw = new XmlTextWriter (sw);
 
-                               WriteTo(xtw);
+                               WriteTo (xtw);
 
-                               return sw.GetStringBuilder().ToString();
+                               return sw.GetStringBuilder ().ToString ();
                        }
                }
 
@@ -198,6 +202,33 @@ namespace System.Xml
                        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
@@ -205,65 +236,13 @@ namespace System.Xml
                public virtual XmlNode AppendChild (XmlNode newChild)
                {
                        // I assume that AppendChild(n) equals to InsertAfter(n, this.LastChild) or InsertBefore(n, null)
-                       return InsertBefore(newChild, null);
-
-                       // Below are formerly used logic.
-/*                     XmlDocument ownerDoc = (NodeType == XmlNodeType.Document) ? (XmlDocument)this : OwnerDocument;
-
-                       if (NodeType == XmlNodeType.Document || NodeType == XmlNodeType.Element || NodeType == XmlNodeType.Attribute || NodeType == XmlNodeType.DocumentFragment) {
-                               
-                               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.");
-
-                               // checking validity finished. then appending...
-
-                               ownerDoc.onNodeInserting (newChild, this);
-
-                               if(newChild.ParentNode != null)
-                                       newChild.ParentNode.RemoveChild(newChild);
-
-                               if(newChild.NodeType == XmlNodeType.DocumentFragment)
-                               {
-                                       int x = newChild.ChildNodes.Count;
-                                       for(int i=0; i<x; i++)
-                                       {
-                                               // When this logic became to remove children in order, then index will have never to increments.
-                                               XmlNode n = newChild.ChildNodes[0];
-                                               this.AppendChild(n);    // recursively invokes events. (It is compatible with MS implementation.)
-                                       }
-                               }
-                               else
-                               {
-                                       XmlLinkedNode newLinkedChild = (XmlLinkedNode) newChild;
-                                       XmlLinkedNode lastLinkedChild = LastLinkedChild;
-
-                                       newLinkedChild.parentNode = this;
-                               
-                                       if (lastLinkedChild != null) 
-                                       {
-                                               newLinkedChild.NextLinkedSibling = lastLinkedChild.NextLinkedSibling;
-                                               lastLinkedChild.NextLinkedSibling = newLinkedChild;
-                                       } 
-                                       else
-                                               newLinkedChild.NextLinkedSibling = newLinkedChild;
-                               
-                                       LastLinkedChild = newLinkedChild;
-
-                                       ownerDoc.onNodeInserted (newChild, newChild.ParentNode);
-
-                               }
-                               return newChild;
-                       } else
-                               throw new InvalidOperationException();
-*/             }
+                       return InsertBefore (newChild, null);
+               }
 
                public virtual XmlNode Clone ()
                {
                        // By MS document, it is equivalent to CloneNode(true).
-                       return this.CloneNode(true);
+                       return this.CloneNode (true);
                }
 
                public abstract XmlNode CloneNode (bool deep);
@@ -271,28 +250,27 @@ namespace System.Xml
                [MonoTODO]
                public XPathNavigator CreateNavigator ()
                {
-                       return new XmlDocumentNavigator(this);
+                       return new XmlDocumentNavigator (this);
                }
 
                public IEnumerator GetEnumerator ()
                {
-                       return new XmlNodeListChildren(this).GetEnumerator();
+                       return new XmlNodeListChildren (this).GetEnumerator ();
                }
 
-//             [MonoTODO]
+               [MonoTODO("performance problem.")]
                public virtual string GetNamespaceOfPrefix (string prefix)
                {
-                       XmlNamespaceManager nsmgr = ConstructNamespaceManager();
-                       return nsmgr.LookupNamespace(prefix);
-//                     throw new NotImplementedException ();
+                       XmlNamespaceManager nsmgr = ConstructNamespaceManager ();
+                       return nsmgr.LookupNamespace (prefix);
                }
 
-//             [MonoTODO]
+               [MonoTODO("performance problem.")]
                public virtual string GetPrefixOfNamespace (string namespaceURI)
                {
-                       XmlNamespaceManager nsmgr = ConstructNamespaceManager();
-                       return nsmgr.LookupPrefix(namespaceURI);
-//                     throw new NotImplementedException ();
+                       XmlNamespaceManager nsmgr = ConstructNamespaceManager ();
+                       string ns = nsmgr.LookupPrefix (namespaceURI);
+                       return (ns != null) ? ns : String.Empty;
                }
 
                object ICloneable.Clone ()
@@ -305,83 +283,80 @@ namespace System.Xml
                        return GetEnumerator ();
                }
 
-               [MonoTODO]
                public virtual XmlNode InsertAfter (XmlNode newChild, XmlNode refChild)
                {
                        // I assume that insertAfter(n1, n2) equals to InsertBefore(n1, n2.PreviousSibling).
 
                        // I took this way because rather than calling InsertAfter() from InsertBefore()
                        //   because current implementation of 'NextSibling' looks faster than 'PreviousSibling'.
-                       XmlNode argNode = (refChild == null) ? null : refChild.NextSibling;
-                       return InsertBefore(newChild, argNode);
+                       XmlNode argNode = null;
+                       if(refChild != null)
+                               argNode = refChild.NextSibling;
+                       else if(ChildNodes.Count > 0)
+                               argNode = FirstChild;
+                       return InsertBefore (newChild, argNode);
                }
 
-               [MonoTODO]
+               [MonoTODO("If inserted node is entity reference, then check conforming entity. Wait for DTD implementation.")]
                public virtual XmlNode InsertBefore (XmlNode newChild, XmlNode refChild)
                {
                        XmlDocument ownerDoc = (NodeType == XmlNodeType.Document) ? (XmlDocument)this : OwnerDocument;
 
-                       if (NodeType == XmlNodeType.Document || NodeType == XmlNodeType.Element || NodeType == XmlNodeType.Attribute || NodeType == XmlNodeType.DocumentFragment) 
-                       {                       
+                       if (NodeType == XmlNodeType.Document ||
+                           NodeType == XmlNodeType.Element ||
+                           NodeType == XmlNodeType.Attribute ||
+                           NodeType == XmlNodeType.DocumentFragment) {
                                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)
-                               {
-                                       if(newChild.OwnerDocument != refChild.OwnerDocument)
+                               if (refChild != null && newChild.OwnerDocument != refChild.OwnerDocument)
                                                throw new ArgumentException ("argument nodes are on the different documents.");
 
-                                       if(refChild == ownerDoc.DocumentElement && (newChild is XmlElement || newChild is XmlCharacterData || newChild is XmlEntityReference))
-                                               throw new XmlException("cannot insert this node to this position.");
-                               }
+                               // This check is done by MS.NET 1.0, but isn't done for MS.NET 1.1. 
+                               // Skip this check in the meantime...
+//                             if(this == ownerDoc && ownerDoc.DocumentElement != null && (newChild is XmlElement))
+//                                     throw new XmlException ("multiple document element not allowed.");
+
                                // checking validity finished. then appending...
 
                                ownerDoc.onNodeInserting (newChild, this);
 
                                if(newChild.ParentNode != null)
-                                       newChild.ParentNode.RemoveChild(newChild);
+                                       newChild.ParentNode.RemoveChild (newChild);
 
-                               if(newChild.NodeType == XmlNodeType.DocumentFragment)
-                               {
+                               if(newChild.NodeType == XmlNodeType.DocumentFragment) {
                                        int x = newChild.ChildNodes.Count;
-                                       for(int i=0; i<x; i++)
-                                       {
-                                               // When this logic became to remove children in order, then index will have never to increments.
-                                               XmlNode n = newChild.ChildNodes[0];
-                                               this.InsertBefore(n, refChild); // recursively invokes events. (It is compatible with MS implementation.)
+                                       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
-                               {
+                               else {
                                        XmlLinkedNode newLinkedChild = (XmlLinkedNode) newChild;
                                        XmlLinkedNode lastLinkedChild = LastLinkedChild;
 
                                        newLinkedChild.parentNode = this;
 
-                                       if(refChild == null)
-                                       {
+                                       if(refChild == null) {
                                                // append last, so:
                                                // * set nextSibling of previous lastchild to newChild
                                                // * set lastchild = newChild
                                                // * set next of newChild to firstChild
-                                               if(LastLinkedChild != null)
-                                               {
+                                               if(LastLinkedChild != null) {
                                                        XmlLinkedNode formerFirst = FirstChild as XmlLinkedNode;
                                                        LastLinkedChild.NextLinkedSibling = newLinkedChild;
                                                        LastLinkedChild = newLinkedChild;
                                                        newLinkedChild.NextLinkedSibling = formerFirst;
                                                }
-                                               else
-                                               {
+                                               else {
                                                        LastLinkedChild = newLinkedChild;
                                                        LastLinkedChild.NextLinkedSibling = newLinkedChild;     // FirstChild
                                                }
                                        }
-                                       else
-                                       {
+                                       else {
                                                // append not last, so:
                                                // * if newchild is first, then set next of lastchild is newChild.
                                                //   otherwise, set next of previous sibling to newChild
@@ -398,7 +373,7 @@ namespace System.Xml
                                return newChild;
                        } 
                        else
-                               throw new InvalidOperationException();
+                               throw new InvalidOperationException ();
                }
 
                [MonoTODO]
@@ -407,62 +382,76 @@ namespace System.Xml
                        throw new NotImplementedException ();
                }
 
-               [MonoTODO]
                public virtual XmlNode PrependChild (XmlNode newChild)
                {
-                       throw new NotImplementedException ();
+                       return InsertAfter (newChild, null);
                }
 
                public virtual void RemoveAll ()
                {
-                       XmlDocument ownerDoc = (NodeType == XmlNodeType.Document) ? (XmlDocument)this : OwnerDocument;
-
-                       ownerDoc.onNodeRemoving (this, this.ParentNode);
-                       LastLinkedChild = null;
-                       ownerDoc.onNodeRemoved (this, this.ParentNode);
+                       XmlNode next = null;
+                       for (XmlNode node = FirstChild; node != null; node = next) {
+                               next = node.NextSibling;
+                               RemoveChild (node);
+                       }
                }
 
                public virtual XmlNode RemoveChild (XmlNode oldChild)
                {
+                       XmlDocument ownerDoc = (NodeType == XmlNodeType.Document) ? (XmlDocument)this : OwnerDocument;
                        if(oldChild.ParentNode != this)
-                               throw new XmlException("specified child is not child of this node.");
+                               throw new XmlException ("specified child is not child of this node.");
 
-                       OwnerDocument.onNodeRemoving (oldChild, oldChild.ParentNode);
+                       ownerDoc.onNodeRemoving (oldChild, oldChild.ParentNode);
 
-                       if (NodeType == XmlNodeType.Document || NodeType == XmlNodeType.Element || NodeType == XmlNodeType.Attribute || NodeType == XmlNodeType.DocumentFragment) 
-                       {
+                       if (NodeType == XmlNodeType.Document || NodeType == XmlNodeType.Element || NodeType == XmlNodeType.Attribute || NodeType == XmlNodeType.DocumentFragment) {
                                if (IsReadOnly)
-                                       throw new ArgumentException();
+                                       throw new ArgumentException ();
 
-                               if (Object.ReferenceEquals(LastLinkedChild, LastLinkedChild.NextLinkedSibling) && Object.ReferenceEquals(LastLinkedChild, oldChild))
+                               if (Object.ReferenceEquals (LastLinkedChild, LastLinkedChild.NextLinkedSibling) && Object.ReferenceEquals (LastLinkedChild, oldChild))
                                        LastLinkedChild = null;
                                else {
                                        XmlLinkedNode oldLinkedChild = (XmlLinkedNode)oldChild;
                                        XmlLinkedNode beforeLinkedChild = LastLinkedChild;
                                        
-                                       while (!Object.ReferenceEquals(beforeLinkedChild.NextLinkedSibling, LastLinkedChild) && !Object.ReferenceEquals(beforeLinkedChild.NextLinkedSibling, oldLinkedChild))
+                                       while (!Object.ReferenceEquals (beforeLinkedChild.NextLinkedSibling, LastLinkedChild) && !Object.ReferenceEquals (beforeLinkedChild.NextLinkedSibling, oldLinkedChild))
                                                beforeLinkedChild = beforeLinkedChild.NextLinkedSibling;
 
-                                       if (!Object.ReferenceEquals(beforeLinkedChild.NextLinkedSibling, oldLinkedChild))
-                                               throw new ArgumentException();
+                                       if (!Object.ReferenceEquals (beforeLinkedChild.NextLinkedSibling, oldLinkedChild))
+                                               throw new ArgumentException ();
 
                                        beforeLinkedChild.NextLinkedSibling = oldLinkedChild.NextLinkedSibling;
                                        oldLinkedChild.NextLinkedSibling = null;
                                 }
 
-                               OwnerDocument.onNodeRemoved (oldChild, oldChild.ParentNode);
+                               ownerDoc.onNodeRemoved (oldChild, oldChild.ParentNode);
                                oldChild.parentNode = null;     // clear parent 'after' above logic.
 
                                return oldChild;
                        } 
                        else
-                               throw new ArgumentException();
+                               throw new ArgumentException ();
                }
 
-               [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.");
+                       XmlNode parent = this.ParentNode;
+                       while(parent != null) {
+                               if(newChild == parent)
+                                       throw new InvalidOperationException ("newChild is ancestor of this node.");
+                               parent = parent.ParentNode;
+                       }
+                       foreach(XmlNode n in ChildNodes) {
+                               if(n == oldChild) {
+                                       XmlNode prev = oldChild.PreviousSibling;
+                                       RemoveChild (oldChild);
+                                       InsertAfter (newChild, prev);
+                                       break;
+                               }
+                       }
+                       return oldChild;
                }
 
                public XmlNodeList SelectNodes (string xpath)
@@ -519,114 +508,31 @@ namespace System.Xml
 
                public abstract void WriteTo (XmlWriter w);
 
-               // It parses with XmlReader and then construct DOM of the parsed contents.
-               internal void ConstructDOM(XmlReader xmlReader, XmlNode currentNode)
-               {
-                       // I am not confident whether this method should be placed in this class or not...
-                       // Please verify its validity and then erase this comment;-)
-                       XmlNode newNode;
-                       XmlDocument doc = currentNode is XmlDocument ? (XmlDocument)currentNode : currentNode.OwnerDocument;
-                       // Below are 'almost' copied from XmlDocument.Load(XmlReader xmlReader)
-                       while (xmlReader.Read ()) 
-                       {
-                               switch (xmlReader.NodeType) 
-                               {
-                                       case XmlNodeType.CDATA:
-                                               newNode = doc.CreateCDataSection(xmlReader.Value);
-                                               currentNode.AppendChild (newNode);
-                                               break;
-
-                                       case XmlNodeType.Comment:
-                                               newNode = doc.CreateComment (xmlReader.Value);
-                                               currentNode.AppendChild (newNode);
-                                               break;
-
-                                       case XmlNodeType.Element:
-                                               XmlElement element = doc.CreateElement (xmlReader.Prefix, xmlReader.LocalName, xmlReader.NamespaceURI);
-                                               currentNode.AppendChild (element);
-
-                                               // set the element's attributes.
-                                               while (xmlReader.MoveToNextAttribute ()) 
-                                               {
-                                                       XmlAttribute attribute = doc.CreateAttribute (xmlReader.Prefix, xmlReader.LocalName, xmlReader.NamespaceURI);
-                                                       attribute.Value = xmlReader.Value;
-                                                       element.SetAttributeNode (attribute);
-                                               }
-
-                                               xmlReader.MoveToElement ();
-
-                                               // if this element isn't empty, push it onto our "stack".
-                                               if (!xmlReader.IsEmptyElement)
-                                                       currentNode = element;
-
-                                               break;
-
-                                       case XmlNodeType.EndElement:
-                                               currentNode = currentNode.ParentNode;
-                                               break;
-
-                                       case XmlNodeType.ProcessingInstruction:
-                                               newNode = doc.CreateProcessingInstruction (xmlReader.Name, xmlReader.Value);
-                                               currentNode.AppendChild (newNode);
-                                               break;
-
-                                       case XmlNodeType.Text:
-                                               newNode = doc.CreateTextNode (xmlReader.Value);
-                                               currentNode.AppendChild (newNode);
-                                               break;
-
-                                       case XmlNodeType.XmlDeclaration:
-                                               // empty strings are dummy, then gives over setting value contents to setter.
-                                               newNode = doc.CreateXmlDeclaration("1.0" , String.Empty, String.Empty);
-                                               ((XmlDeclaration)newNode).Value = xmlReader.Value;
-                                               this.AppendChild(newNode);
-                                               break;
-
-                                       case XmlNodeType.DocumentType:
-                                               XmlTextReader xmlTextReader = xmlReader as XmlTextReader;
-                                               if(xmlTextReader != null)
-                                               {
-                                                       XmlDocumentType dtdNode = doc.CreateDocumentType(xmlTextReader.Name, xmlTextReader.publicId, xmlTextReader.systemId, xmlTextReader.Value);
-                                                       this.AppendChild(dtdNode);
-                                               }
-                                               else
-                                                       throw new XmlException("construction of DocumentType node from this XmlReader is not supported.");
-                                               break;
-                               }
-                       }
-               }
-
                // It parses this and all the ancestor elements,
                // find 'xmlns' declarations, stores and then return them.
                // TODO: tests
-               internal XmlNamespaceManager ConstructNamespaceManager()
+               internal XmlNamespaceManager ConstructNamespaceManager ()
                {
                        XmlDocument doc = this is XmlDocument ? (XmlDocument)this : this.OwnerDocument;
-                       XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
+                       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;
+                       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" || (attr.Name == "xmlns" && attr.Prefix == String.Empty))
-                                       {
-                                               if(nsmgr.LookupNamespace(attr.LocalName) == null )
-                                               {
-                                                       nsmgr.AddNamespace(attr.LocalName, attr.Value);
-                                               }
+                       while(el != null) {
+                               foreach(XmlAttribute attr in el.Attributes) {
+                                       if(attr.Prefix == "xmlns" || (attr.Name == "xmlns" && attr.Prefix == String.Empty)) {
+                                               if(nsmgr.LookupNamespace (attr.LocalName) == null )
+                                                       nsmgr.AddNamespace (attr.LocalName, attr.Value);
                                        }
                                }
                                // When reached to document, then it will set null value :)