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 nsNodeXml = document.CreateAttribute ("xmlns", "xml", Xmlns);
49 nsNodeXml.Value = XmlnsXML;
50 if (node.NodeType == XmlNodeType.Attribute && node.NamespaceURI == XmlNamespaceManager.XmlnsXmlns) {
51 nsNode = (XmlAttribute) node;
52 node = nsNode.OwnerElement;
56 private XmlDocumentNavigator (XmlNode node, XmlAttribute nsNodeXml)
59 this.document = node.NodeType == XmlNodeType.Document ?
60 node as XmlDocument : node.OwnerDocument;
61 this.nsNodeXml = nsNodeXml;
67 private const string Xmlns = "http://www.w3.org/2000/xmlns/";
68 private const string XmlnsXML = "http://www.w3.org/XML/1998/namespace";
70 private XmlAttribute nsNodeXml;
72 private XmlDocument document;
73 // Current namespace node (ancestor's attribute of current node).
74 private XmlAttribute nsNode;
75 private ArrayList iteratedNsNames;
80 public override string BaseURI {
86 public override bool HasAttributes {
91 XmlElement el = node as XmlElement;
92 if (el == null || !el.HasAttributes)
95 for (int i = 0; i < node.Attributes.Count; i++)
96 if (node.Attributes [i].NamespaceURI != Xmlns)
102 public override bool HasChildren {
107 XPathNodeType nodeType = NodeType;
108 bool canHaveChildren = nodeType == XPathNodeType.Root || nodeType == XPathNodeType.Element;
109 return canHaveChildren && GetFirstChild (node) != null;
113 public override bool IsEmptyElement {
118 return node.NodeType == XmlNodeType.Element
119 && ((XmlElement) node).IsEmpty;
123 public XmlAttribute NsNode {
124 get { return nsNode; }
127 iteratedNsNames = null;
130 if (iteratedNsNames == null)
131 iteratedNsNames = new ArrayList();
134 if (iteratedNsNames.IsReadOnly)
135 iteratedNsNames = new ArrayList(iteratedNsNames);
137 iteratedNsNames.Add (value.Name);
143 public override string LocalName {
145 XmlAttribute nsNode = NsNode;
146 if (nsNode != null) {
147 if (nsNode == nsNodeXml)
150 return (nsNode.Name == "xmlns") ? String.Empty : nsNode.LocalName;
153 XPathNodeType nodeType = NodeType;
155 nodeType == XPathNodeType.Element ||
156 nodeType == XPathNodeType.Attribute ||
157 nodeType == XPathNodeType.ProcessingInstruction ||
158 nodeType == XPathNodeType.Namespace;
159 return canHaveName ? node.LocalName : String.Empty;
163 public override string Name {
168 XPathNodeType nodeType = NodeType;
170 nodeType == XPathNodeType.Element ||
171 nodeType == XPathNodeType.Attribute ||
172 nodeType == XPathNodeType.ProcessingInstruction ||
173 nodeType == XPathNodeType.Namespace;
174 return canHaveName ? node.Name : String.Empty;
178 public override string NamespaceURI {
179 get { return (NsNode != null) ? String.Empty : node.NamespaceURI; }
182 public override XmlNameTable NameTable {
184 return document.NameTable;
188 public override XPathNodeType NodeType {
191 return XPathNodeType.Namespace;
195 switch (n.NodeType) {
196 case XmlNodeType.SignificantWhitespace:
198 n = GetNextSibling (n);
200 case XmlNodeType.Whitespace:
201 n = GetNextSibling (n);
203 case XmlNodeType.Text:
204 case XmlNodeType.CDATA:
205 return XPathNodeType.Text;
212 XPathNodeType.SignificantWhitespace :
217 public override string Prefix {
218 get { return (NsNode != null) ? String.Empty : node.Prefix; }
222 public override IXmlSchemaInfo SchemaInfo {
223 get { return NsNode != null ? null : node.SchemaInfo; }
226 public override object UnderlyingObject {
231 public override string Value {
234 case XPathNodeType.Attribute:
235 case XPathNodeType.Comment:
236 case XPathNodeType.ProcessingInstruction:
238 case XPathNodeType.Text:
239 case XPathNodeType.Whitespace:
240 case XPathNodeType.SignificantWhitespace:
241 string value = node.Value;
242 for (XmlNode n = GetNextSibling (node); n != null; n = GetNextSibling (n)) {
243 switch (n.XPathNodeType) {
244 case XPathNodeType.Text:
245 case XPathNodeType.Whitespace:
246 case XPathNodeType.SignificantWhitespace:
253 case XPathNodeType.Element:
254 case XPathNodeType.Root:
255 return node.InnerText;
256 case XPathNodeType.Namespace:
257 return NsNode == nsNodeXml ? XmlnsXML : NsNode.Value;
263 public override string XmlLang {
273 private bool CheckNsNameAppearance (string name, string ns)
275 if (iteratedNsNames != null && iteratedNsNames.Contains (name))
277 // default namespace erasure - just add name and never return this node
278 if (ns == String.Empty) {
279 if (iteratedNsNames == null)
280 iteratedNsNames = new ArrayList();
283 if (iteratedNsNames.IsReadOnly)
284 iteratedNsNames = new ArrayList(iteratedNsNames);
286 iteratedNsNames.Add ("xmlns");
293 public override XPathNavigator Clone ()
295 XmlDocumentNavigator clone = new XmlDocumentNavigator (node, nsNodeXml);
296 clone.nsNode = nsNode;
297 clone.iteratedNsNames = (iteratedNsNames == null || iteratedNsNames.IsReadOnly) ? iteratedNsNames : ArrayList.ReadOnly(iteratedNsNames);
301 public override string GetAttribute (string localName, string namespaceURI)
304 XmlElement el = Node as XmlElement;
305 return el != null ? el.GetAttribute (localName, namespaceURI) : String.Empty;
310 public override string GetNamespace (string name)
312 // MSDN says "String.Empty if a matching namespace
313 // node is not found or if the navigator is not
314 // positioned on an element node", but in fact it
315 // returns actual namespace for the other nodes.
316 return Node.GetNamespaceOfPrefix (name);
319 public override bool IsDescendant (XPathNavigator other)
323 XmlDocumentNavigator o = other as XmlDocumentNavigator;
327 o.node.NodeType == XmlNodeType.Attribute ?
328 ((XmlAttribute) o.node).OwnerElement :
330 for (;n != null; n = n.ParentNode)
336 public override bool IsSamePosition (XPathNavigator other)
338 XmlDocumentNavigator otherDocumentNavigator = other as XmlDocumentNavigator;
339 if (otherDocumentNavigator != null)
340 return node == otherDocumentNavigator.node
341 && NsNode == otherDocumentNavigator.NsNode;
345 public override bool MoveTo (XPathNavigator other)
347 XmlDocumentNavigator otherDocumentNavigator = other as XmlDocumentNavigator;
348 if (otherDocumentNavigator != null) {
349 if (document == otherDocumentNavigator.document) {
350 node = otherDocumentNavigator.node;
351 NsNode = otherDocumentNavigator.NsNode;
358 public override bool MoveToAttribute (string localName, string namespaceURI)
361 XmlAttribute attr = node.Attributes [localName, namespaceURI];
373 public override bool MoveToFirst ()
375 return MoveToFirstImpl ();
379 public override bool MoveToFirstAttribute ()
381 if (NodeType == XPathNodeType.Element) {
382 XmlElement el = node as XmlElement;
383 if (!el.HasAttributes)
385 for (int i = 0; i < node.Attributes.Count; i++) {
386 XmlAttribute attr = node.Attributes [i];
387 if (attr.NamespaceURI != Xmlns) {
397 public override bool MoveToFirstChild ()
400 XmlNode n = GetFirstChild (node);
409 public override bool MoveToFirstNamespace (XPathNamespaceScope namespaceScope)
411 if (NodeType != XPathNodeType.Element)
413 XmlElement el = node as XmlElement;
415 if (el.HasAttributes) {
416 for (int i = 0; i < el.Attributes.Count; i++) {
417 XmlAttribute attr = el.Attributes [i];
418 if (attr.NamespaceURI == Xmlns) {
419 if (CheckNsNameAppearance (attr.Name, attr.Value))
426 if (namespaceScope == XPathNamespaceScope.Local)
428 el = GetParentNode (el) as XmlElement;
429 } while (el != null);
431 if (namespaceScope == XPathNamespaceScope.All) {
432 if (CheckNsNameAppearance (nsNodeXml.Name, nsNodeXml.Value))
441 public override bool MoveToId (string id)
443 XmlElement eltNew = document.GetElementById (id);
451 public override bool MoveToNamespace (string name)
458 if (NodeType != XPathNodeType.Element)
461 XmlElement el = node as XmlElement;
463 if (el.HasAttributes) {
464 for (int i = 0; i < el.Attributes.Count; i++) {
465 XmlAttribute attr = el.Attributes [i];
466 if (attr.NamespaceURI == Xmlns && attr.Name == name) {
472 el = GetParentNode (node) as XmlElement;
473 } while (el != null);
477 public override bool MoveToNext ()
483 if (NodeType == XPathNodeType.Text) {
485 n = GetNextSibling (n);
488 switch (n.NodeType) {
489 case XmlNodeType.CDATA:
490 case XmlNodeType.SignificantWhitespace:
491 case XmlNodeType.Text:
492 case XmlNodeType.Whitespace:
501 n = GetNextSibling (n);
508 public override bool MoveToNextAttribute ()
512 if (NodeType != XPathNodeType.Attribute)
515 // Find current attribute.
517 XmlElement owner = ((XmlAttribute) node).OwnerElement;
521 int count = owner.Attributes.Count;
522 for(; pos < count; pos++)
523 if (owner.Attributes [pos] == node)
526 return false; // Where is current attribute? Maybe removed.
528 // Find next attribute.
529 for(pos++; pos < count; pos++) {
530 if (owner.Attributes [pos].NamespaceURI != Xmlns) {
531 node = owner.Attributes [pos];
539 public override bool MoveToNextNamespace (XPathNamespaceScope namespaceScope)
541 if (NsNode == nsNodeXml)
542 // Current namespace is "xml", so there should be no more namespace nodes.
548 // Get current attribute's position.
550 XmlElement owner = ((XmlAttribute) NsNode).OwnerElement;
554 int count = owner.Attributes.Count;
555 for(; pos < count; pos++)
556 if (owner.Attributes [pos] == NsNode)
559 return false; // Where is current attribute? Maybe removed.
561 // Find next namespace from the same element as current ns node.
562 for(pos++; pos < count; pos++) {
563 if (owner.Attributes [pos].NamespaceURI == Xmlns) {
564 XmlAttribute a = owner.Attributes [pos];
565 if (CheckNsNameAppearance (a.Name, a.Value))
572 // If not found more, then find from ancestors.
573 // But if scope is Local, then it returns false here.
574 if (namespaceScope == XPathNamespaceScope.Local)
576 owner = GetParentNode (owner) as XmlElement;
577 while (owner != null) {
578 if (owner.HasAttributes) {
579 for (int i = 0; i < owner.Attributes.Count; i++) {
580 XmlAttribute attr = owner.Attributes [i];
581 if (attr.NamespaceURI == Xmlns) {
582 if (CheckNsNameAppearance (attr.Name, attr.Value))
589 owner = GetParentNode (owner) as XmlElement;
592 if (namespaceScope == XPathNamespaceScope.All) {
593 if (CheckNsNameAppearance (nsNodeXml.Name, nsNodeXml.Value))
601 public override bool MoveToParent ()
603 if (NsNode != null) {
607 else if (node.NodeType == XmlNodeType.Attribute) {
608 XmlElement ownerElement = ((XmlAttribute)node).OwnerElement;
609 if (ownerElement != null) {
617 XmlNode n = GetParentNode (node);
625 public override bool MoveToPrevious ()
630 XmlNode p = GetPreviousSibling (node);
637 public override void MoveToRoot ()
639 XmlAttribute attr = node as XmlAttribute;
640 XmlNode tmp = attr != null ? attr.OwnerElement : node;
641 for (XmlNode tmp2 = GetParentNode (tmp); tmp2 != null; tmp2 = GetParentNode (tmp2))
647 private XmlNode Node { get { return NsNode != null ? NsNode : node; } }
649 XmlNode IHasXmlNode.GetNode ()
654 private XmlNode GetFirstChild (XmlNode n)
656 if (n.FirstChild == null)
658 switch (n.FirstChild.NodeType) {
659 case XmlNodeType.XmlDeclaration:
660 case XmlNodeType.DocumentType:
661 return GetNextSibling (n.FirstChild);
662 case XmlNodeType.EntityReference:
663 foreach (XmlNode c in n.ChildNodes) {
664 if (c.NodeType == XmlNodeType.EntityReference) {
665 XmlNode ec = GetFirstChild (c);
677 private XmlNode GetLastChild (XmlNode n)
679 if (n.LastChild == null)
681 switch (n.LastChild.NodeType) {
682 case XmlNodeType.XmlDeclaration:
683 case XmlNodeType.DocumentType:
684 return GetPreviousSibling (n.LastChild);
685 case XmlNodeType.EntityReference:
686 for (XmlNode c = n.LastChild; c != null; c = c.PreviousSibling) {
687 if (c.NodeType == XmlNodeType.EntityReference) {
688 XmlNode ec = GetLastChild (c);
700 private XmlNode GetPreviousSibling (XmlNode n)
702 XmlNode p = n.PreviousSibling;
704 switch (p.NodeType) {
705 case XmlNodeType.EntityReference:
706 XmlNode c = GetLastChild (p);
709 else // empty entity reference etc.
710 return GetPreviousSibling (p);
711 case XmlNodeType.XmlDeclaration:
712 case XmlNodeType.DocumentType:
713 return GetPreviousSibling (p);
718 if (n.ParentNode == null || n.ParentNode.NodeType != XmlNodeType.EntityReference)
720 return GetPreviousSibling (n.ParentNode);
724 private XmlNode GetNextSibling (XmlNode n)
726 XmlNode nx = n.NextSibling;
728 switch (nx.NodeType) {
729 case XmlNodeType.EntityReference:
730 XmlNode c = GetFirstChild (nx);
733 else // empty entity reference etc.
734 return GetNextSibling (nx);
735 case XmlNodeType.XmlDeclaration:
736 case XmlNodeType.DocumentType:
737 return GetNextSibling (nx);
739 return n.NextSibling;
742 if (n.ParentNode == null || n.ParentNode.NodeType != XmlNodeType.EntityReference)
744 return GetNextSibling (n.ParentNode);
748 private XmlNode GetParentNode (XmlNode n)
750 if (n.ParentNode == null)
752 for (XmlNode p = n.ParentNode; p != null; p = p.ParentNode)
753 if (p.NodeType != XmlNodeType.EntityReference)