//
// 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 ||
public override string Name {
get {
+ if (NsNode != null)
+ return LocalName;
+
XPathNodeType nodeType = NodeType;
bool canHaveName =
nodeType == XPathNodeType.Element ||
}
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;
}
}
#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;
}
{
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;
}
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
+ }
+}