2004-04-05 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / System.Xml / XmlDocumentNavigator.cs
index 14412c2164d1a606260a531b1650ac0e23102d6e..c00339be618c864f4fdb63caa188016738b53112 100644 (file)
@@ -20,11 +20,23 @@ namespace System.Xml
        {
                #region Constructors
 
-               internal XmlDocumentNavigator(XmlNode node)
+               internal XmlDocumentNavigator (XmlNode node)
+                       : this (node, null)
+               {
+                       nsNodeXml = document.CreateAttribute ("xmlns", "xml", Xmlns);
+                       nsNodeXml.Value = XmlnsXML;
+                       if (node.NodeType == XmlNodeType.Attribute && node.NamespaceURI == XmlNamespaceManager.XmlnsXmlns) {
+                               nsNode = (XmlAttribute) node; 
+                               node = nsNode.OwnerElement;
+                       }
+               }
+
+               private XmlDocumentNavigator (XmlNode node, XmlAttribute nsNodeXml)
                {
                        this.node = node;
                        this.document = node.NodeType == XmlNodeType.Document ?
                                node as XmlDocument : node.OwnerDocument;
+                       this.nsNodeXml = nsNodeXml;
                }
 
                #endregion
@@ -33,10 +45,12 @@ namespace System.Xml
                private const string Xmlns = "http://www.w3.org/2000/xmlns/";
                private const string XmlnsXML = "http://www.w3.org/XML/1998/namespace";
 
+               private XmlAttribute nsNodeXml;
                private XmlNode node;
                private XmlDocument document;
                // Current namespace node (ancestor's attribute of current node).
-               private XmlNode nsNode;
+               private XmlAttribute nsNode;
+               private ArrayList iteratedNsNames = new ArrayList ();
 
                #endregion
 
@@ -50,12 +64,12 @@ namespace System.Xml
 
                public override bool HasAttributes {
                        get {
-                               if (nsNode != null)
+                               if (NsNode != null)
                                        return false;
 
                                if (node.Attributes != null)
-                                       foreach (XmlAttribute attribute in node.Attributes)
-                                               if (attribute.NamespaceURI != Xmlns)
+                                       for (int i = 0; i < node.Attributes.Count; i++)
+                                               if (node.Attributes [i].NamespaceURI != Xmlns)
                                                        return true;
                                return false;
                        }
@@ -63,7 +77,7 @@ namespace System.Xml
 
                public override bool HasChildren {
                        get {
-                               if (nsNode != null)
+                               if (NsNode != null)
                                        return false;
 
                                XPathNodeType nodeType = NodeType;
@@ -74,7 +88,7 @@ namespace System.Xml
 
                public override bool IsEmptyElement {
                        get {
-                               if (nsNode != null)
+                               if (NsNode != null)
                                        return false;
 
                                return node.NodeType == XmlNodeType.Element 
@@ -82,10 +96,22 @@ namespace System.Xml
                        }
                }
 
+               public XmlAttribute NsNode {
+                       get { return nsNode; }
+                       set {
+                               if (value == null)
+                                       iteratedNsNames.Clear ();
+                               else
+                                       iteratedNsNames.Add (value.Name);
+                               nsNode = value;
+                       }
+               }
+
                public override string LocalName {
                        get {
+                               XmlAttribute nsNode = NsNode;
                                if (nsNode != null) {
-                                       if (nsNode == document)
+                                       if (nsNode == nsNodeXml)
                                                return "xml";
                                        else
                                                return (nsNode.Name == "xmlns") ? String.Empty : nsNode.LocalName;
@@ -103,7 +129,7 @@ namespace System.Xml
 
                public override string Name {
                        get {
-                               if (nsNode != null)
+                               if (NsNode != null)
                                        return LocalName;
 
                                XPathNodeType nodeType = NodeType;
@@ -117,7 +143,7 @@ namespace System.Xml
                }
 
                public override string NamespaceURI {
-                       get { return (nsNode != null) ? String.Empty : node.NamespaceURI; }
+                       get { return (NsNode != null) ? String.Empty : node.NamespaceURI; }
                }
 
                public override XmlNameTable NameTable {
@@ -127,11 +153,11 @@ namespace System.Xml
                }
 
                public override XPathNodeType NodeType {
-                       get { return (nsNode != null) ? XPathNodeType.Namespace : node.XPathNodeType; }
+                       get { return (NsNode != null) ? XPathNodeType.Namespace : node.XPathNodeType; }
                }
 
                public override string Prefix {
-                       get { return (nsNode != null) ? String.Empty : node.Prefix; }
+                       get { return (NsNode != null) ? String.Empty : node.Prefix; }
                }
 
                public override string Value {
@@ -140,15 +166,27 @@ namespace System.Xml
                                case XPathNodeType.Attribute:
                                case XPathNodeType.Comment:
                                case XPathNodeType.ProcessingInstruction:
+                                       return node.Value;
                                case XPathNodeType.Text:
                                case XPathNodeType.Whitespace:
                                case XPathNodeType.SignificantWhitespace:
-                                       return node.Value;
+                                       string value = node.Value;
+                                       for (XmlNode n = node.NextSibling; n != null; n = n.NextSibling) {
+                                               switch (n.XPathNodeType) {
+                                               case XPathNodeType.Text:
+                                               case XPathNodeType.Whitespace:
+                                               case XPathNodeType.SignificantWhitespace:
+                                                       value += n.Value;
+                                                       continue;
+                                               }
+                                               break;
+                                       }
+                                       return value;
                                case XPathNodeType.Element:
                                case XPathNodeType.Root:
                                        return node.InnerText;
                                case XPathNodeType.Namespace:
-                                       return nsNode == document ? XmlnsXML : nsNode.Value;
+                                       return NsNode == nsNodeXml ? XmlnsXML : NsNode.Value;
                                }
                                return String.Empty;
                        }
@@ -164,10 +202,24 @@ namespace System.Xml
 
                #region Methods
 
+               private bool CheckNsNameAppearance (string name, string ns)
+               {
+                       if (iteratedNsNames.Contains (name))
+                               return true;
+                       // default namespace erasure - just add name and never return this node
+                       if (ns == String.Empty) {
+                               iteratedNsNames.Add ("xmlns");
+                               return true;
+                       }
+
+                       return false;
+               }
+
                public override XPathNavigator Clone ()
                {
-                       XmlDocumentNavigator clone = new XmlDocumentNavigator (node);
+                       XmlDocumentNavigator clone = new XmlDocumentNavigator (node, nsNodeXml);
                        clone.nsNode = nsNode;
+                       clone.iteratedNsNames = (ArrayList) iteratedNsNames.Clone ();
                        return clone;
                }
 
@@ -194,7 +246,7 @@ namespace System.Xml
                        XmlDocumentNavigator otherDocumentNavigator = other as XmlDocumentNavigator;
                        if (otherDocumentNavigator != null)
                                return node == otherDocumentNavigator.node
-                                       && nsNode == otherDocumentNavigator.nsNode;
+                                       && NsNode == otherDocumentNavigator.NsNode;
                        return false;
                }
 
@@ -204,7 +256,7 @@ namespace System.Xml
                        if (otherDocumentNavigator != null) {
                                if (document == otherDocumentNavigator.document) {
                                        node = otherDocumentNavigator.node;
-                                       nsNode = otherDocumentNavigator.nsNode;
+                                       NsNode = otherDocumentNavigator.NsNode;
                                        return true;
                                }
                        }
@@ -214,11 +266,12 @@ namespace System.Xml
                public override bool MoveToAttribute (string localName, string namespaceURI)
                {
                        if (node.Attributes != null) {
-                               foreach (XmlAttribute attr in node.Attributes) {
+                               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;
+                                               NsNode = null;
                                                return true;
                                        }
                                }
@@ -228,8 +281,9 @@ namespace System.Xml
 
                public override bool MoveToFirst ()
                {
-                       if (nsNode == null && node.NodeType != XmlNodeType.Attribute && node.ParentNode != null) {
-                               MoveToParent ();
+                       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 ();
@@ -243,10 +297,11 @@ namespace System.Xml
                        if (node.Attributes == null)
                                return false;
                        if (NodeType == XPathNodeType.Element) {
-                               foreach (XmlAttribute attr in node.Attributes) {
+                               for (int i = 0; i < node.Attributes.Count; i++) {
+                                       XmlAttribute attr = node.Attributes [i];
                                        if (attr.NamespaceURI != Xmlns) {
                                                node = attr;
-                                               nsNode = null;
+                                               NsNode = null;
                                                return true;
                                        }
                                }
@@ -298,9 +353,12 @@ namespace System.Xml
                        XmlElement el = node as XmlElement;
                        if (node.Attributes != null) {
                                do {
-                                       foreach (XmlAttribute attr in el.Attributes) {
+                                       for (int i = 0; i < el.Attributes.Count; i++) {
+                                               XmlAttribute attr = el.Attributes [i];
                                                if (attr.NamespaceURI == Xmlns) {
-                                                       nsNode = attr;
+                                                       if (CheckNsNameAppearance (attr.Name, attr.Value))
+                                                               continue;
+                                                       NsNode = attr;
                                                        return true;
                                                }
                                        }
@@ -311,7 +369,9 @@ namespace System.Xml
                        }
 
                        if (namespaceScope == XPathNamespaceScope.All) {
-                               nsNode = document;
+                               if (CheckNsNameAppearance (nsNodeXml.Name, nsNodeXml.Value))
+                                       return false;
+                               NsNode = nsNodeXml;
                                return true;
                        }
                        else
@@ -331,7 +391,7 @@ namespace System.Xml
                public override bool MoveToNamespace (string name)
                {
                        if (name == "xml") {
-                               nsNode = document;
+                               NsNode = nsNodeXml;
                                return true;
                        }
 
@@ -341,9 +401,10 @@ namespace System.Xml
                        XmlElement el = node as XmlElement;
                        if (node.Attributes != null) {
                                do {
-                                       foreach (XmlAttribute attr in el.Attributes) {
-                                               if (attr.NamespaceURI == Xmlns && Name == name) {
-                                                       nsNode = attr;
+                                       for (int i = 0; i < el.Attributes.Count; i++) {
+                                               XmlAttribute attr = el.Attributes [i];
+                                               if (attr.NamespaceURI == Xmlns && attr.Name == name) {
+                                                       NsNode = attr;
                                                        return true;
                                                }
                                        }
@@ -355,7 +416,7 @@ namespace System.Xml
 
                public override bool MoveToNext ()
                {
-                       if (nsNode != null)
+                       if (NsNode != null)
                                return false;
 
                        if (node.NextSibling != null) {
@@ -415,7 +476,7 @@ namespace System.Xml
                        for(pos++; pos < count; pos++) {
                                if (owner.Attributes [pos].NamespaceURI != Xmlns) {
                                        node = owner.Attributes [pos];
-                                       nsNode = null;
+                                       NsNode = null;
                                        return true;
                                }
                        }
@@ -424,22 +485,22 @@ namespace System.Xml
 
                public override bool MoveToNextNamespace (XPathNamespaceScope namespaceScope)
                {
-                       if (nsNode == document)
+                       if (NsNode == nsNodeXml)
                                // Current namespace is "xml", so there should be no more namespace nodes.
                                return false;
 
-                       if (nsNode == null)
+                       if (NsNode == null)
                                return false;
 
                        // Get current attribute's position.
                        int pos = 0;
-                       XmlElement owner = ((XmlAttribute) nsNode).OwnerElement;
+                       XmlElement owner = ((XmlAttribute) NsNode).OwnerElement;
                        if (owner == null)
                                return false;
 
                        int count = owner.Attributes.Count;
                        for(; pos < count; pos++)
-                               if (owner.Attributes [pos] == nsNode)
+                               if (owner.Attributes [pos] == NsNode)
                                        break;
                        if (pos == count)
                                return false;   // Where is current attribute? Maybe removed.
@@ -447,7 +508,10 @@ namespace System.Xml
                        // Find next namespace from the same element as current ns node.
                        for(pos++; pos < count; pos++) {
                                if (owner.Attributes [pos].NamespaceURI == Xmlns) {
-                                       nsNode = owner.Attributes [pos];
+                                       XmlAttribute a = owner.Attributes [pos];
+                                       if (CheckNsNameAppearance (a.Name, a.Value))
+                                               continue;
+                                       NsNode = a;
                                        return true;
                                }
                        }
@@ -458,9 +522,12 @@ namespace System.Xml
                                return false;
                        owner = owner.ParentNode as XmlElement;
                        while (owner != null) {
-                               foreach (XmlAttribute attr in owner.Attributes) {
+                               for (int i = 0; i < owner.Attributes.Count; i++) {
+                                       XmlAttribute attr = owner.Attributes [i];
                                        if (attr.NamespaceURI == Xmlns) {
-                                               nsNode = attr;
+                                               if (CheckNsNameAppearance (attr.Name, attr.Value))
+                                                       continue;
+                                               NsNode = attr;
                                                return true;
                                        }
                                }
@@ -468,29 +535,30 @@ namespace System.Xml
                        }
 
                        if (namespaceScope == XPathNamespaceScope.All) {
-                               nsNode = document;
+                               if (CheckNsNameAppearance (nsNodeXml.Name, nsNodeXml.Value))
+                                       return false;
+                               NsNode = nsNodeXml;
                                return true;
                        }
-                       else
-                               return false;
+                       return false;
                }
 
                public override bool MoveToParent ()
                {
-                       if (nsNode != null) {
-                               nsNode = null;
+                       if (NsNode != null) {
+                               NsNode = null;
                                return true;
                        }
                        else if (node.NodeType == XmlNodeType.Attribute) {
                                XmlElement ownerElement = ((XmlAttribute)node).OwnerElement;
                                if (ownerElement != null) {
                                        node = ownerElement;
-                                       nsNode = null;
+                                       NsNode = null;
                                        return true;
                                }
                        } else if (node.ParentNode != null) {
                                node = node.ParentNode;
-                               nsNode = null;
+                               NsNode = null;
                                return true;
                        }
                        return false;
@@ -498,7 +566,7 @@ namespace System.Xml
 
                public override bool MoveToPrevious ()
                {
-                       if (nsNode != null)
+                       if (NsNode != null)
                                return false;
 
                        if (node.PreviousSibling != null) {
@@ -534,10 +602,10 @@ namespace System.Xml
                        while (tmp.ParentNode != null)
                                tmp = tmp.ParentNode;
                        node = tmp;
-                       nsNode = null;
+                       NsNode = null;
                }
 
-               internal XmlNode Node { get { return node; } }
+               internal XmlNode Node { get { return NsNode != null ? NsNode : node; } }
 
                 XmlNode IHasXmlNode.GetNode ()
                 {