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.XPath;
40 internal class XmlDocumentNavigator : XPathNavigator, IHasXmlNode
44 internal XmlDocumentNavigator (XmlNode node)
47 nsNodeXml = document.CreateAttribute ("xmlns", "xml", Xmlns);
48 nsNodeXml.Value = XmlnsXML;
49 if (node.NodeType == XmlNodeType.Attribute && node.NamespaceURI == XmlNamespaceManager.XmlnsXmlns) {
50 nsNode = (XmlAttribute) node;
51 node = nsNode.OwnerElement;
55 private XmlDocumentNavigator (XmlNode node, XmlAttribute nsNodeXml)
58 this.document = node.NodeType == XmlNodeType.Document ?
59 node as XmlDocument : node.OwnerDocument;
60 this.nsNodeXml = nsNodeXml;
66 private const string Xmlns = "http://www.w3.org/2000/xmlns/";
67 private const string XmlnsXML = "http://www.w3.org/XML/1998/namespace";
69 private XmlAttribute nsNodeXml;
71 private XmlDocument document;
72 // Current namespace node (ancestor's attribute of current node).
73 private XmlAttribute nsNode;
74 private ArrayList iteratedNsNames;
79 public override string BaseURI {
85 public override bool HasAttributes {
90 if (node.Attributes != null)
91 for (int i = 0; i < node.Attributes.Count; i++)
92 if (node.Attributes [i].NamespaceURI != Xmlns)
98 public override bool HasChildren {
103 XPathNodeType nodeType = NodeType;
104 bool canHaveChildren = nodeType == XPathNodeType.Root || nodeType == XPathNodeType.Element;
105 return canHaveChildren && GetFirstChild (node) != null;
109 public override bool IsEmptyElement {
114 return node.NodeType == XmlNodeType.Element
115 && ((XmlElement) node).IsEmpty;
119 public XmlAttribute NsNode {
120 get { return nsNode; }
123 iteratedNsNames = null;
126 if (iteratedNsNames == null)
127 iteratedNsNames = new ArrayList();
130 if (iteratedNsNames.IsReadOnly)
131 iteratedNsNames = new ArrayList(iteratedNsNames);
133 iteratedNsNames.Add (value.Name);
139 public override string LocalName {
141 XmlAttribute nsNode = NsNode;
142 if (nsNode != null) {
143 if (nsNode == nsNodeXml)
146 return (nsNode.Name == "xmlns") ? String.Empty : nsNode.LocalName;
149 XPathNodeType nodeType = NodeType;
151 nodeType == XPathNodeType.Element ||
152 nodeType == XPathNodeType.Attribute ||
153 nodeType == XPathNodeType.ProcessingInstruction ||
154 nodeType == XPathNodeType.Namespace;
155 return canHaveName ? node.LocalName : String.Empty;
159 public override string Name {
164 XPathNodeType nodeType = NodeType;
166 nodeType == XPathNodeType.Element ||
167 nodeType == XPathNodeType.Attribute ||
168 nodeType == XPathNodeType.ProcessingInstruction ||
169 nodeType == XPathNodeType.Namespace;
170 return canHaveName ? node.Name : String.Empty;
174 public override string NamespaceURI {
175 get { return (NsNode != null) ? String.Empty : node.NamespaceURI; }
178 public override XmlNameTable NameTable {
180 return document.NameTable;
184 public override XPathNodeType NodeType {
187 return XPathNodeType.Namespace;
191 switch (n.NodeType) {
192 case XmlNodeType.SignificantWhitespace:
194 n = GetNextSibling (n);
196 case XmlNodeType.Whitespace:
197 n = GetNextSibling (n);
199 case XmlNodeType.Text:
200 case XmlNodeType.CDATA:
201 return XPathNodeType.Text;
208 XPathNodeType.SignificantWhitespace :
213 public override string Prefix {
214 get { return (NsNode != null) ? String.Empty : node.Prefix; }
217 public override string Value {
220 case XPathNodeType.Attribute:
221 case XPathNodeType.Comment:
222 case XPathNodeType.ProcessingInstruction:
224 case XPathNodeType.Text:
225 case XPathNodeType.Whitespace:
226 case XPathNodeType.SignificantWhitespace:
227 string value = node.Value;
228 for (XmlNode n = GetNextSibling (node); n != null; n = GetNextSibling (n)) {
229 switch (n.XPathNodeType) {
230 case XPathNodeType.Text:
231 case XPathNodeType.Whitespace:
232 case XPathNodeType.SignificantWhitespace:
239 case XPathNodeType.Element:
240 case XPathNodeType.Root:
241 return node.InnerText;
242 case XPathNodeType.Namespace:
243 return NsNode == nsNodeXml ? XmlnsXML : NsNode.Value;
249 public override string XmlLang {
259 private bool CheckNsNameAppearance (string name, string ns)
261 if (iteratedNsNames != null && iteratedNsNames.Contains (name))
263 // default namespace erasure - just add name and never return this node
264 if (ns == String.Empty) {
265 if (iteratedNsNames == null)
266 iteratedNsNames = new ArrayList();
269 if (iteratedNsNames.IsReadOnly)
270 iteratedNsNames = new ArrayList(iteratedNsNames);
272 iteratedNsNames.Add ("xmlns");
279 public override XPathNavigator Clone ()
281 XmlDocumentNavigator clone = new XmlDocumentNavigator (node, nsNodeXml);
282 clone.nsNode = nsNode;
283 clone.iteratedNsNames = (iteratedNsNames == null || iteratedNsNames.IsReadOnly) ? iteratedNsNames : ArrayList.ReadOnly(iteratedNsNames);
287 public override string GetAttribute (string localName, string namespaceURI)
290 XmlElement el = Node as XmlElement;
291 return el != null ? el.GetAttribute (localName, namespaceURI) : String.Empty;
296 public override string GetNamespace (string name)
298 // MSDN says "String.Empty if a matching namespace
299 // node is not found or if the navigator is not
300 // positioned on an element node", but in fact it
301 // returns actual namespace for the other nodes.
302 return Node.GetNamespaceOfPrefix (name);
305 public override bool IsDescendant (XPathNavigator other)
309 XmlDocumentNavigator o = other as XmlDocumentNavigator;
313 o.node.NodeType == XmlNodeType.Attribute ?
314 ((XmlAttribute) o.node).OwnerElement :
316 for (;n != null; n = n.ParentNode)
322 public override bool IsSamePosition (XPathNavigator other)
324 XmlDocumentNavigator otherDocumentNavigator = other as XmlDocumentNavigator;
325 if (otherDocumentNavigator != null)
326 return node == otherDocumentNavigator.node
327 && NsNode == otherDocumentNavigator.NsNode;
331 public override bool MoveTo (XPathNavigator other)
333 XmlDocumentNavigator otherDocumentNavigator = other as XmlDocumentNavigator;
334 if (otherDocumentNavigator != null) {
335 if (document == otherDocumentNavigator.document) {
336 node = otherDocumentNavigator.node;
337 NsNode = otherDocumentNavigator.NsNode;
344 public override bool MoveToAttribute (string localName, string namespaceURI)
346 if (node.Attributes != null) {
347 for (int i = 0; i < node.Attributes.Count; i++) {
348 XmlAttribute attr = node.Attributes [i];
349 if (attr.LocalName == localName
350 && attr.NamespaceURI == namespaceURI) {
362 public override bool MoveToFirst ()
364 return MoveToFirstImpl ();
368 public override bool MoveToFirstAttribute ()
370 if (node.Attributes == null)
372 if (NodeType == XPathNodeType.Element) {
373 for (int i = 0; i < node.Attributes.Count; i++) {
374 XmlAttribute attr = node.Attributes [i];
375 if (attr.NamespaceURI != Xmlns) {
385 public override bool MoveToFirstChild ()
388 XmlNode n = GetFirstChild (node);
397 public override bool MoveToFirstNamespace (XPathNamespaceScope namespaceScope)
399 if (NodeType != XPathNodeType.Element)
401 XmlElement el = node as XmlElement;
402 if (node.Attributes != null) {
404 for (int i = 0; i < el.Attributes.Count; i++) {
405 XmlAttribute attr = el.Attributes [i];
406 if (attr.NamespaceURI == Xmlns) {
407 if (CheckNsNameAppearance (attr.Name, attr.Value))
413 if (namespaceScope == XPathNamespaceScope.Local)
415 el = GetParentNode (el) as XmlElement;
416 } while (el != null);
419 if (namespaceScope == XPathNamespaceScope.All) {
420 if (CheckNsNameAppearance (nsNodeXml.Name, nsNodeXml.Value))
429 public override bool MoveToId (string id)
431 XmlElement eltNew = document.GetElementById (id);
439 public override bool MoveToNamespace (string name)
446 if (NodeType != XPathNodeType.Element)
449 XmlElement el = node as XmlElement;
450 if (node.Attributes != null) {
452 for (int i = 0; i < el.Attributes.Count; i++) {
453 XmlAttribute attr = el.Attributes [i];
454 if (attr.NamespaceURI == Xmlns && attr.Name == name) {
459 el = GetParentNode (node) as XmlElement;
460 } while (el != null);
465 public override bool MoveToNext ()
471 if (NodeType == XPathNodeType.Text) {
473 n = GetNextSibling (n);
476 switch (n.NodeType) {
477 case XmlNodeType.CDATA:
478 case XmlNodeType.SignificantWhitespace:
479 case XmlNodeType.Text:
480 case XmlNodeType.Whitespace:
489 n = GetNextSibling (n);
496 public override bool MoveToNextAttribute ()
500 if (NodeType != XPathNodeType.Attribute)
503 // Find current attribute.
505 XmlElement owner = ((XmlAttribute) node).OwnerElement;
509 int count = owner.Attributes.Count;
510 for(; pos < count; pos++)
511 if (owner.Attributes [pos] == node)
514 return false; // Where is current attribute? Maybe removed.
516 // Find next attribute.
517 for(pos++; pos < count; pos++) {
518 if (owner.Attributes [pos].NamespaceURI != Xmlns) {
519 node = owner.Attributes [pos];
527 public override bool MoveToNextNamespace (XPathNamespaceScope namespaceScope)
529 if (NsNode == nsNodeXml)
530 // Current namespace is "xml", so there should be no more namespace nodes.
536 // Get current attribute's position.
538 XmlElement owner = ((XmlAttribute) NsNode).OwnerElement;
542 int count = owner.Attributes.Count;
543 for(; pos < count; pos++)
544 if (owner.Attributes [pos] == NsNode)
547 return false; // Where is current attribute? Maybe removed.
549 // Find next namespace from the same element as current ns node.
550 for(pos++; pos < count; pos++) {
551 if (owner.Attributes [pos].NamespaceURI == Xmlns) {
552 XmlAttribute a = owner.Attributes [pos];
553 if (CheckNsNameAppearance (a.Name, a.Value))
560 // If not found more, then find from ancestors.
561 // But if scope is Local, then it returns false here.
562 if (namespaceScope == XPathNamespaceScope.Local)
564 owner = GetParentNode (owner) as XmlElement;
565 while (owner != null) {
566 for (int i = 0; i < owner.Attributes.Count; i++) {
567 XmlAttribute attr = owner.Attributes [i];
568 if (attr.NamespaceURI == Xmlns) {
569 if (CheckNsNameAppearance (attr.Name, attr.Value))
575 owner = GetParentNode (owner) as XmlElement;
578 if (namespaceScope == XPathNamespaceScope.All) {
579 if (CheckNsNameAppearance (nsNodeXml.Name, nsNodeXml.Value))
587 public override bool MoveToParent ()
589 if (NsNode != null) {
593 else if (node.NodeType == XmlNodeType.Attribute) {
594 XmlElement ownerElement = ((XmlAttribute)node).OwnerElement;
595 if (ownerElement != null) {
603 XmlNode n = GetParentNode (node);
611 public override bool MoveToPrevious ()
616 XmlNode p = GetPreviousSibling (node);
623 public override void MoveToRoot ()
625 XmlAttribute attr = node as XmlAttribute;
626 XmlNode tmp = attr != null ? attr.OwnerElement : node;
627 for (XmlNode tmp2 = GetParentNode (tmp); tmp2 != null; tmp2 = GetParentNode (tmp2))
633 private XmlNode Node { get { return NsNode != null ? NsNode : node; } }
635 XmlNode IHasXmlNode.GetNode ()
640 private XmlNode GetFirstChild (XmlNode n)
642 if (n.FirstChild == null)
644 switch (n.FirstChild.NodeType) {
645 case XmlNodeType.XmlDeclaration:
646 case XmlNodeType.DocumentType:
647 return GetNextSibling (n.FirstChild);
648 case XmlNodeType.EntityReference:
649 foreach (XmlNode c in n.ChildNodes) {
650 if (c.NodeType == XmlNodeType.EntityReference) {
651 XmlNode ec = GetFirstChild (c);
663 private XmlNode GetLastChild (XmlNode n)
665 if (n.LastChild == null)
667 switch (n.LastChild.NodeType) {
668 case XmlNodeType.XmlDeclaration:
669 case XmlNodeType.DocumentType:
670 return GetPreviousSibling (n.LastChild);
671 case XmlNodeType.EntityReference:
672 for (XmlNode c = n.LastChild; c != null; c = c.PreviousSibling) {
673 if (c.NodeType == XmlNodeType.EntityReference) {
674 XmlNode ec = GetLastChild (c);
686 private XmlNode GetPreviousSibling (XmlNode n)
688 if (n.PreviousSibling != null) {
689 switch (n.PreviousSibling.NodeType) {
690 case XmlNodeType.EntityReference:
691 XmlNode c = GetLastChild (n.PreviousSibling);
694 else // empty entity reference etc.
695 return GetPreviousSibling (n.PreviousSibling);
696 case XmlNodeType.XmlDeclaration:
697 case XmlNodeType.DocumentType:
698 return GetPreviousSibling (n.PreviousSibling);
700 return n.PreviousSibling;
703 if (n.ParentNode == null || n.ParentNode.NodeType != XmlNodeType.EntityReference)
705 return GetPreviousSibling (n.ParentNode);
709 private XmlNode GetNextSibling (XmlNode n)
711 if (n.NextSibling != null) {
712 switch (n.NextSibling.NodeType) {
713 case XmlNodeType.EntityReference:
714 XmlNode c = GetFirstChild (n.NextSibling);
717 else // empty entity reference etc.
718 return GetNextSibling (n.NextSibling);
719 case XmlNodeType.XmlDeclaration:
720 case XmlNodeType.DocumentType:
721 return GetNextSibling (n.NextSibling);
723 return n.NextSibling;
726 if (n.ParentNode == null || n.ParentNode.NodeType != XmlNodeType.EntityReference)
728 return GetNextSibling (n.ParentNode);
732 private XmlNode GetParentNode (XmlNode n)
734 if (n.ParentNode == null)
736 for (XmlNode p = n.ParentNode; p != null; p = p.ParentNode)
737 if (p.NodeType != XmlNodeType.EntityReference)