2010-04-07 Jb Evain <jbevain@novell.com>
[mcs.git] / class / System.XML / System.Xml / XmlDocumentNavigator.cs
blob885cdd08ec49a4aba8df3d2c22f4e9180cbb28d8
1 //
2 // System.Xml.XmlDocumentNavigator
3 //
4 // Authors:
5 // Jason Diamond <jason@injektilo.org>
6 // Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
7 //
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:
20 //
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 //
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.
33 using System;
34 using System.Collections;
35 using System.Xml;
36 using System.Xml.Schema;
37 using System.Xml.XPath;
39 namespace System.Xml
41 internal class XmlDocumentNavigator : XPathNavigator, IHasXmlNode
43 #region Constructors
45 internal XmlDocumentNavigator (XmlNode node)
47 this.node = node;
48 if (node.NodeType == XmlNodeType.Attribute && node.NamespaceURI == XmlNamespaceManager.XmlnsXmlns) {
49 nsNode = (XmlAttribute) node;
50 node = nsNode.OwnerElement;
54 #endregion
56 #region Fields
57 private const string Xmlns = "http://www.w3.org/2000/xmlns/";
58 private const string XmlnsXML = "http://www.w3.org/XML/1998/namespace";
60 private XmlNode node;
61 // Current namespace node (ancestor's attribute of current node).
62 private XmlAttribute nsNode;
63 private ArrayList iteratedNsNames;
64 #endregion
66 #region Properties
68 internal XmlDocument Document {
69 get { return node.NodeType == XmlNodeType.Document ? node as XmlDocument : node.OwnerDocument; }
72 public override string BaseURI {
73 get {
74 return node.BaseURI;
78 public override bool HasAttributes {
79 get {
80 if (NsNode != null)
81 return false;
83 XmlElement el = node as XmlElement;
84 if (el == null || !el.HasAttributes)
85 return false;
87 for (int i = 0; i < node.Attributes.Count; i++)
88 if (node.Attributes [i].NamespaceURI != Xmlns)
89 return true;
90 return false;
94 public override bool HasChildren {
95 get {
96 if (NsNode != null)
97 return false;
99 XPathNodeType nodeType = NodeType;
100 bool canHaveChildren = nodeType == XPathNodeType.Root || nodeType == XPathNodeType.Element;
101 return canHaveChildren && GetFirstChild (node) != null;
105 public override bool IsEmptyElement {
106 get {
107 if (NsNode != null)
108 return false;
110 return node.NodeType == XmlNodeType.Element
111 && ((XmlElement) node).IsEmpty;
115 public XmlAttribute NsNode {
116 get { return nsNode; }
117 set {
118 if (value == null)
119 iteratedNsNames = null;
120 else
122 if (iteratedNsNames == null)
123 iteratedNsNames = new ArrayList();
124 else
126 if (iteratedNsNames.IsReadOnly)
127 iteratedNsNames = new ArrayList(iteratedNsNames);
129 iteratedNsNames.Add (value.Name);
131 nsNode = value;
135 public override string LocalName {
136 get {
137 XmlAttribute nsNode = NsNode;
138 if (nsNode != null) {
139 if (nsNode == Document.NsNodeXml)
140 return "xml";
141 else
142 return (nsNode.Name == "xmlns") ? String.Empty : nsNode.LocalName;
145 XPathNodeType nodeType = NodeType;
146 bool canHaveName =
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 {
156 get {
157 if (NsNode != null)
158 return LocalName;
160 XPathNodeType nodeType = NodeType;
161 bool canHaveName =
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 {
179 get {
180 if (NsNode != null)
181 return XPathNodeType.Namespace;
182 XmlNode n = node;
183 bool sw = false;
184 do {
185 switch (n.NodeType) {
186 case XmlNodeType.SignificantWhitespace:
187 sw = true;
188 n = GetNextSibling (n);
189 break;
190 case XmlNodeType.Whitespace:
191 n = GetNextSibling (n);
192 break;
193 case XmlNodeType.Text:
194 case XmlNodeType.CDATA:
195 return XPathNodeType.Text;
196 default:
197 n = null;
198 break;
200 } while (n != null);
201 return sw ?
202 XPathNodeType.SignificantWhitespace :
203 node.XPathNodeType;
207 public override string Prefix {
208 get { return (NsNode != null) ? String.Empty : node.Prefix; }
211 #if NET_2_0
212 public override IXmlSchemaInfo SchemaInfo {
213 get { return NsNode != null ? null : node.SchemaInfo; }
216 public override object UnderlyingObject {
217 get { return node; }
219 #endif
221 public override string Value {
222 get {
223 switch (NodeType) {
224 case XPathNodeType.Attribute:
225 case XPathNodeType.Comment:
226 case XPathNodeType.ProcessingInstruction:
227 return node.Value;
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:
237 value += n.Value;
238 continue;
240 break;
242 return value;
243 case XPathNodeType.Element:
244 case XPathNodeType.Root:
245 return node.InnerText;
246 case XPathNodeType.Namespace:
247 return NsNode == Document.NsNodeXml ? XmlnsXML : NsNode.Value;
249 return String.Empty;
253 public override string XmlLang {
254 get {
255 return node.XmlLang;
259 #endregion
261 #region Methods
263 private bool CheckNsNameAppearance (string name, string ns)
265 if (iteratedNsNames != null && iteratedNsNames.Contains (name))
266 return true;
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();
271 else
273 if (iteratedNsNames.IsReadOnly)
274 iteratedNsNames = new ArrayList(iteratedNsNames);
276 iteratedNsNames.Add ("xmlns");
277 return true;
280 return false;
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);
288 return clone;
291 public override string GetAttribute (string localName, string namespaceURI)
293 if (HasAttributes) {
294 XmlElement el = Node as XmlElement;
295 return el != null ? el.GetAttribute (localName, namespaceURI) : String.Empty;
297 return 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)
311 if (NsNode != null)
312 return false;
313 XmlDocumentNavigator o = other as XmlDocumentNavigator;
314 if (o == null)
315 return false;
316 XmlNode n =
317 o.node.NodeType == XmlNodeType.Attribute ?
318 ((XmlAttribute) o.node).OwnerElement :
319 o.node.ParentNode;
320 for (;n != null; n = n.ParentNode)
321 if (n == node)
322 return true;
323 return false;
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;
332 return false;
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;
342 return true;
345 return false;
348 public override bool MoveToAttribute (string localName, string namespaceURI)
350 if (HasAttributes) {
351 XmlAttribute attr = node.Attributes [localName, namespaceURI];
352 if (attr != null) {
353 node = attr;
354 NsNode = null;
355 return true;
358 return false;
361 #if NET_2_0
362 #else
363 public override bool MoveToFirst ()
365 return MoveToFirstImpl ();
367 #endif
369 public override bool MoveToFirstAttribute ()
371 if (NodeType == XPathNodeType.Element) {
372 XmlElement el = node as XmlElement;
373 if (!el.HasAttributes)
374 return false;
375 for (int i = 0; i < node.Attributes.Count; i++) {
376 XmlAttribute attr = node.Attributes [i];
377 if (attr.NamespaceURI != Xmlns) {
378 node = attr;
379 NsNode = null;
380 return true;
384 return false;
387 public override bool MoveToFirstChild ()
389 if (HasChildren) {
390 XmlNode n = GetFirstChild (node);
391 if (n == null)
392 return false;
393 node = n;
394 return true;
396 return false;
399 public override bool MoveToFirstNamespace (XPathNamespaceScope namespaceScope)
401 if (NodeType != XPathNodeType.Element)
402 return false;
403 XmlElement el = node as XmlElement;
404 do {
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))
410 continue;
411 NsNode = attr;
412 return true;
416 if (namespaceScope == XPathNamespaceScope.Local)
417 return false;
418 el = GetParentNode (el) as XmlElement;
419 } while (el != null);
421 if (namespaceScope == XPathNamespaceScope.All) {
422 if (CheckNsNameAppearance (Document.NsNodeXml.Name, Document.NsNodeXml.Value))
423 return false;
424 NsNode = Document.NsNodeXml;
425 return true;
427 else
428 return false;
431 public override bool MoveToId (string id)
433 XmlElement eltNew = Document.GetElementById (id);
434 if (eltNew == null)
435 return false;
437 node = eltNew;
438 return true;
441 public override bool MoveToNamespace (string name)
443 if (name == "xml") {
444 NsNode = Document.NsNodeXml;
445 return true;
448 if (NodeType != XPathNodeType.Element)
449 return false;
451 XmlElement el = node as XmlElement;
452 do {
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) {
457 NsNode = attr;
458 return true;
462 el = GetParentNode (node) as XmlElement;
463 } while (el != null);
464 return false;
467 public override bool MoveToNext ()
469 if (NsNode != null)
470 return false;
472 XmlNode n = node;
473 if (NodeType == XPathNodeType.Text) {
474 do {
475 n = GetNextSibling (n);
476 if (n == null)
477 return false;
478 switch (n.NodeType) {
479 case XmlNodeType.CDATA:
480 case XmlNodeType.SignificantWhitespace:
481 case XmlNodeType.Text:
482 case XmlNodeType.Whitespace:
483 continue;
484 default:
485 break;
487 break;
488 } while (true);
490 else
491 n = GetNextSibling (n);
492 if (n == null)
493 return false;
494 node = n;
495 return true;
498 public override bool MoveToNextAttribute ()
500 if (node == null)
501 return false;
502 if (NodeType != XPathNodeType.Attribute)
503 return false;
505 // Find current attribute.
506 int pos = 0;
507 XmlElement owner = ((XmlAttribute) node).OwnerElement;
508 if (owner == null)
509 return false;
511 int count = owner.Attributes.Count;
512 for(; pos < count; pos++)
513 if (owner.Attributes [pos] == node)
514 break;
515 if (pos == count)
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];
522 NsNode = null;
523 return true;
526 return false;
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.
533 return false;
535 if (NsNode == null)
536 return false;
538 // Get current attribute's position.
539 int pos = 0;
540 XmlElement owner = ((XmlAttribute) NsNode).OwnerElement;
541 if (owner == null)
542 return false;
544 int count = owner.Attributes.Count;
545 for(; pos < count; pos++)
546 if (owner.Attributes [pos] == NsNode)
547 break;
548 if (pos == count)
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))
556 continue;
557 NsNode = a;
558 return true;
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)
565 return false;
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))
573 continue;
574 NsNode = attr;
575 return true;
579 owner = GetParentNode (owner) as XmlElement;
582 if (namespaceScope == XPathNamespaceScope.All) {
583 if (CheckNsNameAppearance (Document.NsNodeXml.Name, Document.NsNodeXml.Value))
584 return false;
585 NsNode = Document.NsNodeXml;
586 return true;
588 return false;
591 public override bool MoveToParent ()
593 if (NsNode != null) {
594 NsNode = null;
595 return true;
597 else if (node.NodeType == XmlNodeType.Attribute) {
598 XmlElement ownerElement = ((XmlAttribute)node).OwnerElement;
599 if (ownerElement != null) {
600 node = ownerElement;
601 NsNode = null;
602 return true;
604 else
605 return false;
607 XmlNode n = GetParentNode (node);
608 if (n == null)
609 return false;
610 node = n;
611 NsNode = null;
612 return true;
615 public override bool MoveToPrevious ()
617 if (NsNode != null)
618 return false;
620 XmlNode p = GetPreviousSibling (node);
621 if (p == null)
622 return false;
623 node = p;
624 return true;
627 public override void MoveToRoot ()
629 XmlAttribute attr = node as XmlAttribute;
630 XmlNode tmp = attr != null ? attr.OwnerElement : node;
631 if (tmp == null)
632 return; // i.e. attr has null OwnerElement.
633 for (XmlNode tmp2 = GetParentNode (tmp); tmp2 != null; tmp2 = GetParentNode (tmp2))
634 tmp = tmp2;
635 node = tmp;
636 NsNode = null;
639 private XmlNode Node { get { return NsNode != null ? NsNode : node; } }
641 XmlNode IHasXmlNode.GetNode ()
643 return Node;
646 private XmlNode GetFirstChild (XmlNode n)
648 if (n.FirstChild == null)
649 return 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);
658 if (ec != null)
659 return ec;
661 else
662 return c;
664 return null;
665 default:
666 return n.FirstChild;
670 private XmlNode GetLastChild (XmlNode n)
672 if (n.LastChild == null)
673 return 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);
682 if (ec != null)
683 return ec;
685 else
686 return c;
688 return null;
689 default:
690 return n.LastChild;
694 private XmlNode GetPreviousSibling (XmlNode n)
696 XmlNode p = n.PreviousSibling;
697 if (p != null) {
698 switch (p.NodeType) {
699 case XmlNodeType.EntityReference:
700 XmlNode c = GetLastChild (p);
701 if (c != null)
702 return c;
703 else // empty entity reference etc.
704 return GetPreviousSibling (p);
705 case XmlNodeType.XmlDeclaration:
706 case XmlNodeType.DocumentType:
707 return GetPreviousSibling (p);
708 default:
709 return p;
711 } else {
712 if (n.ParentNode == null || n.ParentNode.NodeType != XmlNodeType.EntityReference)
713 return null;
714 return GetPreviousSibling (n.ParentNode);
718 private XmlNode GetNextSibling (XmlNode n)
720 XmlNode nx = n.NextSibling;
721 if (nx != null) {
722 switch (nx.NodeType) {
723 case XmlNodeType.EntityReference:
724 XmlNode c = GetFirstChild (nx);
725 if (c != null)
726 return c;
727 else // empty entity reference etc.
728 return GetNextSibling (nx);
729 case XmlNodeType.XmlDeclaration:
730 case XmlNodeType.DocumentType:
731 return GetNextSibling (nx);
732 default:
733 return n.NextSibling;
735 } else {
736 if (n.ParentNode == null || n.ParentNode.NodeType != XmlNodeType.EntityReference)
737 return null;
738 return GetNextSibling (n.ParentNode);
742 private XmlNode GetParentNode (XmlNode n)
744 if (n.ParentNode == null)
745 return null;
746 for (XmlNode p = n.ParentNode; p != null; p = p.ParentNode)
747 if (p.NodeType != XmlNodeType.EntityReference)
748 return p;
749 return null;
752 #if NET_2_0
753 public
754 #else
755 internal
756 #endif
757 override string LookupNamespace (string prefix)
759 // FIXME: optimize
760 return base.LookupNamespace (prefix);
763 #if NET_2_0
764 public
765 #else
766 internal
767 #endif
768 override string LookupPrefix (string namespaceUri)
770 // FIXME: optimize
771 return base.LookupPrefix (namespaceUri);
774 #if NET_2_0
775 public
776 #else
777 internal
778 #endif
779 override bool MoveToChild (XPathNodeType type)
781 // FIXME: optimize
782 return base.MoveToChild (type);
785 #if NET_2_0
786 public
787 #else
788 internal
789 #endif
790 override bool MoveToChild (string localName, string namespaceURI)
792 // FIXME: optimize
793 return base.MoveToChild (localName, namespaceURI);
796 #if NET_2_0
797 public
798 #else
799 internal
800 #endif
801 override bool MoveToNext (string localName, string namespaceURI)
803 // FIXME: optimize
804 return base.MoveToNext (localName, namespaceURI);
807 #if NET_2_0
808 public
809 #else
810 internal
811 #endif
812 override bool MoveToNext (XPathNodeType type)
814 // FIXME: optimize
815 return base.MoveToNext (type);
818 #if NET_2_0
819 public
820 #else
821 internal
822 #endif
823 override bool MoveToFollowing (string localName,
824 string namespaceURI, XPathNavigator end)
826 // FIXME: optimize
827 return base.MoveToFollowing (localName, namespaceURI, end);
830 #if NET_2_0
831 public
832 #else
833 internal
834 #endif
835 override bool MoveToFollowing (XPathNodeType type,
836 XPathNavigator end)
838 // FIXME: optimize
839 return base.MoveToFollowing (type, end);
841 #endregion