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 if (node.Attributes != null)
92 for (int i = 0; i < node.Attributes.Count; i++)
93 if (node.Attributes [i].NamespaceURI != Xmlns)
99 public override bool HasChildren {
104 XPathNodeType nodeType = NodeType;
105 bool canHaveChildren = nodeType == XPathNodeType.Root || nodeType == XPathNodeType.Element;
106 return canHaveChildren && GetFirstChild (node) != null;
110 public override bool IsEmptyElement {
115 return node.NodeType == XmlNodeType.Element
116 && ((XmlElement) node).IsEmpty;
120 public XmlAttribute NsNode {
121 get { return nsNode; }
124 iteratedNsNames = null;
127 if (iteratedNsNames == null)
128 iteratedNsNames = new ArrayList();
131 if (iteratedNsNames.IsReadOnly)
132 iteratedNsNames = new ArrayList(iteratedNsNames);
134 iteratedNsNames.Add (value.Name);
140 public override string LocalName {
142 XmlAttribute nsNode = NsNode;
143 if (nsNode != null) {
144 if (nsNode == nsNodeXml)
147 return (nsNode.Name == "xmlns") ? String.Empty : nsNode.LocalName;
150 XPathNodeType nodeType = NodeType;
152 nodeType == XPathNodeType.Element ||
153 nodeType == XPathNodeType.Attribute ||
154 nodeType == XPathNodeType.ProcessingInstruction ||
155 nodeType == XPathNodeType.Namespace;
156 return canHaveName ? node.LocalName : String.Empty;
160 public override string Name {
165 XPathNodeType nodeType = NodeType;
167 nodeType == XPathNodeType.Element ||
168 nodeType == XPathNodeType.Attribute ||
169 nodeType == XPathNodeType.ProcessingInstruction ||
170 nodeType == XPathNodeType.Namespace;
171 return canHaveName ? node.Name : String.Empty;
175 public override string NamespaceURI {
176 get { return (NsNode != null) ? String.Empty : node.NamespaceURI; }
179 public override XmlNameTable NameTable {
181 return document.NameTable;
185 public override XPathNodeType NodeType {
188 return XPathNodeType.Namespace;
192 switch (n.NodeType) {
193 case XmlNodeType.SignificantWhitespace:
195 n = GetNextSibling (n);
197 case XmlNodeType.Whitespace:
198 n = GetNextSibling (n);
200 case XmlNodeType.Text:
201 case XmlNodeType.CDATA:
202 return XPathNodeType.Text;
209 XPathNodeType.SignificantWhitespace :
214 public override string Prefix {
215 get { return (NsNode != null) ? String.Empty : node.Prefix; }
219 public override IXmlSchemaInfo SchemaInfo {
220 get { return NsNode != null ? null : node.SchemaInfo; }
223 public override object UnderlyingObject {
228 public override string Value {
231 case XPathNodeType.Attribute:
232 case XPathNodeType.Comment:
233 case XPathNodeType.ProcessingInstruction:
235 case XPathNodeType.Text:
236 case XPathNodeType.Whitespace:
237 case XPathNodeType.SignificantWhitespace:
238 string value = node.Value;
239 for (XmlNode n = GetNextSibling (node); n != null; n = GetNextSibling (n)) {
240 switch (n.XPathNodeType) {
241 case XPathNodeType.Text:
242 case XPathNodeType.Whitespace:
243 case XPathNodeType.SignificantWhitespace:
250 case XPathNodeType.Element:
251 case XPathNodeType.Root:
252 return node.InnerText;
253 case XPathNodeType.Namespace:
254 return NsNode == nsNodeXml ? XmlnsXML : NsNode.Value;
260 public override string XmlLang {
270 private bool CheckNsNameAppearance (string name, string ns)
272 if (iteratedNsNames != null && iteratedNsNames.Contains (name))
274 // default namespace erasure - just add name and never return this node
275 if (ns == String.Empty) {
276 if (iteratedNsNames == null)
277 iteratedNsNames = new ArrayList();
280 if (iteratedNsNames.IsReadOnly)
281 iteratedNsNames = new ArrayList(iteratedNsNames);
283 iteratedNsNames.Add ("xmlns");
290 public override XPathNavigator Clone ()
292 XmlDocumentNavigator clone = new XmlDocumentNavigator (node, nsNodeXml);
293 clone.nsNode = nsNode;
294 clone.iteratedNsNames = (iteratedNsNames == null || iteratedNsNames.IsReadOnly) ? iteratedNsNames : ArrayList.ReadOnly(iteratedNsNames);
298 public override string GetAttribute (string localName, string namespaceURI)
301 XmlElement el = Node as XmlElement;
302 return el != null ? el.GetAttribute (localName, namespaceURI) : String.Empty;
307 public override string GetNamespace (string name)
309 // MSDN says "String.Empty if a matching namespace
310 // node is not found or if the navigator is not
311 // positioned on an element node", but in fact it
312 // returns actual namespace for the other nodes.
313 return Node.GetNamespaceOfPrefix (name);
316 public override bool IsDescendant (XPathNavigator other)
320 XmlDocumentNavigator o = other as XmlDocumentNavigator;
324 o.node.NodeType == XmlNodeType.Attribute ?
325 ((XmlAttribute) o.node).OwnerElement :
327 for (;n != null; n = n.ParentNode)
333 public override bool IsSamePosition (XPathNavigator other)
335 XmlDocumentNavigator otherDocumentNavigator = other as XmlDocumentNavigator;
336 if (otherDocumentNavigator != null)
337 return node == otherDocumentNavigator.node
338 && NsNode == otherDocumentNavigator.NsNode;
342 public override bool MoveTo (XPathNavigator other)
344 XmlDocumentNavigator otherDocumentNavigator = other as XmlDocumentNavigator;
345 if (otherDocumentNavigator != null) {
346 if (document == otherDocumentNavigator.document) {
347 node = otherDocumentNavigator.node;
348 NsNode = otherDocumentNavigator.NsNode;
355 public override bool MoveToAttribute (string localName, string namespaceURI)
357 if (node.Attributes != null) {
358 for (int i = 0; i < node.Attributes.Count; i++) {
359 XmlAttribute attr = node.Attributes [i];
360 if (attr.LocalName == localName
361 && attr.NamespaceURI == namespaceURI) {
373 public override bool MoveToFirst ()
375 return MoveToFirstImpl ();
379 public override bool MoveToFirstAttribute ()
381 if (node.Attributes == null)
383 if (NodeType == XPathNodeType.Element) {
384 for (int i = 0; i < node.Attributes.Count; i++) {
385 XmlAttribute attr = node.Attributes [i];
386 if (attr.NamespaceURI != Xmlns) {
396 public override bool MoveToFirstChild ()
399 XmlNode n = GetFirstChild (node);
408 public override bool MoveToFirstNamespace (XPathNamespaceScope namespaceScope)
410 if (NodeType != XPathNodeType.Element)
412 XmlElement el = node as XmlElement;
413 if (node.Attributes != null) {
415 for (int i = 0; i < el.Attributes.Count; i++) {
416 XmlAttribute attr = el.Attributes [i];
417 if (attr.NamespaceURI == Xmlns) {
418 if (CheckNsNameAppearance (attr.Name, attr.Value))
424 if (namespaceScope == XPathNamespaceScope.Local)
426 el = GetParentNode (el) as XmlElement;
427 } while (el != null);
430 if (namespaceScope == XPathNamespaceScope.All) {
431 if (CheckNsNameAppearance (nsNodeXml.Name, nsNodeXml.Value))
440 public override bool MoveToId (string id)
442 XmlElement eltNew = document.GetElementById (id);
450 public override bool MoveToNamespace (string name)
457 if (NodeType != XPathNodeType.Element)
460 XmlElement el = node as XmlElement;
461 if (node.Attributes != null) {
463 for (int i = 0; i < el.Attributes.Count; i++) {
464 XmlAttribute attr = el.Attributes [i];
465 if (attr.NamespaceURI == Xmlns && attr.Name == name) {
470 el = GetParentNode (node) as XmlElement;
471 } while (el != null);
476 public override bool MoveToNext ()
482 if (NodeType == XPathNodeType.Text) {
484 n = GetNextSibling (n);
487 switch (n.NodeType) {
488 case XmlNodeType.CDATA:
489 case XmlNodeType.SignificantWhitespace:
490 case XmlNodeType.Text:
491 case XmlNodeType.Whitespace:
500 n = GetNextSibling (n);
507 public override bool MoveToNextAttribute ()
511 if (NodeType != XPathNodeType.Attribute)
514 // Find current attribute.
516 XmlElement owner = ((XmlAttribute) node).OwnerElement;
520 int count = owner.Attributes.Count;
521 for(; pos < count; pos++)
522 if (owner.Attributes [pos] == node)
525 return false; // Where is current attribute? Maybe removed.
527 // Find next attribute.
528 for(pos++; pos < count; pos++) {
529 if (owner.Attributes [pos].NamespaceURI != Xmlns) {
530 node = owner.Attributes [pos];
538 public override bool MoveToNextNamespace (XPathNamespaceScope namespaceScope)
540 if (NsNode == nsNodeXml)
541 // Current namespace is "xml", so there should be no more namespace nodes.
547 // Get current attribute's position.
549 XmlElement owner = ((XmlAttribute) NsNode).OwnerElement;
553 int count = owner.Attributes.Count;
554 for(; pos < count; pos++)
555 if (owner.Attributes [pos] == NsNode)
558 return false; // Where is current attribute? Maybe removed.
560 // Find next namespace from the same element as current ns node.
561 for(pos++; pos < count; pos++) {
562 if (owner.Attributes [pos].NamespaceURI == Xmlns) {
563 XmlAttribute a = owner.Attributes [pos];
564 if (CheckNsNameAppearance (a.Name, a.Value))
571 // If not found more, then find from ancestors.
572 // But if scope is Local, then it returns false here.
573 if (namespaceScope == XPathNamespaceScope.Local)
575 owner = GetParentNode (owner) as XmlElement;
576 while (owner != null) {
577 for (int i = 0; i < owner.Attributes.Count; i++) {
578 XmlAttribute attr = owner.Attributes [i];
579 if (attr.NamespaceURI == Xmlns) {
580 if (CheckNsNameAppearance (attr.Name, attr.Value))
586 owner = GetParentNode (owner) as XmlElement;
589 if (namespaceScope == XPathNamespaceScope.All) {
590 if (CheckNsNameAppearance (nsNodeXml.Name, nsNodeXml.Value))
598 public override bool MoveToParent ()
600 if (NsNode != null) {
604 else if (node.NodeType == XmlNodeType.Attribute) {
605 XmlElement ownerElement = ((XmlAttribute)node).OwnerElement;
606 if (ownerElement != null) {
614 XmlNode n = GetParentNode (node);
622 public override bool MoveToPrevious ()
627 XmlNode p = GetPreviousSibling (node);
634 public override void MoveToRoot ()
636 XmlAttribute attr = node as XmlAttribute;
637 XmlNode tmp = attr != null ? attr.OwnerElement : node;
638 for (XmlNode tmp2 = GetParentNode (tmp); tmp2 != null; tmp2 = GetParentNode (tmp2))
644 private XmlNode Node { get { return NsNode != null ? NsNode : node; } }
646 XmlNode IHasXmlNode.GetNode ()
651 private XmlNode GetFirstChild (XmlNode n)
653 if (n.FirstChild == null)
655 switch (n.FirstChild.NodeType) {
656 case XmlNodeType.XmlDeclaration:
657 case XmlNodeType.DocumentType:
658 return GetNextSibling (n.FirstChild);
659 case XmlNodeType.EntityReference:
660 foreach (XmlNode c in n.ChildNodes) {
661 if (c.NodeType == XmlNodeType.EntityReference) {
662 XmlNode ec = GetFirstChild (c);
674 private XmlNode GetLastChild (XmlNode n)
676 if (n.LastChild == null)
678 switch (n.LastChild.NodeType) {
679 case XmlNodeType.XmlDeclaration:
680 case XmlNodeType.DocumentType:
681 return GetPreviousSibling (n.LastChild);
682 case XmlNodeType.EntityReference:
683 for (XmlNode c = n.LastChild; c != null; c = c.PreviousSibling) {
684 if (c.NodeType == XmlNodeType.EntityReference) {
685 XmlNode ec = GetLastChild (c);
697 private XmlNode GetPreviousSibling (XmlNode n)
699 if (n.PreviousSibling != null) {
700 switch (n.PreviousSibling.NodeType) {
701 case XmlNodeType.EntityReference:
702 XmlNode c = GetLastChild (n.PreviousSibling);
705 else // empty entity reference etc.
706 return GetPreviousSibling (n.PreviousSibling);
707 case XmlNodeType.XmlDeclaration:
708 case XmlNodeType.DocumentType:
709 return GetPreviousSibling (n.PreviousSibling);
711 return n.PreviousSibling;
714 if (n.ParentNode == null || n.ParentNode.NodeType != XmlNodeType.EntityReference)
716 return GetPreviousSibling (n.ParentNode);
720 private XmlNode GetNextSibling (XmlNode n)
722 if (n.NextSibling != null) {
723 switch (n.NextSibling.NodeType) {
724 case XmlNodeType.EntityReference:
725 XmlNode c = GetFirstChild (n.NextSibling);
728 else // empty entity reference etc.
729 return GetNextSibling (n.NextSibling);
730 case XmlNodeType.XmlDeclaration:
731 case XmlNodeType.DocumentType:
732 return GetNextSibling (n.NextSibling);
734 return n.NextSibling;
737 if (n.ParentNode == null || n.ParentNode.NodeType != XmlNodeType.EntityReference)
739 return GetNextSibling (n.ParentNode);
743 private XmlNode GetParentNode (XmlNode n)
745 if (n.ParentNode == null)
747 for (XmlNode p = n.ParentNode; p != null; p = p.ParentNode)
748 if (p.NodeType != XmlNodeType.EntityReference)