* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / System.XML / System.Xml / XmlDocumentNavigator.cs
index bdd44a165f651deb7b6dbfe6ed5d0e17eee60dcb..15f75ee4eb07b1ce4f97d8f88e539612abba5a57 100644 (file)
 //
 // System.Xml.XmlDocumentNavigator
 //
-// Author:
+// Authors:
 //   Jason Diamond <jason@injektilo.org>
+//   Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
 //
 // (C) 2002 Jason Diamond
+// (C) 2003 Atsushi Enomoto
 //
-\r
-using System;\r
-using System.Collections;\r
-using System.Xml;\r
-using System.Xml.XPath;\r
-\r
-namespace System.Xml\r
-{\r
-       internal class XmlDocumentNavigator : XPathNavigator\r
-       {\r
-               #region Constructors\r
-\r
-               [MonoTODO]
-               internal XmlDocumentNavigator(XmlNode node)\r
-               {\r
-                       this.node = node;\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region Fields\r
-\r
-               private XmlNode node;\r
-               private IEnumerator attributesEnumerator;\r
-\r
-               #endregion\r
+
+//
+// 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
+{
+       internal class XmlDocumentNavigator : XPathNavigator, IHasXmlNode
+       {
+               #region Constructors
+
+               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
+
+               #region Fields
+               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 XmlAttribute nsNode;
+               private ArrayList iteratedNsNames;
+               #endregion
 
                #region Properties
 
-               [MonoTODO]
                public override string BaseURI {
                        get {
-                               throw new NotImplementedException ();
+                               return node.BaseURI;
                        }
                }
 
                public override bool HasAttributes {
                        get {
-                               if (node.Attributes != null)
-                                       foreach (XmlAttribute attribute in node.Attributes)
-                                               if (attribute.NamespaceURI != "http://www.w3.org/2000/xmlns/")
-                                                       return true;
+                               if (NsNode != null)
+                                       return false;
+
+                               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;
                        }
                }
 
                public override bool HasChildren {
                        get {
+                               if (NsNode != null)
+                                       return false;
+
                                XPathNodeType nodeType = NodeType;
                                bool canHaveChildren = nodeType == XPathNodeType.Root || nodeType == XPathNodeType.Element;
-                               return canHaveChildren && node.FirstChild != null;
+                               return canHaveChildren && GetFirstChild (node) != null;
                        }
                }
 
                public override bool IsEmptyElement {
                        get {
-                               return node.NodeType == XmlNodeType.Element && !HasChildren;
+                               if (NsNode != null)
+                                       return false;
+
+                               return node.NodeType == XmlNodeType.Element 
+                                       && ((XmlElement) node).IsEmpty;
+                       }
+               }
+
+               public XmlAttribute NsNode {
+                       get { return nsNode; }
+                       set {
+                               if (value == null)
+                                       iteratedNsNames = null;
+                               else
+                               {
+                                       if (iteratedNsNames == null)
+                                               iteratedNsNames = new ArrayList();
+                                       else
+                                       {
+                                               if (iteratedNsNames.IsReadOnly)
+                                                       iteratedNsNames = new ArrayList(iteratedNsNames);
+                                       }
+                                       iteratedNsNames.Add (value.Name);
+                               }
+                               nsNode = value;
                        }
                }
 
                public override string LocalName {
                        get {
+                               XmlAttribute nsNode = NsNode;
+                               if (nsNode != null) {
+                                       if (nsNode == nsNodeXml)
+                                               return "xml";
+                                       else
+                                               return (nsNode.Name == "xmlns") ? String.Empty : nsNode.LocalName;
+                               }
+
                                XPathNodeType nodeType = NodeType;
                                bool canHaveName = 
                                        nodeType == XPathNodeType.Element || 
@@ -80,6 +162,9 @@ namespace System.Xml
 
                public override string Name {
                        get {
+                               if (NsNode != null)
+                                       return LocalName;
+
                                XPathNodeType nodeType = NodeType;
                                bool canHaveName = 
                                        nodeType == XPathNodeType.Element || 
@@ -91,76 +176,93 @@ namespace System.Xml
                }
 
                public override string NamespaceURI {
-                       get {
-                               return node.NamespaceURI;
-                       }
+                       get { return (NsNode != null) ? String.Empty : node.NamespaceURI; }
                }
 
-               [MonoTODO]
                public override XmlNameTable NameTable {
                        get {
-                               throw new NotImplementedException ();
+                               return document.NameTable;
                        }
                }
 
                public override XPathNodeType NodeType {
                        get {
-                               switch (node.NodeType) {
-                               case XmlNodeType.Document:
-                                       return XPathNodeType.Root;
-                               case XmlNodeType.Element:
-                                       return XPathNodeType.Element;
-                               case XmlNodeType.Attribute:
-                                       return XPathNodeType.Attribute;
-                               case XmlNodeType.Text:
-                                       return XPathNodeType.Text;
-                               case XmlNodeType.Whitespace:
-                                       return XPathNodeType.Whitespace;
-                               case XmlNodeType.SignificantWhitespace:
-                                       return XPathNodeType.SignificantWhitespace;
-                               case XmlNodeType.Comment:
-                                       return XPathNodeType.Comment;
-                               case XmlNodeType.ProcessingInstruction:
-                                       return XPathNodeType.ProcessingInstruction;
-                               }
-                               throw new InvalidOperationException ();
+                               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 node.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) {
                                case XPathNodeType.Attribute:
-                                       return node.Value;
-                               case XPathNodeType.Element:
-                                       return node.InnerText;
                                case XPathNodeType.Comment:
-                                       return node.Value;
                                case XPathNodeType.ProcessingInstruction:
                                        return node.Value;
                                case XPathNodeType.Text:
-                                       return node.Value;
                                case XPathNodeType.Whitespace:
-                                       return node.Value;
                                case XPathNodeType.SignificantWhitespace:
-                                       return node.Value;
+                                       string value = node.Value;
+                                       for (XmlNode n = GetNextSibling (node); n != null; n = GetNextSibling (n)) {
+                                               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 == nsNodeXml ? XmlnsXML : NsNode.Value;
                                }
                                return String.Empty;
                        }
                }
 
-               [MonoTODO]
                public override string XmlLang {
                        get {
-                               throw new NotImplementedException ();
+                               return node.XmlLang;
                        }
                }
 
@@ -168,28 +270,75 @@ namespace System.Xml
 
                #region Methods
 
+               private bool CheckNsNameAppearance (string name, string ns)
+               {
+                       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;
+                       }
+
+                       return false;
+               }
+
                public override XPathNavigator Clone ()
                {
-                       return new XmlDocumentNavigator (node);
+                       XmlDocumentNavigator clone = new XmlDocumentNavigator (node, nsNodeXml);
+                       clone.nsNode = nsNode;
+                       clone.iteratedNsNames = (iteratedNsNames == null || iteratedNsNames.IsReadOnly) ? iteratedNsNames : ArrayList.ReadOnly(iteratedNsNames);
+                       return clone;
                }
 
-               [MonoTODO]
                public override string GetAttribute (string localName, string namespaceURI)
                {
-                       throw new NotImplementedException ();
+                       if (HasAttributes) {
+                               XmlElement el = Node as XmlElement;
+                               return el != null ? el.GetAttribute (localName, namespaceURI) : String.Empty;
+                       }
+                       return String.Empty;
                }
 
-               [MonoTODO]
                public override string GetNamespace (string name)
                {
-                       throw new NotImplementedException ();
+                       // MSDN says "String.Empty if a matching namespace 
+                       // node is not found or if the navigator is not 
+                       // positioned on an element node", but in fact it
+                       // 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;
                        if (otherDocumentNavigator != null)
-                               return node == otherDocumentNavigator.node;
+                               return node == otherDocumentNavigator.node
+                                       && NsNode == otherDocumentNavigator.NsNode;
                        return false;
                }
 
@@ -197,34 +346,50 @@ namespace System.Xml
                {
                        XmlDocumentNavigator otherDocumentNavigator = other as XmlDocumentNavigator;
                        if (otherDocumentNavigator != null) {
-                               if (node.OwnerDocument == otherDocumentNavigator.node.OwnerDocument) {
+                               if (document == otherDocumentNavigator.document) {
                                        node = otherDocumentNavigator.node;
+                                       NsNode = otherDocumentNavigator.NsNode;
                                        return true;
                                }
                        }
                        return false;
                }
 
-               [MonoTODO]
                public override bool MoveToAttribute (string localName, string namespaceURI)
                {
-                       throw new NotImplementedException ();
+                       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 (node.NodeType != XmlNodeType.Attribute && node.ParentNode != null) {
-                               node = node.ParentNode.FirstChild;
-                               return true;
-                       }
-                       return false;
+                       return MoveToFirstImpl ();
                }
+#endif
 
                public override bool MoveToFirstAttribute ()
                {
                        if (NodeType == XPathNodeType.Element) {
-                               attributesEnumerator = node.Attributes.GetEnumerator ();
-                               return MoveToNextAttribute ();
+                               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) {
+                                               node = attr;
+                                               NsNode = null;
+                                               return true;
+                                       }
+                               }
                        }
                        return false;
                }
@@ -232,80 +397,364 @@ namespace System.Xml
                public override bool MoveToFirstChild ()
                {
                        if (HasChildren) {
-                               node = node.FirstChild;
+                               XmlNode n = GetFirstChild (node);
+                               if (n == null)
+                                       return false;
+                               node = n;
                                return true;
                        }
                        return false;
                }
 
-               [MonoTODO]
                public override bool MoveToFirstNamespace (XPathNamespaceScope namespaceScope)
                {
-                       throw new NotImplementedException ();
+                       if (NodeType != XPathNodeType.Element)
+                               return false;
+                       XmlElement el = node as XmlElement;
+                       do {
+                               if (el.HasAttributes) {
+                                       for (int i = 0; i < el.Attributes.Count; i++) {
+                                               XmlAttribute attr = el.Attributes [i];
+                                               if (attr.NamespaceURI == Xmlns) {
+                                                       if (CheckNsNameAppearance (attr.Name, attr.Value))
+                                                               continue;
+                                                       NsNode = attr;
+                                                       return true;
+                                               }
+                                       }
+                               }
+                               if (namespaceScope == XPathNamespaceScope.Local)
+                                       return false;
+                               el = GetParentNode (el) as XmlElement;
+                       } while (el != null);
+
+                       if (namespaceScope == XPathNamespaceScope.All) {
+                               if (CheckNsNameAppearance (nsNodeXml.Name, nsNodeXml.Value))
+                                       return false;
+                               NsNode = nsNodeXml;
+                               return true;
+                       }
+                       else
+                               return false;
                }
 
-               [MonoTODO]
                public override bool MoveToId (string id)
                {
-                       throw new NotImplementedException ();
+                       XmlElement eltNew = document.GetElementById (id);
+                       if (eltNew == null)
+                               return false;
+
+                       node = eltNew;
+                       return true;
                }
 
-               [MonoTODO]
                public override bool MoveToNamespace (string name)
                {
-                       throw new NotImplementedException ();
+                       if (name == "xml") {
+                               NsNode = nsNodeXml;
+                               return true;
+                       }
+
+                       if (NodeType != XPathNodeType.Element)
+                               return false;
+
+                       XmlElement el = node as XmlElement;
+                       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) {
+                                                       NsNode = attr;
+                                                       return true;
+                                               }
+                                       }
+                               }
+                               el = GetParentNode (node) as XmlElement;
+                       } while (el != null);
+                       return false;
                }
 
                public override bool MoveToNext ()
                {
-                       if (node.NextSibling != null) {
-                               node = node.NextSibling;
-                               return true;
+                       if (NsNode != null)
+                               return false;
+
+                       XmlNode n = node;
+                       if (NodeType == XPathNodeType.Text) {
+                               do {
+                                       n = GetNextSibling (n);
+                                       if (n == null)
+                                               return false;
+                                       switch (n.NodeType) {
+                                       case XmlNodeType.CDATA:
+                                       case XmlNodeType.SignificantWhitespace:
+                                       case XmlNodeType.Text:
+                                       case XmlNodeType.Whitespace:
+                                               continue;
+                                       default:
+                                               break;
+                                       }
+                                       break;
+                               } while (true);
                        }
-                       return false;
+                       else
+                               n = GetNextSibling (n);
+                       if (n == null)
+                               return false;
+                       node = n;
+                       return true;
                }
 
                public override bool MoveToNextAttribute ()
                {
-                       if (attributesEnumerator != null && attributesEnumerator.MoveNext ()) {
-                               node = attributesEnumerator.Current as XmlAttribute;
-                               return true;
+                       if (node == null)
+                               return false;
+                       if (NodeType != XPathNodeType.Attribute)
+                               return false;
+
+                       // Find current attribute.
+                       int pos = 0;
+                       XmlElement owner = ((XmlAttribute) node).OwnerElement;
+                       if (owner == null)
+                               return false;
+
+                       int count = owner.Attributes.Count;
+                       for(; pos < count; pos++)
+                               if (owner.Attributes [pos] == node)
+                                       break;
+                       if (pos == count)
+                               return false;   // Where is current attribute? Maybe removed.
+
+                       // Find next attribute.
+                       for(pos++; pos < count; pos++) {
+                               if (owner.Attributes [pos].NamespaceURI != Xmlns) {
+                                       node = owner.Attributes [pos];
+                                       NsNode = null;
+                                       return true;
+                               }
                        }
                        return false;
                }
 
-               [MonoTODO]
                public override bool MoveToNextNamespace (XPathNamespaceScope namespaceScope)
                {
-                       throw new NotImplementedException ();
+                       if (NsNode == nsNodeXml)
+                               // Current namespace is "xml", so there should be no more namespace nodes.
+                               return false;
+
+                       if (NsNode == null)
+                               return false;
+
+                       // Get current attribute's position.
+                       int pos = 0;
+                       XmlElement owner = ((XmlAttribute) NsNode).OwnerElement;
+                       if (owner == null)
+                               return false;
+
+                       int count = owner.Attributes.Count;
+                       for(; pos < count; pos++)
+                               if (owner.Attributes [pos] == NsNode)
+                                       break;
+                       if (pos == count)
+                               return false;   // Where is current attribute? Maybe removed.
+
+                       // Find next namespace from the same element as current ns node.
+                       for(pos++; pos < count; pos++) {
+                               if (owner.Attributes [pos].NamespaceURI == Xmlns) {
+                                       XmlAttribute a = owner.Attributes [pos];
+                                       if (CheckNsNameAppearance (a.Name, a.Value))
+                                               continue;
+                                       NsNode = a;
+                                       return true;
+                               }
+                       }
+
+                       // If not found more, then find from ancestors.
+                       // But if scope is Local, then it returns false here.
+                       if (namespaceScope == XPathNamespaceScope.Local)
+                               return false;
+                       owner = GetParentNode (owner) as XmlElement;
+                       while (owner != null) {
+                               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 = GetParentNode (owner) as XmlElement;
+                       }
+
+                       if (namespaceScope == XPathNamespaceScope.All) {
+                               if (CheckNsNameAppearance (nsNodeXml.Name, nsNodeXml.Value))
+                                       return false;
+                               NsNode = nsNodeXml;
+                               return true;
+                       }
+                       return false;
                }
 
                public override bool MoveToParent ()
                {
-                       if (node.ParentNode != null) {
-                               node = node.ParentNode;
+                       if (NsNode != null) {
+                               NsNode = null;
                                return true;
                        }
-                       return false;
+                       else if (node.NodeType == XmlNodeType.Attribute) {
+                               XmlElement ownerElement = ((XmlAttribute)node).OwnerElement;
+                               if (ownerElement != null) {
+                                       node = ownerElement;
+                                       NsNode = null;
+                                       return true;
+                               }
+                               else
+                                       return false;
+                       }
+                       XmlNode n = GetParentNode (node);
+                       if (n == null)
+                               return false;
+                       node = n;
+                       NsNode = null;
+                       return true;
                }
 
                public override bool MoveToPrevious ()
                {
-                       if (node.PreviousSibling != null) {
-                               node = node.PreviousSibling;
-                               return true;
-                       }
-                       return false;
+                       if (NsNode != null)
+                               return false;
+
+                       XmlNode p = GetPreviousSibling (node);
+                       if (p == null)
+                               return false;
+                       node = p;
+                       return true;
                }
 
                public override void MoveToRoot ()
                {
-                       if (node.NodeType != XmlNodeType.Document)
-                               node = node.OwnerDocument;
+                       XmlAttribute attr = node as XmlAttribute;
+                       XmlNode tmp = attr != null ? attr.OwnerElement : node;
+                       for (XmlNode tmp2 = GetParentNode (tmp); tmp2 != null; tmp2 = GetParentNode (tmp2))
+                               tmp = tmp2;
+                       node = tmp;
+                       NsNode = null;
+               }
+
+               private XmlNode Node { get { return NsNode != null ? NsNode : node; } }
+
+               XmlNode IHasXmlNode.GetNode ()
+               {
+                       return Node;
                }
 
-               internal XmlNode Node { get { 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);
+                       }
+               }
+
+               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
-       }\r
-}\r
+       }
+}