2009-04-22 Sebastien Pouliot <sebastien@ximian.com>
[mono-project.git] / mcs / class / System.XML / System.Xml.XPath / XPathNavigator.cs
blob981d06a92d66d1ec53768d0de1a320d8a44aed5c
1 //
2 // System.Xml.XPath.XPathNavigator
3 //
4 // Author:
5 // Jason Diamond (jason@injektilo.org)
6 // Atsushi Enomoto (atsushi@ximian.com)
7 //
8 // (C) 2002 Jason Diamond http://injektilo.org/
9 // (C) 2004 Novell Inc.
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 #if NET_2_0
36 using System.Collections.Generic;
37 using System.Diagnostics;
38 using System.Text;
39 #endif
40 using System.IO;
41 using System.Xml;
42 using System.Xml.Schema;
43 using Mono.Xml.XPath;
45 #if NET_2_0
46 using NSResolver = System.Xml.IXmlNamespaceResolver;
47 #else
48 using NSResolver = System.Xml.XmlNamespaceManager;
49 #endif
51 namespace System.Xml.XPath
53 #if NET_2_0
54 public abstract class XPathNavigator : XPathItem,
55 ICloneable, IXPathNavigable, IXmlNamespaceResolver
56 #else
57 public abstract class XPathNavigator : ICloneable
58 #endif
60 class EnumerableIterator : XPathNodeIterator
62 IEnumerable source;
63 IEnumerator e;
64 int pos;
66 public EnumerableIterator (IEnumerable source, int pos)
68 this.source = source;
69 for (int i = 0; i < pos; i++)
70 MoveNext ();
73 public override XPathNodeIterator Clone ()
75 return new EnumerableIterator (source, pos);
78 public override bool MoveNext ()
80 if (e == null)
81 e = source.GetEnumerator ();
82 if (!e.MoveNext ())
83 return false;
84 pos++;
85 return true;
88 public override int CurrentPosition {
89 get { return pos; }
92 public override XPathNavigator Current {
93 get { return pos == 0 ? null : (XPathNavigator) e.Current; }
97 #region Static members
98 #if NET_2_0
99 public static IEqualityComparer NavigatorComparer {
100 get { return XPathNavigatorComparer.Instance; }
102 #endif
103 #endregion
105 #region Constructor
107 protected XPathNavigator ()
111 #endregion
113 #region Properties
115 public abstract string BaseURI { get; }
117 #if NET_2_0
118 public virtual bool CanEdit {
119 get { return false; }
122 public virtual bool HasAttributes {
123 get {
124 if (!MoveToFirstAttribute ())
125 return false;
126 MoveToParent ();
127 return true;
131 public virtual bool HasChildren {
132 get {
133 if (!MoveToFirstChild ())
134 return false;
135 MoveToParent ();
136 return true;
139 #else
140 public abstract bool HasAttributes { get; }
142 public abstract bool HasChildren { get; }
143 #endif
145 public abstract bool IsEmptyElement { get; }
147 public abstract string LocalName { get; }
149 public abstract string Name { get; }
151 public abstract string NamespaceURI { get; }
153 public abstract XmlNameTable NameTable { get; }
155 public abstract XPathNodeType NodeType { get; }
157 public abstract string Prefix { get; }
159 #if NET_2_0
160 public virtual string XmlLang {
161 get {
162 XPathNavigator nav = Clone ();
163 switch (nav.NodeType) {
164 case XPathNodeType.Attribute:
165 case XPathNodeType.Namespace:
166 nav.MoveToParent ();
167 break;
169 do {
170 if (nav.MoveToAttribute ("lang", "http://www.w3.org/XML/1998/namespace"))
171 return nav.Value;
172 } while (nav.MoveToParent ());
173 return String.Empty;
176 #else
177 public abstract string Value { get; }
179 public abstract string XmlLang { get; }
180 #endif
182 #endregion
184 #region Methods
186 public abstract XPathNavigator Clone ();
188 public virtual XmlNodeOrder ComparePosition (XPathNavigator nav)
190 if (IsSamePosition (nav))
191 return XmlNodeOrder.Same;
193 // quick check for direct descendant
194 if (IsDescendant (nav))
195 return XmlNodeOrder.Before;
197 // quick check for direct ancestor
198 if (nav.IsDescendant (this))
199 return XmlNodeOrder.After;
201 XPathNavigator nav1 = Clone ();
202 XPathNavigator nav2 = nav.Clone ();
204 // check if document instance is the same.
205 nav1.MoveToRoot ();
206 nav2.MoveToRoot ();
207 if (!nav1.IsSamePosition (nav2))
208 return XmlNodeOrder.Unknown;
209 nav1.MoveTo (this);
210 nav2.MoveTo (nav);
212 int depth1 = 0;
213 while (nav1.MoveToParent ())
214 depth1++;
215 nav1.MoveTo (this);
216 int depth2 = 0;
217 while (nav2.MoveToParent ())
218 depth2++;
219 nav2.MoveTo (nav);
221 // find common parent depth
222 int common = depth1;
223 for (;common > depth2; common--)
224 nav1.MoveToParent ();
225 for (int i = depth2; i > common; i--)
226 nav2.MoveToParent ();
227 while (!nav1.IsSamePosition (nav2)) {
228 nav1.MoveToParent ();
229 nav2.MoveToParent ();
230 common--;
233 // For each this and target, move to the node that is
234 // ancestor of the node and child of the common parent.
235 nav1.MoveTo (this);
236 for (int i = depth1; i > common + 1; i--)
237 nav1.MoveToParent ();
238 nav2.MoveTo (nav);
239 for (int i = depth2; i > common + 1; i--)
240 nav2.MoveToParent ();
242 // Those children of common parent are comparable.
243 // namespace nodes precede to attributes, and they
244 // precede to other nodes.
245 if (nav1.NodeType == XPathNodeType.Namespace) {
246 if (nav2.NodeType != XPathNodeType.Namespace)
247 return XmlNodeOrder.Before;
248 while (nav1.MoveToNextNamespace ())
249 if (nav1.IsSamePosition (nav2))
250 return XmlNodeOrder.Before;
251 return XmlNodeOrder.After;
253 if (nav2.NodeType == XPathNodeType.Namespace)
254 return XmlNodeOrder.After;
255 if (nav1.NodeType == XPathNodeType.Attribute) {
256 if (nav2.NodeType != XPathNodeType.Attribute)
257 return XmlNodeOrder.Before;
258 while (nav1.MoveToNextAttribute ())
259 if (nav1.IsSamePosition (nav2))
260 return XmlNodeOrder.Before;
261 return XmlNodeOrder.After;
263 while (nav1.MoveToNext ())
264 if (nav1.IsSamePosition (nav2))
265 return XmlNodeOrder.Before;
266 return XmlNodeOrder.After;
269 public virtual XPathExpression Compile (string xpath)
271 return XPathExpression.Compile (xpath);
274 internal virtual XPathExpression Compile (string xpath, System.Xml.Xsl.IStaticXsltContext ctx)
276 return XPathExpression.Compile (xpath, null, ctx);
279 public virtual object Evaluate (string xpath)
281 return Evaluate (Compile (xpath));
284 public virtual object Evaluate (XPathExpression expr)
286 return Evaluate (expr, null);
289 public virtual object Evaluate (XPathExpression expr, XPathNodeIterator context)
291 return Evaluate (expr, context, null);
294 BaseIterator ToBaseIterator (XPathNodeIterator iter, NSResolver ctx)
296 BaseIterator i = iter as BaseIterator;
297 if (i == null)
298 i = new WrapperIterator (iter, ctx);
299 return i;
302 object Evaluate (XPathExpression expr, XPathNodeIterator context, NSResolver ctx)
304 CompiledExpression cexpr = (CompiledExpression) expr;
305 if (ctx == null)
306 ctx = cexpr.NamespaceManager;
308 if (context == null)
309 context = new NullIterator (this, ctx);
310 BaseIterator iterContext = ToBaseIterator (context, ctx);
311 iterContext.NamespaceManager = ctx;
312 return cexpr.Evaluate (iterContext);
315 internal XPathNodeIterator EvaluateNodeSet (XPathExpression expr, XPathNodeIterator context, NSResolver ctx)
317 CompiledExpression cexpr = (CompiledExpression) expr;
318 if (ctx == null)
319 ctx = cexpr.NamespaceManager;
321 if (context == null)
322 context = new NullIterator (this, cexpr.NamespaceManager);
323 BaseIterator iterContext = ToBaseIterator (context, ctx);
324 iterContext.NamespaceManager = ctx;
325 return cexpr.EvaluateNodeSet (iterContext);
328 internal string EvaluateString (XPathExpression expr, XPathNodeIterator context, NSResolver ctx)
330 CompiledExpression cexpr = (CompiledExpression) expr;
331 if (ctx == null)
332 ctx = cexpr.NamespaceManager;
334 if (context == null)
335 context = new NullIterator (this, cexpr.NamespaceManager);
336 BaseIterator iterContext = ToBaseIterator (context, ctx);
337 return cexpr.EvaluateString (iterContext);
340 internal double EvaluateNumber (XPathExpression expr, XPathNodeIterator context, NSResolver ctx)
342 CompiledExpression cexpr = (CompiledExpression) expr;
343 if (ctx == null)
344 ctx = cexpr.NamespaceManager;
346 if (context == null)
347 context = new NullIterator (this, cexpr.NamespaceManager);
348 BaseIterator iterContext = ToBaseIterator (context, ctx);
349 iterContext.NamespaceManager = ctx;
350 return cexpr.EvaluateNumber (iterContext);
353 internal bool EvaluateBoolean (XPathExpression expr, XPathNodeIterator context, NSResolver ctx)
355 CompiledExpression cexpr = (CompiledExpression) expr;
356 if (ctx == null)
357 ctx = cexpr.NamespaceManager;
359 if (context == null)
360 context = new NullIterator (this, cexpr.NamespaceManager);
361 BaseIterator iterContext = ToBaseIterator (context, ctx);
362 iterContext.NamespaceManager = ctx;
363 return cexpr.EvaluateBoolean (iterContext);
366 #if NET_2_0
367 public virtual string GetAttribute (string localName, string namespaceURI)
369 if (!MoveToAttribute (localName, namespaceURI))
370 return String.Empty;
371 string value = Value;
372 MoveToParent ();
373 return value;
376 public virtual string GetNamespace (string name)
378 if (!MoveToNamespace (name))
379 return String.Empty;
380 string value = Value;
381 MoveToParent ();
382 return value;
385 #else
386 public abstract string GetAttribute (string localName, string namespaceURI);
388 public abstract string GetNamespace (string name);
389 #endif
391 object ICloneable.Clone ()
393 return Clone ();
396 public virtual bool IsDescendant (XPathNavigator nav)
398 if (nav != null)
400 nav = nav.Clone ();
401 while (nav.MoveToParent ())
403 if (IsSamePosition (nav))
404 return true;
407 return false;
410 public abstract bool IsSamePosition (XPathNavigator other);
412 public virtual bool Matches (string xpath)
414 return Matches (Compile (xpath));
417 public virtual bool Matches (XPathExpression expr)
419 Expression e = ((CompiledExpression) expr).ExpressionNode;
420 if (e is ExprRoot)
421 return NodeType == XPathNodeType.Root;
423 NodeTest nt = e as NodeTest;
424 if (nt != null) {
425 switch (nt.Axis.Axis) {
426 case Axes.Child:
427 case Axes.Attribute:
428 break;
429 default:
430 throw new XPathException ("Only child and attribute pattern are allowed for a pattern.");
432 return nt.Match (((CompiledExpression)expr).NamespaceManager, this);
434 if (e is ExprFilter) {
435 do {
436 e = ((ExprFilter) e).LeftHandSide;
437 } while (e is ExprFilter);
439 if (e is NodeTest && !((NodeTest) e).Match (((CompiledExpression) expr).NamespaceManager, this))
440 return false;
443 XPathResultType resultType = e.ReturnType;
444 switch (resultType) {
445 case XPathResultType.Any:
446 case XPathResultType.NodeSet:
447 break;
448 default:
449 return false;
452 switch (e.EvaluatedNodeType) {
453 case XPathNodeType.Attribute:
454 case XPathNodeType.Namespace:
455 if (NodeType != e.EvaluatedNodeType)
456 return false;
457 break;
460 XPathNodeIterator nodes;
461 nodes = this.Select (expr);
462 while (nodes.MoveNext ()) {
463 if (IsSamePosition (nodes.Current))
464 return true;
467 // ancestors might select this node.
469 XPathNavigator navigator = Clone ();
471 while (navigator.MoveToParent ()) {
472 nodes = navigator.Select (expr);
474 while (nodes.MoveNext ()) {
475 if (IsSamePosition (nodes.Current))
476 return true;
480 return false;
483 public abstract bool MoveTo (XPathNavigator other);
485 #if NET_2_0
486 public virtual bool MoveToAttribute (string localName, string namespaceURI)
488 if (MoveToFirstAttribute ()) {
489 do {
490 if (LocalName == localName && NamespaceURI == namespaceURI)
491 return true;
492 } while (MoveToNextAttribute ());
493 MoveToParent ();
495 return false;
498 public virtual bool MoveToNamespace (string name)
500 if (MoveToFirstNamespace ()) {
501 do {
502 if (LocalName == name)
503 return true;
504 } while (MoveToNextNamespace ());
505 MoveToParent ();
507 return false;
511 public virtual bool MoveToFirst ()
513 if (MoveToPrevious ()) {
514 // It would be able to invoke MoveToPrevious() until the end, but this way would be much faster
515 MoveToParent ();
516 MoveToFirstChild ();
517 return true;
519 return false;
523 public virtual bool MoveToFirst ()
525 return MoveToFirstImpl ();
528 public virtual void MoveToRoot ()
530 while (MoveToParent ())
533 #else
534 public abstract bool MoveToAttribute (string localName, string namespaceURI);
536 public abstract bool MoveToNamespace (string name);
538 public abstract bool MoveToFirst ();
540 public abstract void MoveToRoot ();
541 #endif
543 internal bool MoveToFirstImpl ()
545 switch (NodeType) {
546 case XPathNodeType.Attribute:
547 case XPathNodeType.Namespace:
548 return false;
549 default:
550 if (!MoveToParent ())
551 return false;
552 // Follow these 2 steps so that we can skip
553 // some types of nodes .
554 MoveToFirstChild ();
555 return true;
559 public abstract bool MoveToFirstAttribute ();
561 public abstract bool MoveToFirstChild ();
563 public bool MoveToFirstNamespace ()
565 return MoveToFirstNamespace (XPathNamespaceScope.All);
568 public abstract bool MoveToFirstNamespace (XPathNamespaceScope namespaceScope);
570 public abstract bool MoveToId (string id);
572 public abstract bool MoveToNext ();
574 public abstract bool MoveToNextAttribute ();
576 public bool MoveToNextNamespace ()
578 return MoveToNextNamespace (XPathNamespaceScope.All);
581 public abstract bool MoveToNextNamespace (XPathNamespaceScope namespaceScope);
583 public abstract bool MoveToParent ();
585 public abstract bool MoveToPrevious ();
587 public virtual XPathNodeIterator Select (string xpath)
589 return Select (Compile (xpath));
592 public virtual XPathNodeIterator Select (XPathExpression expr)
594 return Select (expr, null);
597 internal XPathNodeIterator Select (XPathExpression expr, NSResolver ctx)
599 CompiledExpression cexpr = (CompiledExpression) expr;
600 if (ctx == null)
601 ctx = cexpr.NamespaceManager;
603 BaseIterator iter = new NullIterator (this, ctx);
604 return cexpr.EvaluateNodeSet (iter);
607 public virtual XPathNodeIterator SelectAncestors (XPathNodeType type, bool matchSelf)
609 Axes axis = (matchSelf) ? Axes.AncestorOrSelf : Axes.Ancestor;
610 return SelectTest (new NodeTypeTest (axis, type));
613 public virtual XPathNodeIterator SelectAncestors (string name, string namespaceURI, bool matchSelf)
615 if (name == null)
616 throw new ArgumentNullException ("name");
617 if (namespaceURI == null)
618 throw new ArgumentNullException ("namespaceURI");
620 Axes axis = (matchSelf) ? Axes.AncestorOrSelf : Axes.Ancestor;
621 XmlQualifiedName qname = new XmlQualifiedName (name, namespaceURI);
622 return SelectTest (new NodeNameTest (axis, qname, true));
625 static IEnumerable EnumerateChildren (XPathNavigator n, XPathNodeType type)
627 if (!n.MoveToFirstChild ())
628 yield break;
629 n.MoveToParent ();
630 XPathNavigator nav = n.Clone ();
631 nav.MoveToFirstChild ();
632 XPathNavigator nav2 = null;
633 do {
634 if (type == XPathNodeType.All || nav.NodeType == type) {
635 if (nav2 == null)
636 nav2 = nav.Clone ();
637 else
638 nav2.MoveTo (nav);
639 yield return nav2;
641 } while (nav.MoveToNext ());
644 public virtual XPathNodeIterator SelectChildren (XPathNodeType type)
646 #if false
647 return SelectTest (new NodeTypeTest (Axes.Child, type));
648 #else
649 return new WrapperIterator (new EnumerableIterator (EnumerateChildren (this, type), 0), null);
650 // FIXME: make it work i.e. remove dependency on BaseIterator
651 // return new EnumerableIterator (EnumerateChildren (this, type), 0);
652 #endif
655 static IEnumerable EnumerateChildren (XPathNavigator n, string name, string ns)
657 if (!n.MoveToFirstChild ())
658 yield break;
659 n.MoveToParent ();
660 XPathNavigator nav = n.Clone ();
661 nav.MoveToFirstChild ();
662 XPathNavigator nav2 = nav.Clone ();
663 do {
664 if (nav.LocalName == name && nav.NamespaceURI == ns) {
665 nav2.MoveTo (nav);
666 yield return nav2;
668 } while (nav.MoveToNext ());
671 public virtual XPathNodeIterator SelectChildren (string name, string namespaceURI)
673 if (name == null)
674 throw new ArgumentNullException ("name");
675 if (namespaceURI == null)
676 throw new ArgumentNullException ("namespaceURI");
678 #if false
679 Axes axis = Axes.Child;
680 XmlQualifiedName qname = new XmlQualifiedName (name, namespaceURI);
681 return SelectTest (new NodeNameTest (axis, qname, true));
682 #else
683 return new WrapperIterator (new EnumerableIterator (EnumerateChildren (this, name, namespaceURI), 0), null);
684 #endif
687 public virtual XPathNodeIterator SelectDescendants (XPathNodeType type, bool matchSelf)
689 Axes axis = (matchSelf) ? Axes.DescendantOrSelf : Axes.Descendant;
690 return SelectTest (new NodeTypeTest (axis, type));
693 public virtual XPathNodeIterator SelectDescendants (string name, string namespaceURI, bool matchSelf)
695 if (name == null)
696 throw new ArgumentNullException ("name");
697 if (namespaceURI == null)
698 throw new ArgumentNullException ("namespaceURI");
701 Axes axis = (matchSelf) ? Axes.DescendantOrSelf : Axes.Descendant;
702 XmlQualifiedName qname = new XmlQualifiedName (name, namespaceURI);
703 return SelectTest (new NodeNameTest (axis, qname, true));
706 internal XPathNodeIterator SelectTest (NodeTest test)
708 return test.EvaluateNodeSet (new NullIterator (this));
711 public override string ToString ()
713 return Value;
716 #endregion
718 #if NET_2_0
720 public virtual bool CheckValidity (XmlSchemaSet schemas, ValidationEventHandler handler)
722 XmlReaderSettings settings = new XmlReaderSettings ();
723 settings.NameTable = NameTable;
724 settings.SetSchemas (schemas);
725 settings.ValidationEventHandler += handler;
726 settings.ValidationType = ValidationType.Schema;
727 try {
728 XmlReader r = XmlReader.Create (
729 ReadSubtree (), settings);
730 while (!r.EOF)
731 r.Read ();
732 } catch (XmlSchemaValidationException) {
733 return false;
735 return true;
738 public virtual XPathNavigator CreateNavigator ()
740 return Clone ();
743 public virtual object Evaluate (string xpath, IXmlNamespaceResolver nsResolver)
745 return Evaluate (Compile (xpath), null, nsResolver);
748 public virtual IDictionary<string, string> GetNamespacesInScope (XmlNamespaceScope scope)
750 IDictionary<string, string> table = new Dictionary<string, string> ();
751 XPathNamespaceScope xpscope =
752 scope == XmlNamespaceScope.Local ?
753 XPathNamespaceScope.Local :
754 scope == XmlNamespaceScope.ExcludeXml ?
755 XPathNamespaceScope.ExcludeXml :
756 XPathNamespaceScope.All;
757 XPathNavigator nav = Clone ();
758 if (nav.NodeType != XPathNodeType.Element)
759 nav.MoveToParent ();
760 if (!nav.MoveToFirstNamespace (xpscope))
761 return table;
762 do {
763 table.Add (nav.Name, nav.Value);
764 } while (nav.MoveToNextNamespace (xpscope));
765 return table;
767 #endif
769 #if NET_2_0
770 public
771 #else
772 internal
773 #endif
774 virtual string LookupNamespace (string prefix)
776 XPathNavigator nav = Clone ();
777 if (nav.NodeType != XPathNodeType.Element)
778 nav.MoveToParent ();
779 if (nav.MoveToNamespace (prefix))
780 return nav.Value;
781 return null;
784 #if NET_2_0
785 public
786 #else
787 internal
788 #endif
789 virtual string LookupPrefix (string namespaceUri)
791 XPathNavigator nav = Clone ();
792 if (nav.NodeType != XPathNodeType.Element)
793 nav.MoveToParent ();
794 if (!nav.MoveToFirstNamespace ())
795 return null;
796 do {
797 if (nav.Value == namespaceUri)
798 return nav.Name;
799 } while (nav.MoveToNextNamespace ());
800 return null;
803 private bool MoveTo (XPathNodeIterator iter)
805 if (iter.MoveNext ()) {
806 MoveTo (iter.Current);
807 return true;
809 else
810 return false;
813 #if NET_2_0
814 public
815 #else
816 internal
817 #endif
818 virtual bool MoveToChild (XPathNodeType type)
820 return MoveTo (SelectChildren (type));
823 #if NET_2_0
824 public
825 #else
826 internal
827 #endif
828 virtual bool MoveToChild (string localName, string namespaceURI)
830 return MoveTo (SelectChildren (localName, namespaceURI));
833 #if NET_2_0
834 public
835 #else
836 internal
837 #endif
838 virtual bool MoveToNext (string localName, string namespaceURI)
840 XPathNavigator nav = Clone ();
841 while (nav.MoveToNext ()) {
842 if (nav.LocalName == localName &&
843 nav.NamespaceURI == namespaceURI) {
844 MoveTo (nav);
845 return true;
848 return false;
851 #if NET_2_0
852 public
853 #else
854 internal
855 #endif
856 virtual bool MoveToNext (XPathNodeType type)
858 XPathNavigator nav = Clone ();
859 while (nav.MoveToNext ()) {
860 if (type == XPathNodeType.All || nav.NodeType == type) {
861 MoveTo (nav);
862 return true;
865 return false;
868 #if NET_2_0
869 public
870 #else
871 internal
872 #endif
873 virtual bool MoveToFollowing (string localName,
874 string namespaceURI)
876 return MoveToFollowing (localName, namespaceURI, null);
879 #if NET_2_0
880 public
881 #else
882 internal
883 #endif
884 virtual bool MoveToFollowing (string localName,
885 string namespaceURI, XPathNavigator end)
887 if (localName == null)
888 throw new ArgumentNullException ("localName");
889 if (namespaceURI == null)
890 throw new ArgumentNullException ("namespaceURI");
891 localName = NameTable.Get (localName);
892 if (localName == null)
893 return false;
894 namespaceURI = NameTable.Get (namespaceURI);
895 if (namespaceURI == null)
896 return false;
898 XPathNavigator nav = Clone ();
899 switch (nav.NodeType) {
900 case XPathNodeType.Attribute:
901 case XPathNodeType.Namespace:
902 nav.MoveToParent ();
903 break;
905 do {
906 if (!nav.MoveToFirstChild ()) {
907 do {
908 if (!nav.MoveToNext ()) {
909 if (!nav.MoveToParent ())
910 return false;
912 else
913 break;
914 } while (true);
916 if (end != null && end.IsSamePosition (nav))
917 return false;
918 if (object.ReferenceEquals (localName, nav.LocalName) &&
919 object.ReferenceEquals (namespaceURI, nav.NamespaceURI)) {
920 MoveTo (nav);
921 return true;
923 } while (true);
926 #if NET_2_0
927 public
928 #else
929 internal
930 #endif
931 virtual bool MoveToFollowing (XPathNodeType type)
933 return MoveToFollowing (type, null);
936 #if NET_2_0
937 public
938 #else
939 internal
940 #endif
941 virtual bool MoveToFollowing (XPathNodeType type,
942 XPathNavigator end)
944 if (type == XPathNodeType.Root)
945 return false; // will never match
946 XPathNavigator nav = Clone ();
947 switch (nav.NodeType) {
948 case XPathNodeType.Attribute:
949 case XPathNodeType.Namespace:
950 nav.MoveToParent ();
951 break;
953 do {
954 if (!nav.MoveToFirstChild ()) {
955 do {
956 if (!nav.MoveToNext ()) {
957 if (!nav.MoveToParent ())
958 return false;
960 else
961 break;
962 } while (true);
964 if (end != null && end.IsSamePosition (nav))
965 return false;
966 if (type == XPathNodeType.All || nav.NodeType == type) {
967 MoveTo (nav);
968 return true;
970 } while (true);
973 #if NET_2_0
974 public virtual XmlReader ReadSubtree ()
976 switch (NodeType) {
977 case XPathNodeType.Element:
978 case XPathNodeType.Root:
979 return new XPathNavigatorReader (this);
980 default:
981 throw new InvalidOperationException (String.Format ("NodeType {0} is not supported to read as a subtree of an XPathNavigator.", NodeType));
985 public virtual XPathNodeIterator Select (string xpath, IXmlNamespaceResolver nsResolver)
987 return Select (Compile (xpath), nsResolver);
990 public virtual XPathNavigator SelectSingleNode (string xpath)
992 return SelectSingleNode (xpath, null);
995 public virtual XPathNavigator SelectSingleNode (string xpath, IXmlNamespaceResolver nsResolver)
997 XPathExpression expr = Compile (xpath);
998 expr.SetContext (nsResolver);
999 return SelectSingleNode (expr);
1002 public virtual XPathNavigator SelectSingleNode (XPathExpression expression)
1004 XPathNodeIterator iter = Select (expression);
1005 if (iter.MoveNext ())
1006 return iter.Current;
1007 else
1008 return null;
1011 // it is not very effective code but should just work
1012 public override object ValueAs (Type type, IXmlNamespaceResolver nsResolver)
1014 return new XmlAtomicValue (Value, XmlSchemaSimpleType.XsString).ValueAs (type, nsResolver);
1017 public virtual void WriteSubtree (XmlWriter writer)
1019 writer.WriteNode (this, false);
1022 static readonly char [] escape_text_chars =
1023 new char [] {'&', '<', '>'};
1024 static readonly char [] escape_attr_chars =
1025 new char [] {'"', '&', '<', '>', '\r', '\n'};
1027 static string EscapeString (string value, bool attr)
1029 StringBuilder sb = null;
1030 char [] escape = attr ? escape_attr_chars : escape_text_chars;
1031 if (value.IndexOfAny (escape) < 0)
1032 return value;
1033 sb = new StringBuilder (value, value.Length + 10);
1034 if (attr)
1035 sb.Replace ("\"", "&quot;");
1036 sb.Replace ("<", "&lt;");
1037 sb.Replace (">", "&gt;");
1038 if (attr) {
1039 sb.Replace ("\r\n", "&#10;");
1040 sb.Replace ("\r", "&#10;");
1041 sb.Replace ("\n", "&#10;");
1043 return sb.ToString ();
1046 public virtual string InnerXml {
1047 get {
1048 switch (NodeType) {
1049 case XPathNodeType.Element:
1050 case XPathNodeType.Root:
1051 break;
1052 case XPathNodeType.Attribute:
1053 case XPathNodeType.Namespace:
1054 return EscapeString (Value, true);
1055 case XPathNodeType.Text:
1056 case XPathNodeType.Whitespace:
1057 case XPathNodeType.SignificantWhitespace:
1058 return String.Empty;
1059 case XPathNodeType.ProcessingInstruction:
1060 case XPathNodeType.Comment:
1061 return Value;
1064 XmlReader r = ReadSubtree ();
1065 r.Read (); // start
1066 // skip the element itself (or will reach to
1067 // EOF if other than element) unless writing
1068 // doc itself
1069 int depth = r.Depth;
1070 if (NodeType != XPathNodeType.Root)
1071 r.Read ();
1072 else
1073 depth = -1; // for Root, it should consume the entire tree, so no depth check is done.
1074 StringWriter sw = new StringWriter ();
1075 XmlWriterSettings s = new XmlWriterSettings ();
1076 s.Indent = true;
1077 s.ConformanceLevel = ConformanceLevel.Fragment;
1078 s.OmitXmlDeclaration = true;
1079 XmlWriter xtw = XmlWriter.Create (sw, s);
1080 while (!r.EOF && r.Depth > depth)
1081 xtw.WriteNode (r, false);
1082 return sw.ToString ();
1084 set {
1085 DeleteChildren ();
1086 if (NodeType == XPathNodeType.Attribute) {
1087 SetValue (value);
1088 return;
1090 AppendChild (value);
1094 public override sealed bool IsNode {
1095 get { return true; }
1098 public virtual string OuterXml {
1099 get {
1100 switch (NodeType) {
1101 case XPathNodeType.Attribute:
1102 return String.Concat (
1103 Prefix,
1104 Prefix.Length > 0 ? ":" : String.Empty,
1105 LocalName,
1106 "=\"",
1107 EscapeString (Value, true),
1108 "\"");
1109 case XPathNodeType.Namespace:
1110 return String.Concat (
1111 "xmlns",
1112 LocalName.Length > 0 ? ":" : String.Empty,
1113 LocalName,
1114 "=\"",
1115 EscapeString (Value, true),
1116 "\"");
1117 case XPathNodeType.Text:
1118 return EscapeString (Value, false);
1119 case XPathNodeType.Whitespace:
1120 case XPathNodeType.SignificantWhitespace:
1121 return Value;
1124 XmlWriterSettings s = new XmlWriterSettings ();
1125 s.Indent = true;
1126 s.OmitXmlDeclaration = true;
1127 s.ConformanceLevel = ConformanceLevel.Fragment;
1128 StringBuilder sb = new StringBuilder ();
1129 using (XmlWriter w = XmlWriter.Create (sb, s)) {
1130 WriteSubtree (w);
1132 return sb.ToString ();
1134 set {
1135 switch (NodeType) {
1136 case XPathNodeType.Root:
1137 case XPathNodeType.Attribute:
1138 case XPathNodeType.Namespace:
1139 throw new XmlException ("Setting OuterXml Root, Attribute and Namespace is not supported.");
1142 DeleteSelf ();
1143 AppendChild (value);
1144 MoveToFirstChild ();
1148 public virtual IXmlSchemaInfo SchemaInfo {
1149 get {
1150 return null;
1154 public override object TypedValue {
1155 get {
1156 switch (NodeType) {
1157 case XPathNodeType.Element:
1158 case XPathNodeType.Attribute:
1159 if (XmlType == null)
1160 break;
1161 XmlSchemaDatatype dt = XmlType.Datatype;
1162 if (dt == null)
1163 break;
1164 return dt.ParseValue (Value, NameTable, this as IXmlNamespaceResolver);
1166 return Value;
1170 public virtual object UnderlyingObject {
1171 get { return null; }
1174 public override bool ValueAsBoolean {
1175 get { return XQueryConvert.StringToBoolean (Value); }
1178 public override DateTime ValueAsDateTime {
1179 get { return XmlConvert.ToDateTime (Value); }
1182 public override double ValueAsDouble {
1183 get { return XQueryConvert.StringToDouble (Value); }
1186 public override int ValueAsInt {
1187 get { return XQueryConvert.StringToInt (Value); }
1190 public override long ValueAsLong {
1191 get { return XQueryConvert.StringToInteger (Value); }
1194 public override Type ValueType {
1195 get {
1196 return SchemaInfo != null &&
1197 SchemaInfo.SchemaType != null &&
1198 SchemaInfo.SchemaType.Datatype != null ?
1199 SchemaInfo.SchemaType.Datatype.ValueType
1200 : null;
1204 public override XmlSchemaType XmlType {
1205 get {
1206 if (SchemaInfo != null)
1207 return SchemaInfo.SchemaType;
1208 return null;
1212 private XmlReader CreateFragmentReader (string fragment)
1214 XmlReaderSettings settings = new XmlReaderSettings ();
1215 settings.ConformanceLevel = ConformanceLevel.Fragment;
1216 XmlNamespaceManager nsmgr = new XmlNamespaceManager (NameTable);
1217 foreach (KeyValuePair<string,string> nss in GetNamespacesInScope (XmlNamespaceScope.All))
1218 nsmgr.AddNamespace (nss.Key, nss.Value);
1219 return XmlReader.Create (
1220 new StringReader (fragment),
1221 settings,
1222 new XmlParserContext (NameTable, nsmgr, null, XmlSpace.None));
1225 // must override it.
1226 public virtual XmlWriter AppendChild ()
1228 throw new NotSupportedException ();
1231 public virtual void AppendChild (
1232 string xmlFragments)
1234 AppendChild (CreateFragmentReader (xmlFragments));
1237 public virtual void AppendChild (
1238 XmlReader reader)
1240 XmlWriter w = AppendChild ();
1241 while (!reader.EOF)
1242 w.WriteNode (reader, false);
1243 w.Close ();
1246 public virtual void AppendChild (
1247 XPathNavigator nav)
1249 AppendChild (new XPathNavigatorReader (nav));
1252 public virtual void AppendChildElement (string prefix, string name, string ns, string value)
1254 XmlWriter xw = AppendChild ();
1255 xw.WriteStartElement (prefix, name, ns);
1256 xw.WriteString (value);
1257 xw.WriteEndElement ();
1258 xw.Close ();
1261 public virtual void CreateAttribute (string prefix, string localName, string namespaceURI, string value)
1263 using (XmlWriter w = CreateAttributes ()) {
1264 w.WriteAttributeString (prefix, localName, namespaceURI, value);
1268 // must override it.
1269 public virtual XmlWriter CreateAttributes ()
1271 throw new NotSupportedException ();
1274 // must override it.
1275 public virtual void DeleteSelf ()
1277 throw new NotSupportedException ();
1280 // must override it.
1281 public virtual void DeleteRange (XPathNavigator nav)
1283 throw new NotSupportedException ();
1286 public virtual XmlWriter ReplaceRange (XPathNavigator nav)
1288 throw new NotSupportedException ();
1291 public virtual XmlWriter InsertAfter ()
1293 switch (NodeType) {
1294 case XPathNodeType.Root:
1295 case XPathNodeType.Attribute:
1296 case XPathNodeType.Namespace:
1297 throw new InvalidOperationException (String.Format ("Insertion after {0} is not allowed.", NodeType));
1299 XPathNavigator nav = Clone ();
1300 if (nav.MoveToNext ())
1301 return nav.InsertBefore ();
1302 else if (nav.MoveToParent ())
1303 return nav.AppendChild ();
1304 else
1305 throw new InvalidOperationException ("Could not move to parent to insert sibling node");
1308 public virtual void InsertAfter (string xmlFragments)
1310 InsertAfter (CreateFragmentReader (xmlFragments));
1313 public virtual void InsertAfter (XmlReader reader)
1315 using (XmlWriter w = InsertAfter ()) {
1316 w.WriteNode (reader, false);
1320 public virtual void InsertAfter (XPathNavigator nav)
1322 InsertAfter (new XPathNavigatorReader (nav));
1325 public virtual XmlWriter InsertBefore ()
1327 throw new NotSupportedException ();
1330 public virtual void InsertBefore (string xmlFragments)
1332 InsertBefore (CreateFragmentReader (xmlFragments));
1335 public virtual void InsertBefore (XmlReader reader)
1337 using (XmlWriter w = InsertBefore ()) {
1338 w.WriteNode (reader, false);
1342 public virtual void InsertBefore (XPathNavigator nav)
1344 InsertBefore (new XPathNavigatorReader (nav));
1347 public virtual void InsertElementAfter (string prefix,
1348 string localName, string namespaceURI, string value)
1350 using (XmlWriter w = InsertAfter ()) {
1351 w.WriteElementString (prefix, localName, namespaceURI, value);
1355 public virtual void InsertElementBefore (string prefix,
1356 string localName, string namespaceURI, string value)
1358 using (XmlWriter w = InsertBefore ()) {
1359 w.WriteElementString (prefix, localName, namespaceURI, value);
1363 public virtual XmlWriter PrependChild ()
1365 XPathNavigator nav = Clone ();
1366 if (nav.MoveToFirstChild ())
1367 return nav.InsertBefore ();
1368 else
1369 return AppendChild ();
1372 public virtual void PrependChild (string xmlFragments)
1374 PrependChild (CreateFragmentReader (xmlFragments));
1377 public virtual void PrependChild (XmlReader reader)
1379 using (XmlWriter w = PrependChild ()) {
1380 w.WriteNode (reader, false);
1384 public virtual void PrependChild (XPathNavigator nav)
1386 PrependChild (new XPathNavigatorReader (nav));
1389 public virtual void PrependChildElement (string prefix,
1390 string localName, string namespaceURI, string value)
1392 using (XmlWriter w = PrependChild ()) {
1393 w.WriteElementString (prefix, localName, namespaceURI, value);
1397 public virtual void ReplaceSelf (string xmlFragment)
1399 ReplaceSelf (CreateFragmentReader (xmlFragment));
1402 // must override it.
1403 public virtual void ReplaceSelf (XmlReader reader)
1405 throw new NotSupportedException ();
1408 public virtual void ReplaceSelf (XPathNavigator navigator)
1410 ReplaceSelf (new XPathNavigatorReader (navigator));
1413 // Dunno the exact purpose, but maybe internal editor use
1414 [MonoTODO]
1415 public virtual void SetTypedValue (object value)
1417 throw new NotSupportedException ();
1420 public virtual void SetValue (string value)
1422 throw new NotSupportedException ();
1425 private void DeleteChildren ()
1427 switch (NodeType) {
1428 case XPathNodeType.Namespace:
1429 throw new InvalidOperationException ("Removing namespace node content is not supported.");
1430 case XPathNodeType.Attribute:
1431 return;
1432 case XPathNodeType.Text:
1433 case XPathNodeType.SignificantWhitespace:
1434 case XPathNodeType.Whitespace:
1435 case XPathNodeType.ProcessingInstruction:
1436 case XPathNodeType.Comment:
1437 DeleteSelf ();
1438 return;
1440 if (!HasChildren)
1441 return;
1442 XPathNavigator nav = Clone ();
1443 nav.MoveToFirstChild ();
1444 while (!nav.IsSamePosition (this))
1445 nav.DeleteSelf ();
1447 #endif