* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / System.XML / System.Xml / XmlDocumentNavigator.cs
index 0426ae7f62dbb1775605fde2b946f76c99fa8320..15f75ee4eb07b1ce4f97d8f88e539612abba5a57 100644 (file)
@@ -9,9 +9,31 @@
 // (C) 2003 Atsushi Enomoto
 //
 
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
 using System;
 using System.Collections;
 using System.Xml;
+using System.Xml.Schema;
 using System.Xml.XPath;
 
 namespace System.Xml
@@ -50,7 +72,7 @@ namespace System.Xml
                private XmlDocument document;
                // Current namespace node (ancestor's attribute of current node).
                private XmlAttribute nsNode;
-               private ArrayList iteratedNsNames = new ArrayList ();
+               private ArrayList iteratedNsNames;
                #endregion
 
                #region Properties
@@ -66,10 +88,13 @@ namespace System.Xml
                                if (NsNode != null)
                                        return false;
 
-                               if (node.Attributes != null)
-                                       for (int i = 0; i < node.Attributes.Count; i++)
-                                               if (node.Attributes [i].NamespaceURI != Xmlns)
-                                                       return true;
+                               XmlElement el = node as XmlElement;
+                               if (el == null || !el.HasAttributes)
+                                       return false;
+
+                               for (int i = 0; i < node.Attributes.Count; i++)
+                                       if (node.Attributes [i].NamespaceURI != Xmlns)
+                                               return true;
                                return false;
                        }
                }
@@ -81,7 +106,7 @@ namespace System.Xml
 
                                XPathNodeType nodeType = NodeType;
                                bool canHaveChildren = nodeType == XPathNodeType.Root || nodeType == XPathNodeType.Element;
-                               return canHaveChildren && node.FirstChild != null;
+                               return canHaveChildren && GetFirstChild (node) != null;
                        }
                }
 
@@ -99,9 +124,18 @@ namespace System.Xml
                        get { return nsNode; }
                        set {
                                if (value == null)
-                                       iteratedNsNames.Clear ();
+                                       iteratedNsNames = null;
                                else
+                               {
+                                       if (iteratedNsNames == null)
+                                               iteratedNsNames = new ArrayList();
+                                       else
+                                       {
+                                               if (iteratedNsNames.IsReadOnly)
+                                                       iteratedNsNames = new ArrayList(iteratedNsNames);
+                                       }
                                        iteratedNsNames.Add (value.Name);
+                               }
                                nsNode = value;
                        }
                }
@@ -152,13 +186,48 @@ namespace System.Xml
                }
 
                public override XPathNodeType NodeType {
-                       get { return (NsNode != null) ? XPathNodeType.Namespace : node.XPathNodeType; }
+                       get {
+                               if (NsNode != null)
+                                       return XPathNodeType.Namespace;
+                               XmlNode n = node;
+                               bool sw = false;
+                               do {
+                                       switch (n.NodeType) {
+                                       case XmlNodeType.SignificantWhitespace:
+                                               sw = true;
+                                               n = GetNextSibling (n);
+                                               break;
+                                       case XmlNodeType.Whitespace:
+                                               n = GetNextSibling (n);
+                                               break;
+                                       case XmlNodeType.Text:
+                                       case XmlNodeType.CDATA:
+                                               return XPathNodeType.Text;
+                                       default:
+                                               n = null;
+                                               break;
+                                       }
+                               } while (n != null);
+                               return sw ?
+                                       XPathNodeType.SignificantWhitespace :
+                                       node.XPathNodeType;
+                       }
                }
 
                public override string Prefix {
                        get { return (NsNode != null) ? String.Empty : node.Prefix; }
                }
 
+#if NET_2_0
+               public override IXmlSchemaInfo SchemaInfo {
+                       get { return NsNode != null ? null : node.SchemaInfo; }
+               }
+
+               public override object UnderlyingObject {
+                       get { return node; }
+               }
+#endif
+
                public override string Value {
                        get {
                                switch (NodeType) {
@@ -170,7 +239,7 @@ namespace System.Xml
                                case XPathNodeType.Whitespace:
                                case XPathNodeType.SignificantWhitespace:
                                        string value = node.Value;
-                                       for (XmlNode n = node.NextSibling; n != null; n = n.NextSibling) {
+                                       for (XmlNode n = GetNextSibling (node); n != null; n = GetNextSibling (n)) {
                                                switch (n.XPathNodeType) {
                                                case XPathNodeType.Text:
                                                case XPathNodeType.Whitespace:
@@ -203,10 +272,17 @@ namespace System.Xml
 
                private bool CheckNsNameAppearance (string name, string ns)
                {
-                       if (iteratedNsNames.Contains (name))
+                       if (iteratedNsNames != null && iteratedNsNames.Contains (name))
                                return true;
                        // default namespace erasure - just add name and never return this node
                        if (ns == String.Empty) {
+                               if (iteratedNsNames == null)
+                                       iteratedNsNames = new ArrayList();
+                               else
+                               {
+                                       if (iteratedNsNames.IsReadOnly)
+                                               iteratedNsNames = new ArrayList(iteratedNsNames);
+                               }
                                iteratedNsNames.Add ("xmlns");
                                return true;
                        }
@@ -218,7 +294,7 @@ namespace System.Xml
                {
                        XmlDocumentNavigator clone = new XmlDocumentNavigator (node, nsNodeXml);
                        clone.nsNode = nsNode;
-                       clone.iteratedNsNames = (ArrayList) iteratedNsNames.Clone ();
+                       clone.iteratedNsNames = (iteratedNsNames == null || iteratedNsNames.IsReadOnly) ? iteratedNsNames : ArrayList.ReadOnly(iteratedNsNames);
                        return clone;
                }
 
@@ -239,7 +315,24 @@ namespace System.Xml
                        // returns actual namespace for the other nodes.
                        return Node.GetNamespaceOfPrefix (name);
                }
-               
+
+               public override bool IsDescendant (XPathNavigator other)
+               {
+                       if (NsNode != null)
+                               return false;
+                       XmlDocumentNavigator o = other as XmlDocumentNavigator;
+                       if (o == null)
+                               return false;
+                       XmlNode n =
+                               o.node.NodeType == XmlNodeType.Attribute ?
+                               ((XmlAttribute) o.node).OwnerElement :
+                               o.node.ParentNode;
+                       for (;n != null; n = n.ParentNode)
+                               if (n == node)
+                                       return true;
+                       return false;
+               }
+
                public override bool IsSamePosition (XPathNavigator other)
                {
                        XmlDocumentNavigator otherDocumentNavigator = other as XmlDocumentNavigator;
@@ -264,38 +357,31 @@ namespace System.Xml
 
                public override bool MoveToAttribute (string localName, string namespaceURI)
                {
-                       if (node.Attributes != null) {
-                               for (int i = 0; i < node.Attributes.Count; i++) {
-                                       XmlAttribute attr = node.Attributes [i];
-                                       if (attr.LocalName == localName
-                                               && attr.NamespaceURI == namespaceURI) {
-                                               node = attr;
-                                               NsNode = null;
-                                               return true;
-                                       }
+                       if (HasAttributes) {
+                               XmlAttribute attr = node.Attributes [localName, namespaceURI];
+                               if (attr != null) {
+                                       node = attr;
+                                       NsNode = null;
+                                       return true;
                                }
                        }
                        return false;
                }
 
+#if NET_2_0
+#else
                public override bool MoveToFirst ()
                {
-                       if (NsNode == null && node.NodeType != XmlNodeType.Attribute && node.ParentNode != null) {
-                               if (!MoveToParent ())
-                                       return false;
-                               // Follow these 2 steps so that we can skip 
-                               // some types of nodes .
-                               MoveToFirstChild ();
-                               return true;
-                       }
-                       return false;
+                       return MoveToFirstImpl ();
                }
+#endif
 
                public override bool MoveToFirstAttribute ()
                {
-                       if (node.Attributes == null)
-                               return false;
                        if (NodeType == XPathNodeType.Element) {
+                               XmlElement el = node as XmlElement;
+                               if (!el.HasAttributes)
+                                       return false;
                                for (int i = 0; i < node.Attributes.Count; i++) {
                                        XmlAttribute attr = node.Attributes [i];
                                        if (attr.NamespaceURI != Xmlns) {
@@ -311,37 +397,10 @@ namespace System.Xml
                public override bool MoveToFirstChild ()
                {
                        if (HasChildren) {
-                               if (node == document) {
-                                       XmlNode n = node.FirstChild;
-                                       if (n == null)
-                                               return false;
-                                       bool loop = true;
-                                       do {
-                                               switch (n.NodeType) {
-                                               case XmlNodeType.XmlDeclaration:
-                                               case XmlNodeType.DocumentType:
-                                                       n = n.NextSibling;
-                                                       if (n == null)
-                                                               return false;
-                                                       break;
-                                               default:
-                                                       loop = false;
-                                                       break;
-                                               }
-                                       } while (loop);
-                                       node = n;
-                               } else {
-                                       XmlNode n2 = null;
-                                       do {
-                                               n2 = node.FirstChild;
-                                               if (node.NodeType != XmlNodeType.EntityReference)
-                                                       break;
-                                               n2 = node.NextSibling;
-                                       } while (n2 != null);
-                                       if (n2 == null)
-                                               return false;
-                                       node = n2;
-                               }
+                               XmlNode n = GetFirstChild (node);
+                               if (n == null)
+                                       return false;
+                               node = n;
                                return true;
                        }
                        return false;
@@ -352,8 +411,8 @@ namespace System.Xml
                        if (NodeType != XPathNodeType.Element)
                                return false;
                        XmlElement el = node as XmlElement;
-                       if (node.Attributes != null) {
-                               do {
+                       do {
+                               if (el.HasAttributes) {
                                        for (int i = 0; i < el.Attributes.Count; i++) {
                                                XmlAttribute attr = el.Attributes [i];
                                                if (attr.NamespaceURI == Xmlns) {
@@ -363,11 +422,11 @@ namespace System.Xml
                                                        return true;
                                                }
                                        }
-                                       if (namespaceScope == XPathNamespaceScope.Local)
-                                               return false;
-                                       el = el.ParentNode as XmlElement;
-                               } while (el != null);
-                       }
+                               }
+                               if (namespaceScope == XPathNamespaceScope.Local)
+                                       return false;
+                               el = GetParentNode (el) as XmlElement;
+                       } while (el != null);
 
                        if (namespaceScope == XPathNamespaceScope.All) {
                                if (CheckNsNameAppearance (nsNodeXml.Name, nsNodeXml.Value))
@@ -400,8 +459,8 @@ namespace System.Xml
                                return false;
 
                        XmlElement el = node as XmlElement;
-                       if (node.Attributes != null) {
-                               do {
+                       do {
+                               if (el.HasAttributes) {
                                        for (int i = 0; i < el.Attributes.Count; i++) {
                                                XmlAttribute attr = el.Attributes [i];
                                                if (attr.NamespaceURI == Xmlns && attr.Name == name) {
@@ -409,9 +468,9 @@ namespace System.Xml
                                                        return true;
                                                }
                                        }
-                                       el = node.ParentNode as XmlElement;
-                               } while (el != null);
-                       }
+                               }
+                               el = GetParentNode (node) as XmlElement;
+                       } while (el != null);
                        return false;
                }
 
@@ -423,12 +482,11 @@ namespace System.Xml
                        XmlNode n = node;
                        if (NodeType == XPathNodeType.Text) {
                                do {
-                                       n = n.NextSibling;
+                                       n = GetNextSibling (n);
                                        if (n == null)
                                                return false;
                                        switch (n.NodeType) {
                                        case XmlNodeType.CDATA:
-                                       case XmlNodeType.EntityReference:
                                        case XmlNodeType.SignificantWhitespace:
                                        case XmlNodeType.Text:
                                        case XmlNodeType.Whitespace:
@@ -438,37 +496,12 @@ namespace System.Xml
                                        }
                                        break;
                                } while (true);
-                       } else {
-                               n = n.NextSibling;
-                               if (n == null)
-                                       return false;
-                       }
-
-                       if (n.ParentNode != null && n.ParentNode.NodeType == XmlNodeType.Document) {
-                               while (n != null) {
-                                       switch (n.NodeType) {
-                                       case XmlNodeType.DocumentType:
-                                       case XmlNodeType.XmlDeclaration:
-                                               n = n.NextSibling;
-                                               continue;
-                                       }
-                                       break;
-                               }
-                               if (n != null)
-                                       node = n;
-                               else
-                                       return false;
-                       } else {
-                               while (n != null) {
-                                       if (n.NodeType != XmlNodeType.EntityReference)
-                                               break;
-                                       n = n.NextSibling;
-                               }
-                               if (n != null)
-                                       node = n;
-                               else
-                                       return false;
                        }
+                       else
+                               n = GetNextSibling (n);
+                       if (n == null)
+                               return false;
+                       node = n;
                        return true;
                }
 
@@ -540,18 +573,20 @@ namespace System.Xml
                        // But if scope is Local, then it returns false here.
                        if (namespaceScope == XPathNamespaceScope.Local)
                                return false;
-                       owner = owner.ParentNode as XmlElement;
+                       owner = GetParentNode (owner) as XmlElement;
                        while (owner != null) {
-                               for (int i = 0; i < owner.Attributes.Count; i++) {
-                                       XmlAttribute attr = owner.Attributes [i];
-                                       if (attr.NamespaceURI == Xmlns) {
-                                               if (CheckNsNameAppearance (attr.Name, attr.Value))
-                                                       continue;
-                                               NsNode = attr;
-                                               return true;
+                               if (owner.HasAttributes) {
+                                       for (int i = 0; i < owner.Attributes.Count; i++) {
+                                               XmlAttribute attr = owner.Attributes [i];
+                                               if (attr.NamespaceURI == Xmlns) {
+                                                       if (CheckNsNameAppearance (attr.Name, attr.Value))
+                                                               continue;
+                                                       NsNode = attr;
+                                                       return true;
+                                               }
                                        }
                                }
-                               owner = owner.ParentNode as XmlElement;
+                               owner = GetParentNode (owner) as XmlElement;
                        }
 
                        if (namespaceScope == XPathNamespaceScope.All) {
@@ -576,12 +611,15 @@ namespace System.Xml
                                        NsNode = null;
                                        return true;
                                }
-                       } else if (node.ParentNode != null) {
-                               node = node.ParentNode;
-                               NsNode = null;
-                               return true;
+                               else
+                                       return false;
                        }
-                       return false;
+                       XmlNode n = GetParentNode (node);
+                       if (n == null)
+                               return false;
+                       node = n;
+                       NsNode = null;
+                       return true;
                }
 
                public override bool MoveToPrevious ()
@@ -589,48 +627,133 @@ namespace System.Xml
                        if (NsNode != null)
                                return false;
 
-                       if (node.PreviousSibling != null) {
-                               if (node.ParentNode != null && node.ParentNode.NodeType == XmlNodeType.Document) {
-                                       XmlNode n = node.PreviousSibling;
-                                       while (n != null) {
-                                               switch (n.NodeType) {
-                                               case XmlNodeType.DocumentType:
-                                               case XmlNodeType.XmlDeclaration:
-                                                       n = n.PreviousSibling;
-                                                       continue;
-                                               }
-                                               break;
-                                       }
-                                       if (n != null)
-                                               node = n;
-                                       else
-                                               return false;
-                               }
-                               else
-                                       node = node.PreviousSibling;
-                               
-                               return true;
-                       }
-                       else
+                       XmlNode p = GetPreviousSibling (node);
+                       if (p == null)
                                return false;
+                       node = p;
+                       return true;
                }
 
                public override void MoveToRoot ()
                {
                        XmlAttribute attr = node as XmlAttribute;
                        XmlNode tmp = attr != null ? attr.OwnerElement : node;
-                       while (tmp.ParentNode != null)
-                               tmp = tmp.ParentNode;
+                       for (XmlNode tmp2 = GetParentNode (tmp); tmp2 != null; tmp2 = GetParentNode (tmp2))
+                               tmp = tmp2;
                        node = tmp;
                        NsNode = null;
                }
 
-               internal XmlNode Node { get { return NsNode != null ? NsNode : node; } }
+               private XmlNode Node { get { return NsNode != null ? NsNode : node; } }
+
+               XmlNode IHasXmlNode.GetNode ()
+               {
+                       return Node;
+               }
+
+               private XmlNode GetFirstChild (XmlNode n)
+               {
+                       if (n.FirstChild == null)
+                               return null;
+                       switch (n.FirstChild.NodeType) {
+                       case XmlNodeType.XmlDeclaration:
+                       case XmlNodeType.DocumentType:
+                               return GetNextSibling (n.FirstChild);
+                       case XmlNodeType.EntityReference:
+                               foreach (XmlNode c in n.ChildNodes) {
+                                       if (c.NodeType == XmlNodeType.EntityReference) {
+                                               XmlNode ec = GetFirstChild (c);
+                                               if (ec != null)
+                                                       return ec;
+                                       }
+                                       return c;
+                               }
+                               return null;
+                       default:
+                               return n.FirstChild;
+                       }
+               }
+
+               private XmlNode GetLastChild (XmlNode n)
+               {
+                       if (n.LastChild == null)
+                               return null;
+                       switch (n.LastChild.NodeType) {
+                       case XmlNodeType.XmlDeclaration:
+                       case XmlNodeType.DocumentType:
+                               return GetPreviousSibling (n.LastChild);
+                       case XmlNodeType.EntityReference:
+                               for (XmlNode c = n.LastChild; c != null; c = c.PreviousSibling) {
+                                       if (c.NodeType == XmlNodeType.EntityReference) {
+                                               XmlNode ec = GetLastChild (c);
+                                               if (ec != null)
+                                                       return ec;
+                                       }
+                                       return c;
+                               }
+                               return null;
+                       default:
+                               return n.LastChild;
+                       }
+               }
+
+               private XmlNode GetPreviousSibling (XmlNode n)
+               {
+                       XmlNode p = n.PreviousSibling;
+                       if (p != null) {
+                               switch (p.NodeType) {
+                               case XmlNodeType.EntityReference:
+                                       XmlNode c = GetLastChild (p);
+                                       if (c != null)
+                                               return c;
+                                       else // empty entity reference etc.
+                                               return GetPreviousSibling (p);
+                               case XmlNodeType.XmlDeclaration:
+                               case XmlNodeType.DocumentType:
+                                       return GetPreviousSibling (p);
+                               default:
+                                       return p;
+                               }
+                       } else {
+                               if (n.ParentNode == null || n.ParentNode.NodeType != XmlNodeType.EntityReference)
+                                       return null;
+                               return GetPreviousSibling (n.ParentNode);
+                       }
+               }
+
+               private XmlNode GetNextSibling (XmlNode n)
+               {
+                       XmlNode nx = n.NextSibling;
+                       if (nx != null) {
+                               switch (nx.NodeType) {
+                               case XmlNodeType.EntityReference:
+                                       XmlNode c = GetFirstChild (nx);
+                                       if (c != null)
+                                               return c;
+                                       else // empty entity reference etc.
+                                               return GetNextSibling (nx);
+                               case XmlNodeType.XmlDeclaration:
+                               case XmlNodeType.DocumentType:
+                                       return GetNextSibling (nx);
+                               default:
+                                       return n.NextSibling;
+                               }
+                       } else {
+                               if (n.ParentNode == null || n.ParentNode.NodeType != XmlNodeType.EntityReference)
+                                       return null;
+                               return GetNextSibling (n.ParentNode);
+                       }
+               }
 
-                XmlNode IHasXmlNode.GetNode ()
-                {
-                        return node;
-                }
+               private XmlNode GetParentNode (XmlNode n)
+               {
+                       if (n.ParentNode == null)
+                               return null;
+                       for (XmlNode p = n.ParentNode; p != null; p = p.ParentNode)
+                               if (p.NodeType != XmlNodeType.EntityReference)
+                                       return p;
+                       return null;
+               }
 
                #endregion
        }