2 // System.Xml.XmlDocumentNavigator
5 // Jason Diamond <jason@injektilo.org>
6 // Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
8 // (C) 2002 Jason Diamond
9 // (C) 2003 Atsushi Enomoto
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Collections;
36 using System.Xml.Schema;
37 using System.Xml.XPath;
41 internal class XmlDocumentNavigator : XPathNavigator, IHasXmlNode
45 internal XmlDocumentNavigator (XmlNode node)
48 if (node.NodeType == XmlNodeType.Attribute && node.NamespaceURI == XmlNamespaceManager.XmlnsXmlns) {
49 nsNode = (XmlAttribute) node;
50 node = nsNode.OwnerElement;
57 private const string Xmlns = "http://www.w3.org/2000/xmlns/";
58 private const string XmlnsXML = "http://www.w3.org/XML/1998/namespace";
61 // Current namespace node (ancestor's attribute of current node).
62 private XmlAttribute nsNode;
63 private ArrayList iteratedNsNames;
68 internal XmlDocument Document {
69 get { return node.NodeType == XmlNodeType.Document ? node as XmlDocument : node.OwnerDocument; }
72 public override string BaseURI {
78 public override bool HasAttributes {
83 XmlElement el = node as XmlElement;
84 if (el == null || !el.HasAttributes)
87 for (int i = 0; i < node.Attributes.Count; i++)
88 if (node.Attributes [i].NamespaceURI != Xmlns)
94 public override bool HasChildren {
99 XPathNodeType nodeType = NodeType;
100 bool canHaveChildren = nodeType == XPathNodeType.Root || nodeType == XPathNodeType.Element;
101 return canHaveChildren && GetFirstChild (node) != null;
105 public override bool IsEmptyElement {
110 return node.NodeType == XmlNodeType.Element
111 && ((XmlElement) node).IsEmpty;
115 public XmlAttribute NsNode {
116 get { return nsNode; }
119 iteratedNsNames = null;
122 if (iteratedNsNames == null)
123 iteratedNsNames = new ArrayList();
126 if (iteratedNsNames.IsReadOnly)
127 iteratedNsNames = new ArrayList(iteratedNsNames);
129 iteratedNsNames.Add (value.Name);
135 public override string LocalName {
137 XmlAttribute nsNode = NsNode;
138 if (nsNode != null) {
139 if (nsNode == Document.NsNodeXml)
142 return (nsNode.Name == "xmlns") ? String.Empty : nsNode.LocalName;
145 XPathNodeType nodeType = NodeType;
147 nodeType == XPathNodeType.Element ||
148 nodeType == XPathNodeType.Attribute ||
149 nodeType == XPathNodeType.ProcessingInstruction ||
150 nodeType == XPathNodeType.Namespace;
151 return canHaveName ? node.LocalName : String.Empty;
155 public override string Name {
160 XPathNodeType nodeType = NodeType;
162 nodeType == XPathNodeType.Element ||
163 nodeType == XPathNodeType.Attribute ||
164 nodeType == XPathNodeType.ProcessingInstruction ||
165 nodeType == XPathNodeType.Namespace;
166 return canHaveName ? node.Name : String.Empty;
170 public override string NamespaceURI {
171 get { return (NsNode != null) ? String.Empty : node.NamespaceURI; }
174 public override XmlNameTable NameTable {
175 get { return Document.NameTable; }
178 public override XPathNodeType NodeType {
181 return XPathNodeType.Namespace;
185 switch (n.NodeType) {
186 case XmlNodeType.SignificantWhitespace:
188 n = GetNextSibling (n);
190 case XmlNodeType.Whitespace:
191 n = GetNextSibling (n);
193 case XmlNodeType.Text:
194 case XmlNodeType.CDATA:
195 return XPathNodeType.Text;
202 XPathNodeType.SignificantWhitespace :
207 public override string Prefix {
208 get { return (NsNode != null) ? String.Empty : node.Prefix; }
212 public override IXmlSchemaInfo SchemaInfo {
213 get { return NsNode != null ? null : node.SchemaInfo; }
216 public override object UnderlyingObject {
221 public override string Value {
224 case XPathNodeType.Attribute:
225 case XPathNodeType.Comment:
226 case XPathNodeType.ProcessingInstruction:
228 case XPathNodeType.Text:
229 case XPathNodeType.Whitespace:
230 case XPathNodeType.SignificantWhitespace:
231 string value = node.Value;
232 for (XmlNode n = GetNextSibling (node); n != null; n = GetNextSibling (n)) {
233 switch (n.XPathNodeType) {
234 case XPathNodeType.Text:
235 case XPathNodeType.Whitespace:
236 case XPathNodeType.SignificantWhitespace:
243 case XPathNodeType.Element:
244 case XPathNodeType.Root:
245 return node.InnerText;
246 case XPathNodeType.Namespace:
247 return NsNode == Document.NsNodeXml ? XmlnsXML : NsNode.Value;
253 public override string XmlLang {
263 private bool CheckNsNameAppearance (string name, string ns)
265 if (iteratedNsNames != null && iteratedNsNames.Contains (name))
267 // default namespace erasure - just add name and never return this node
268 if (ns == String.Empty) {
269 if (iteratedNsNames == null)
270 iteratedNsNames = new ArrayList();
273 if (iteratedNsNames.IsReadOnly)
274 iteratedNsNames = new ArrayList(iteratedNsNames);
276 iteratedNsNames.Add ("xmlns");
283 public override XPathNavigator Clone ()
285 XmlDocumentNavigator clone = new XmlDocumentNavigator (node);
286 clone.nsNode = nsNode;
287 clone.iteratedNsNames = (iteratedNsNames == null || iteratedNsNames.IsReadOnly) ? iteratedNsNames : ArrayList.ReadOnly(iteratedNsNames);
291 public override string GetAttribute (string localName, string namespaceURI)
294 XmlElement el = Node as XmlElement;
295 return el != null ? el.GetAttribute (localName, namespaceURI) : String.Empty;
300 public override string GetNamespace (string name)
302 // MSDN says "String.Empty if a matching namespace
303 // node is not found or if the navigator is not
304 // positioned on an element node", but in fact it
305 // returns actual namespace for the other nodes.
306 return Node.GetNamespaceOfPrefix (name);
309 public override bool IsDescendant (XPathNavigator other)
313 XmlDocumentNavigator o = other as XmlDocumentNavigator;
317 o.node.NodeType == XmlNodeType.Attribute ?
318 ((XmlAttribute) o.node).OwnerElement :
320 for (;n != null; n = n.ParentNode)
326 public override bool IsSamePosition (XPathNavigator other)
328 XmlDocumentNavigator otherDocumentNavigator = other as XmlDocumentNavigator;
329 if (otherDocumentNavigator != null)
330 return node == otherDocumentNavigator.node
331 && NsNode == otherDocumentNavigator.NsNode;
335 public override bool MoveTo (XPathNavigator other)
337 XmlDocumentNavigator otherDocumentNavigator = other as XmlDocumentNavigator;
338 if (otherDocumentNavigator != null) {
339 if (Document == otherDocumentNavigator.Document) {
340 node = otherDocumentNavigator.node;
341 NsNode = otherDocumentNavigator.NsNode;
348 public override bool MoveToAttribute (string localName, string namespaceURI)
351 XmlAttribute attr = node.Attributes [localName, namespaceURI];
363 public override bool MoveToFirst ()
365 return MoveToFirstImpl ();
369 public override bool MoveToFirstAttribute ()
371 if (NodeType == XPathNodeType.Element) {
372 XmlElement el = node as XmlElement;
373 if (!el.HasAttributes)
375 for (int i = 0; i < node.Attributes.Count; i++) {
376 XmlAttribute attr = node.Attributes [i];
377 if (attr.NamespaceURI != Xmlns) {
387 public override bool MoveToFirstChild ()
390 XmlNode n = GetFirstChild (node);
399 public override bool MoveToFirstNamespace (XPathNamespaceScope namespaceScope)
401 if (NodeType != XPathNodeType.Element)
403 XmlElement el = node as XmlElement;
405 if (el.HasAttributes) {
406 for (int i = 0; i < el.Attributes.Count; i++) {
407 XmlAttribute attr = el.Attributes [i];
408 if (attr.NamespaceURI == Xmlns) {
409 if (CheckNsNameAppearance (attr.Name, attr.Value))
416 if (namespaceScope == XPathNamespaceScope.Local)
418 el = GetParentNode (el) as XmlElement;
419 } while (el != null);
421 if (namespaceScope == XPathNamespaceScope.All) {
422 if (CheckNsNameAppearance (Document.NsNodeXml.Name, Document.NsNodeXml.Value))
424 NsNode = Document.NsNodeXml;
431 public override bool MoveToId (string id)
433 XmlElement eltNew = Document.GetElementById (id);
441 public override bool MoveToNamespace (string name)
444 NsNode = Document.NsNodeXml;
448 if (NodeType != XPathNodeType.Element)
451 XmlElement el = node as XmlElement;
453 if (el.HasAttributes) {
454 for (int i = 0; i < el.Attributes.Count; i++) {
455 XmlAttribute attr = el.Attributes [i];
456 if (attr.NamespaceURI == Xmlns && attr.Name == name) {
462 el = GetParentNode (node) as XmlElement;
463 } while (el != null);
467 public override bool MoveToNext ()
473 if (NodeType == XPathNodeType.Text) {
475 n = GetNextSibling (n);
478 switch (n.NodeType) {
479 case XmlNodeType.CDATA:
480 case XmlNodeType.SignificantWhitespace:
481 case XmlNodeType.Text:
482 case XmlNodeType.Whitespace:
491 n = GetNextSibling (n);
498 public override bool MoveToNextAttribute ()
502 if (NodeType != XPathNodeType.Attribute)
505 // Find current attribute.
507 XmlElement owner = ((XmlAttribute) node).OwnerElement;
511 int count = owner.Attributes.Count;
512 for(; pos < count; pos++)
513 if (owner.Attributes [pos] == node)
516 return false; // Where is current attribute? Maybe removed.
518 // Find next attribute.
519 for(pos++; pos < count; pos++) {
520 if (owner.Attributes [pos].NamespaceURI != Xmlns) {
521 node = owner.Attributes [pos];
529 public override bool MoveToNextNamespace (XPathNamespaceScope namespaceScope)
531 if (NsNode == Document.NsNodeXml)
532 // Current namespace is "xml", so there should be no more namespace nodes.
538 // Get current attribute's position.
540 XmlElement owner = ((XmlAttribute) NsNode).OwnerElement;
544 int count = owner.Attributes.Count;
545 for(; pos < count; pos++)
546 if (owner.Attributes [pos] == NsNode)
549 return false; // Where is current attribute? Maybe removed.
551 // Find next namespace from the same element as current ns node.
552 for(pos++; pos < count; pos++) {
553 if (owner.Attributes [pos].NamespaceURI == Xmlns) {
554 XmlAttribute a = owner.Attributes [pos];
555 if (CheckNsNameAppearance (a.Name, a.Value))
562 // If not found more, then find from ancestors.
563 // But if scope is Local, then it returns false here.
564 if (namespaceScope == XPathNamespaceScope.Local)
566 owner = GetParentNode (owner) as XmlElement;
567 while (owner != null) {
568 if (owner.HasAttributes) {
569 for (int i = 0; i < owner.Attributes.Count; i++) {
570 XmlAttribute attr = owner.Attributes [i];
571 if (attr.NamespaceURI == Xmlns) {
572 if (CheckNsNameAppearance (attr.Name, attr.Value))
579 owner = GetParentNode (owner) as XmlElement;
582 if (namespaceScope == XPathNamespaceScope.All) {
583 if (CheckNsNameAppearance (Document.NsNodeXml.Name, Document.NsNodeXml.Value))
585 NsNode = Document.NsNodeXml;
591 public override bool MoveToParent ()
593 if (NsNode != null) {
597 else if (node.NodeType == XmlNodeType.Attribute) {
598 XmlElement ownerElement = ((XmlAttribute)node).OwnerElement;
599 if (ownerElement != null) {
607 XmlNode n = GetParentNode (node);
615 public override bool MoveToPrevious ()
620 XmlNode p = GetPreviousSibling (node);
627 public override void MoveToRoot ()
629 XmlAttribute attr = node as XmlAttribute;
630 XmlNode tmp = attr != null ? attr.OwnerElement : node;
632 return; // i.e. attr has null OwnerElement.
633 for (XmlNode tmp2 = GetParentNode (tmp); tmp2 != null; tmp2 = GetParentNode (tmp2))
639 private XmlNode Node { get { return NsNode != null ? NsNode : node; } }
641 XmlNode IHasXmlNode.GetNode ()
646 private XmlNode GetFirstChild (XmlNode n)
648 if (n.FirstChild == null)
650 switch (n.FirstChild.NodeType) {
651 case XmlNodeType.XmlDeclaration:
652 case XmlNodeType.DocumentType:
653 return GetNextSibling (n.FirstChild);
654 case XmlNodeType.EntityReference:
655 foreach (XmlNode c in n.ChildNodes) {
656 if (c.NodeType == XmlNodeType.EntityReference) {
657 XmlNode ec = GetFirstChild (c);
670 private XmlNode GetLastChild (XmlNode n)
672 if (n.LastChild == null)
674 switch (n.LastChild.NodeType) {
675 case XmlNodeType.XmlDeclaration:
676 case XmlNodeType.DocumentType:
677 return GetPreviousSibling (n.LastChild);
678 case XmlNodeType.EntityReference:
679 for (XmlNode c = n.LastChild; c != null; c = c.PreviousSibling) {
680 if (c.NodeType == XmlNodeType.EntityReference) {
681 XmlNode ec = GetLastChild (c);
694 private XmlNode GetPreviousSibling (XmlNode n)
696 XmlNode p = n.PreviousSibling;
698 switch (p.NodeType) {
699 case XmlNodeType.EntityReference:
700 XmlNode c = GetLastChild (p);
703 else // empty entity reference etc.
704 return GetPreviousSibling (p);
705 case XmlNodeType.XmlDeclaration:
706 case XmlNodeType.DocumentType:
707 return GetPreviousSibling (p);
712 if (n.ParentNode == null || n.ParentNode.NodeType != XmlNodeType.EntityReference)
714 return GetPreviousSibling (n.ParentNode);
718 private XmlNode GetNextSibling (XmlNode n)
720 XmlNode nx = n.NextSibling;
722 switch (nx.NodeType) {
723 case XmlNodeType.EntityReference:
724 XmlNode c = GetFirstChild (nx);
727 else // empty entity reference etc.
728 return GetNextSibling (nx);
729 case XmlNodeType.XmlDeclaration:
730 case XmlNodeType.DocumentType:
731 return GetNextSibling (nx);
733 return n.NextSibling;
736 if (n.ParentNode == null || n.ParentNode.NodeType != XmlNodeType.EntityReference)
738 return GetNextSibling (n.ParentNode);
742 private XmlNode GetParentNode (XmlNode n)
744 if (n.ParentNode == null)
746 for (XmlNode p = n.ParentNode; p != null; p = p.ParentNode)
747 if (p.NodeType != XmlNodeType.EntityReference)
757 override string LookupNamespace (string prefix)
760 return base.LookupNamespace (prefix);
768 override string LookupPrefix (string namespaceUri)
771 return base.LookupPrefix (namespaceUri);
779 override bool MoveToChild (XPathNodeType type)
782 return base.MoveToChild (type);
790 override bool MoveToChild (string localName, string namespaceURI)
793 return base.MoveToChild (localName, namespaceURI);
801 override bool MoveToNext (string localName, string namespaceURI)
804 return base.MoveToNext (localName, namespaceURI);
812 override bool MoveToNext (XPathNodeType type)
815 return base.MoveToNext (type);
823 override bool MoveToFollowing (string localName,
824 string namespaceURI, XPathNavigator end)
827 return base.MoveToFollowing (localName, namespaceURI, end);
835 override bool MoveToFollowing (XPathNodeType type,
839 return base.MoveToFollowing (type, end);