svn path=/trunk/mcs/; revision=104772
[mono.git] / mcs / class / System.XML / System.Xml.XPath / XPathNavigator.cs
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.
10 //
11
12 //
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.
31 //
32
33 using System;
34 #if NET_2_0
35 using System.Collections;
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;
44
45 #if NET_2_0
46 using NSResolver = System.Xml.IXmlNamespaceResolver;
47 #else
48 using NSResolver = System.Xml.XmlNamespaceManager;
49 #endif
50
51 namespace System.Xml.XPath
52 {
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
59         {
60                 #region Static members
61 #if NET_2_0
62                 public static IEqualityComparer NavigatorComparer {
63                         get { return XPathNavigatorComparer.Instance; }
64                 }
65 #endif
66                 #endregion
67
68                 #region Constructor
69
70                 protected XPathNavigator ()
71                 {
72                 }
73
74                 #endregion
75
76                 #region Properties
77
78                 public abstract string BaseURI { get; }
79
80 #if NET_2_0
81                 public virtual bool CanEdit {
82                         get { return false; }
83                 }
84
85                 public virtual bool HasAttributes {
86                         get {
87                                 if (!MoveToFirstAttribute ())
88                                         return false;
89                                 MoveToParent ();
90                                 return true;
91                         }
92                 }
93
94                 public virtual bool HasChildren {
95                         get {
96                                 if (!MoveToFirstChild ())
97                                         return false;
98                                 MoveToParent ();
99                                 return true;
100                         }
101                 }
102 #else
103                 public abstract bool HasAttributes { get; }
104
105                 public abstract bool HasChildren { get; }
106 #endif
107
108                 public abstract bool IsEmptyElement { get; }
109
110                 public abstract string LocalName { get; }
111
112                 public abstract string Name { get; }
113
114                 public abstract string NamespaceURI { get; }
115
116                 public abstract XmlNameTable NameTable { get; }
117
118                 public abstract XPathNodeType NodeType { get; }
119
120                 public abstract string Prefix { get; }
121
122 #if NET_2_0
123                 public virtual string XmlLang {
124                         get {
125                                 XPathNavigator nav = Clone ();
126                                 switch (nav.NodeType) {
127                                 case XPathNodeType.Attribute:
128                                 case XPathNodeType.Namespace:
129                                         nav.MoveToParent ();
130                                         break;
131                                 }
132                                 do {
133                                         if (nav.MoveToAttribute ("lang", "http://www.w3.org/XML/1998/namespace"))
134                                                 return nav.Value;
135                                 } while (nav.MoveToParent ());
136                                 return String.Empty;
137                         }
138                 }
139 #else
140                 public abstract string Value { get; }
141
142                 public abstract string XmlLang { get; }
143 #endif
144
145                 #endregion
146
147                 #region Methods
148
149                 public abstract XPathNavigator Clone ();
150
151                 public virtual XmlNodeOrder ComparePosition (XPathNavigator nav)
152                 {
153                         if (IsSamePosition (nav))
154                                 return XmlNodeOrder.Same;
155
156                         // quick check for direct descendant
157                         if (IsDescendant (nav))
158                                 return XmlNodeOrder.Before;
159
160                         // quick check for direct ancestor
161                         if (nav.IsDescendant (this))
162                                 return XmlNodeOrder.After;
163
164                         XPathNavigator nav1 = Clone ();
165                         XPathNavigator nav2 = nav.Clone ();
166
167                         // check if document instance is the same.
168                         nav1.MoveToRoot ();
169                         nav2.MoveToRoot ();
170                         if (!nav1.IsSamePosition (nav2))
171                                 return XmlNodeOrder.Unknown;
172                         nav1.MoveTo (this);
173                         nav2.MoveTo (nav);
174
175                         int depth1 = 0;
176                         while (nav1.MoveToParent ())
177                                 depth1++;
178                         nav1.MoveTo (this);
179                         int depth2 = 0;
180                         while (nav2.MoveToParent ())
181                                 depth2++;
182                         nav2.MoveTo (nav);
183
184                         // find common parent depth
185                         int common = depth1;
186                         for (;common > depth2; common--)
187                                 nav1.MoveToParent ();
188                         for (int i = depth2; i > common; i--)
189                                 nav2.MoveToParent ();
190                         while (!nav1.IsSamePosition (nav2)) {
191                                 nav1.MoveToParent ();
192                                 nav2.MoveToParent ();
193                                 common--;
194                         }
195
196                         // For each this and target, move to the node that is 
197                         // ancestor of the node and child of the common parent.
198                         nav1.MoveTo (this);
199                         for (int i = depth1; i > common + 1; i--)
200                                 nav1.MoveToParent ();
201                         nav2.MoveTo (nav);
202                         for (int i = depth2; i > common + 1; i--)
203                                 nav2.MoveToParent ();
204
205                         // Those children of common parent are comparable.
206                         // namespace nodes precede to attributes, and they
207                         // precede to other nodes.
208                         if (nav1.NodeType == XPathNodeType.Namespace) {
209                                 if (nav2.NodeType != XPathNodeType.Namespace)
210                                         return XmlNodeOrder.Before;
211                                 while (nav1.MoveToNextNamespace ())
212                                         if (nav1.IsSamePosition (nav2))
213                                                 return XmlNodeOrder.Before;
214                                 return XmlNodeOrder.After;
215                         }
216                         if (nav2.NodeType == XPathNodeType.Namespace)
217                                 return XmlNodeOrder.After;
218                         if (nav1.NodeType == XPathNodeType.Attribute) {
219                                 if (nav2.NodeType != XPathNodeType.Attribute)
220                                         return XmlNodeOrder.Before;
221                                 while (nav1.MoveToNextAttribute ())
222                                         if (nav1.IsSamePosition (nav2))
223                                                 return XmlNodeOrder.Before;
224                                 return XmlNodeOrder.After;
225                         }
226                         while (nav1.MoveToNext ())
227                                 if (nav1.IsSamePosition (nav2))
228                                         return XmlNodeOrder.Before;
229                         return XmlNodeOrder.After;
230                 }
231
232                 public virtual XPathExpression Compile (string xpath)
233                 {
234                         return XPathExpression.Compile (xpath);
235                 }
236                 
237                 internal virtual XPathExpression Compile (string xpath, System.Xml.Xsl.IStaticXsltContext ctx)
238                 {
239                         return XPathExpression.Compile (xpath, null, ctx);
240                 }
241
242                 public virtual object Evaluate (string xpath)
243                 {
244                         return Evaluate (Compile (xpath));
245                 }
246
247                 public virtual object Evaluate (XPathExpression expr)
248                 {
249                         return Evaluate (expr, null);
250                 }
251
252                 public virtual object Evaluate (XPathExpression expr, XPathNodeIterator context)
253                 {
254                         return Evaluate (expr, context, null);
255                 }
256                 
257                 internal virtual object Evaluate (XPathExpression expr, XPathNodeIterator context, NSResolver ctx)
258                 {
259                         CompiledExpression cexpr = (CompiledExpression) expr;
260                         if (ctx == null)
261                                 ctx = cexpr.NamespaceManager;
262                         
263                         if (context == null)
264                                 context = new NullIterator (this, ctx);
265                         BaseIterator iterContext = (BaseIterator) context;
266                         iterContext.NamespaceManager = ctx;
267                         return cexpr.Evaluate (iterContext);
268                 }
269
270                 internal XPathNodeIterator EvaluateNodeSet (XPathExpression expr, XPathNodeIterator context, NSResolver ctx)
271                 {
272                         CompiledExpression cexpr = (CompiledExpression) expr;
273                         if (ctx == null)
274                                 ctx = cexpr.NamespaceManager;
275                         
276                         if (context == null)
277                                 context = new NullIterator (this, cexpr.NamespaceManager);
278                         BaseIterator iterContext = (BaseIterator) context;
279                         iterContext.NamespaceManager = ctx;
280                         return cexpr.EvaluateNodeSet (iterContext);
281                 }
282
283                 internal string EvaluateString (XPathExpression expr, XPathNodeIterator context, NSResolver ctx)
284                 {
285                         CompiledExpression cexpr = (CompiledExpression) expr;
286                         if (ctx == null)
287                                 ctx = cexpr.NamespaceManager;
288                         
289                         if (context == null)
290                                 context = new NullIterator (this, cexpr.NamespaceManager);
291                         BaseIterator iterContext = (BaseIterator) context;
292                         iterContext.NamespaceManager = ctx;
293                         return cexpr.EvaluateString (iterContext);
294                 }
295
296                 internal double EvaluateNumber (XPathExpression expr, XPathNodeIterator context, NSResolver ctx)
297                 {
298                         CompiledExpression cexpr = (CompiledExpression) expr;
299                         if (ctx == null)
300                                 ctx = cexpr.NamespaceManager;
301                         
302                         if (context == null)
303                                 context = new NullIterator (this, cexpr.NamespaceManager);
304                         BaseIterator iterContext = (BaseIterator) context;
305                         iterContext.NamespaceManager = ctx;
306                         return cexpr.EvaluateNumber (iterContext);
307                 }
308
309                 internal bool EvaluateBoolean (XPathExpression expr, XPathNodeIterator context, NSResolver ctx)
310                 {
311                         CompiledExpression cexpr = (CompiledExpression) expr;
312                         if (ctx == null)
313                                 ctx = cexpr.NamespaceManager;
314                         
315                         if (context == null)
316                                 context = new NullIterator (this, cexpr.NamespaceManager);
317                         BaseIterator iterContext = (BaseIterator) context;
318                         iterContext.NamespaceManager = ctx;
319                         return cexpr.EvaluateBoolean (iterContext);
320                 }
321
322 #if NET_2_0
323                 public virtual string GetAttribute (string localName, string namespaceURI)
324                 {
325                         if (!MoveToAttribute (localName, namespaceURI))
326                                 return String.Empty;
327                         string value = Value;
328                         MoveToParent ();
329                         return value;
330                 }
331
332                 public virtual string GetNamespace (string name)
333                 {
334                         if (!MoveToNamespace (name))
335                                 return String.Empty;
336                         string value = Value;
337                         MoveToParent ();
338                         return value;
339                 }
340
341 #else
342                 public abstract string GetAttribute (string localName, string namespaceURI);
343
344                 public abstract string GetNamespace (string name);
345 #endif
346                 
347                 object ICloneable.Clone ()
348                 {
349                         return Clone ();
350                 }
351
352                 public virtual bool IsDescendant (XPathNavigator nav)
353                 {
354                         if (nav != null)
355                         {
356                                 nav = nav.Clone ();
357                                 while (nav.MoveToParent ())
358                                 {
359                                         if (IsSamePosition (nav))
360                                                 return true;
361                                 }
362                         }
363                         return false;
364                 }
365
366                 public abstract bool IsSamePosition (XPathNavigator other);
367
368                 public virtual bool Matches (string xpath)
369                 {
370                         return Matches (Compile (xpath));
371                 }
372
373                 public virtual bool Matches (XPathExpression expr)
374                 {
375                         Expression e = ((CompiledExpression) expr).ExpressionNode;
376                         if (e is ExprRoot)
377                                 return NodeType == XPathNodeType.Root;
378                         
379                         NodeTest nt = e as NodeTest;
380                         if (nt != null) {
381                                 switch (nt.Axis.Axis) {
382                                 case Axes.Child:
383                                 case Axes.Attribute:
384                                         break;
385                                 default:
386                                         throw new XPathException ("Only child and attribute pattern are allowed for a pattern.");
387                                 }
388                                 return nt.Match (((CompiledExpression)expr).NamespaceManager, this);
389                         }
390                         if (e is ExprFilter) {
391                                 do {
392                                         e = ((ExprFilter) e).LeftHandSide;
393                                 } while (e is ExprFilter);
394                                 
395                                 if (e is NodeTest && !((NodeTest) e).Match (((CompiledExpression) expr).NamespaceManager, this))
396                                         return false;
397                         }
398
399                         XPathResultType resultType = e.ReturnType;
400                         switch (resultType) {
401                         case XPathResultType.Any:
402                         case XPathResultType.NodeSet:
403                                 break;
404                         default:
405                                 return false;
406                         }
407
408                         switch (e.EvaluatedNodeType) {
409                         case XPathNodeType.Attribute:
410                         case XPathNodeType.Namespace:
411                                 if (NodeType != e.EvaluatedNodeType)
412                                         return false;
413                                 break;
414                         }
415
416                         XPathNodeIterator nodes;
417                         nodes = this.Select (expr);
418                         while (nodes.MoveNext ()) {
419                                 if (IsSamePosition (nodes.Current))
420                                         return true;
421                         }
422
423                         // ancestors might select this node.
424
425                         XPathNavigator navigator = Clone ();
426
427                         while (navigator.MoveToParent ()) {
428                                 nodes = navigator.Select (expr);
429
430                                 while (nodes.MoveNext ()) {
431                                         if (IsSamePosition (nodes.Current))
432                                                 return true;
433                                 }
434                         }
435
436                         return false;
437                 }
438
439                 public abstract bool MoveTo (XPathNavigator other);
440
441 #if NET_2_0
442                 public virtual bool MoveToAttribute (string localName, string namespaceURI)
443                 {
444                         if (MoveToFirstAttribute ()) {
445                                 do {
446                                         if (LocalName == localName && NamespaceURI == namespaceURI)
447                                                 return true;
448                                 } while (MoveToNextAttribute ());
449                                 MoveToParent ();
450                         }
451                         return false;
452                 }
453
454                 public virtual bool MoveToNamespace (string name)
455                 {
456                         if (MoveToFirstNamespace ()) {
457                                 do {
458                                         if (LocalName == name)
459                                                 return true;
460                                 } while (MoveToNextNamespace ());
461                                 MoveToParent ();
462                         }
463                         return false;
464                 }
465
466                 /*
467                 public virtual bool MoveToFirst ()
468                 {
469                         if (MoveToPrevious ()) {
470                                 // It would be able to invoke MoveToPrevious() until the end, but this way would be much faster
471                                 MoveToParent ();
472                                 MoveToFirstChild ();
473                                 return true;
474                         }
475                         return false;
476                 }
477                 */
478
479                 public virtual bool MoveToFirst ()
480                 {
481                         return MoveToFirstImpl ();
482                 }
483
484                 public virtual void MoveToRoot ()
485                 {
486                         while (MoveToParent ())
487                                 ;
488                 }
489 #else
490                 public abstract bool MoveToAttribute (string localName, string namespaceURI);
491
492                 public abstract bool MoveToNamespace (string name);
493
494                 public abstract bool MoveToFirst ();
495
496                 public abstract void MoveToRoot ();
497 #endif
498
499                 internal bool MoveToFirstImpl ()
500                 {
501                         switch (NodeType) {
502                         case XPathNodeType.Attribute:
503                         case XPathNodeType.Namespace:
504                                 return false;
505                         default:
506                                 if (!MoveToParent ())
507                                         return false;
508                                 // Follow these 2 steps so that we can skip 
509                                 // some types of nodes .
510                                 MoveToFirstChild ();
511                                 return true;
512                         }
513                 }
514
515                 public abstract bool MoveToFirstAttribute ();
516
517                 public abstract bool MoveToFirstChild ();
518
519                 public bool MoveToFirstNamespace ()
520                 {
521                         return MoveToFirstNamespace (XPathNamespaceScope.All);
522                 }
523
524                 public abstract bool MoveToFirstNamespace (XPathNamespaceScope namespaceScope);
525
526                 public abstract bool MoveToId (string id);
527
528                 public abstract bool MoveToNext ();
529
530                 public abstract bool MoveToNextAttribute ();
531
532                 public bool MoveToNextNamespace ()
533                 {
534                         return MoveToNextNamespace (XPathNamespaceScope.All);
535                 }
536
537                 public abstract bool MoveToNextNamespace (XPathNamespaceScope namespaceScope);
538
539                 public abstract bool MoveToParent ();
540
541                 public abstract bool MoveToPrevious ();
542
543                 public virtual XPathNodeIterator Select (string xpath)
544                 {
545                         return Select (Compile (xpath));
546                 }
547
548                 public virtual XPathNodeIterator Select (XPathExpression expr)
549                 {
550                         return Select (expr, null);
551                 }
552                 
553                 internal virtual XPathNodeIterator Select (XPathExpression expr, NSResolver ctx)
554                 {
555                         CompiledExpression cexpr = (CompiledExpression) expr;
556                         if (ctx == null)
557                                 ctx = cexpr.NamespaceManager;
558                         
559                         BaseIterator iter = new NullIterator (this, ctx);
560                         return cexpr.EvaluateNodeSet (iter);
561                 }
562
563                 public virtual XPathNodeIterator SelectAncestors (XPathNodeType type, bool matchSelf)
564                 {
565                         Axes axis = (matchSelf) ? Axes.AncestorOrSelf : Axes.Ancestor;
566                         return SelectTest (new NodeTypeTest (axis, type));
567                 }
568
569                 public virtual XPathNodeIterator SelectAncestors (string name, string namespaceURI, bool matchSelf)
570                 {
571                         if (name == null)
572                                 throw new ArgumentNullException ("name");
573                         if (namespaceURI == null)
574                                 throw new ArgumentNullException ("namespaceURI");
575
576                         Axes axis = (matchSelf) ? Axes.AncestorOrSelf : Axes.Ancestor;
577                         XmlQualifiedName qname = new XmlQualifiedName (name, namespaceURI);
578                         return SelectTest (new NodeNameTest (axis, qname, true));
579                 }
580
581                 public virtual XPathNodeIterator SelectChildren (XPathNodeType type)
582                 {
583                         return SelectTest (new NodeTypeTest (Axes.Child, type));
584                 }
585
586                 public virtual XPathNodeIterator SelectChildren (string name, string namespaceURI)
587                 {
588                         if (name == null)
589                                 throw new ArgumentNullException ("name");
590                         if (namespaceURI == null)
591                                 throw new ArgumentNullException ("namespaceURI");
592
593                         Axes axis = Axes.Child;
594                         XmlQualifiedName qname = new XmlQualifiedName (name, namespaceURI);
595                         return SelectTest (new NodeNameTest (axis, qname, true));
596                 }
597
598                 public virtual XPathNodeIterator SelectDescendants (XPathNodeType type, bool matchSelf)
599                 {
600                         Axes axis = (matchSelf) ? Axes.DescendantOrSelf : Axes.Descendant;
601                         return SelectTest (new NodeTypeTest (axis, type));
602                 }
603
604                 public virtual XPathNodeIterator SelectDescendants (string name, string namespaceURI, bool matchSelf)
605                 {
606                         if (name == null)
607                                 throw new ArgumentNullException ("name");
608                         if (namespaceURI == null)
609                                 throw new ArgumentNullException ("namespaceURI");
610
611
612                         Axes axis = (matchSelf) ? Axes.DescendantOrSelf : Axes.Descendant;
613                         XmlQualifiedName qname = new XmlQualifiedName (name, namespaceURI);
614                         return SelectTest (new NodeNameTest (axis, qname, true));
615                 }
616
617                 internal XPathNodeIterator SelectTest (NodeTest test)
618                 {
619                         return test.EvaluateNodeSet (new NullIterator (this));
620                 }
621
622                 public override string ToString ()
623                 {
624                         return Value;
625                 }
626
627                 #endregion
628
629 #if NET_2_0
630
631                 public virtual bool CheckValidity (XmlSchemaSet schemas, ValidationEventHandler handler)
632                 {
633                         XmlReaderSettings settings = new XmlReaderSettings ();
634                         settings.NameTable = NameTable;
635                         settings.SetSchemas (schemas);
636                         settings.ValidationEventHandler += handler;
637                         settings.ValidationType = ValidationType.Schema;
638                         try {
639                                 XmlReader r = XmlReader.Create (
640                                         ReadSubtree (), settings);
641                                 while (!r.EOF)
642                                         r.Read ();
643                         } catch (XmlSchemaValidationException) {
644                                 return false;
645                         }
646                         return true;
647                 }
648
649                 public virtual XPathNavigator CreateNavigator ()
650                 {
651                         return Clone ();
652                 }
653
654                 public virtual object Evaluate (string xpath, IXmlNamespaceResolver nsResolver)
655                 {
656                         return Evaluate (Compile (xpath), null, nsResolver);
657                 }
658
659                 public virtual IDictionary<string, string> GetNamespacesInScope (XmlNamespaceScope scope)
660                 {
661                         IDictionary<string, string> table = new Dictionary<string, string> ();
662                         XPathNamespaceScope xpscope =
663                                 scope == XmlNamespaceScope.Local ?
664                                         XPathNamespaceScope.Local :
665                                 scope == XmlNamespaceScope.ExcludeXml ?
666                                         XPathNamespaceScope.ExcludeXml :
667                                 XPathNamespaceScope.All;
668                         XPathNavigator nav = Clone ();
669                         if (nav.NodeType != XPathNodeType.Element)
670                                 nav.MoveToParent ();
671                         if (!nav.MoveToFirstNamespace (xpscope))
672                                 return table;
673                         do {
674                                 table.Add (nav.Name, nav.Value);
675                         } while (nav.MoveToNextNamespace (xpscope));
676                         return table;
677                 }
678
679                 public virtual string LookupNamespace (string prefix)
680                 {
681                         XPathNavigator nav = Clone ();
682                         if (nav.NodeType != XPathNodeType.Element)
683                                 nav.MoveToParent ();
684                         if (nav.MoveToNamespace (prefix))
685                                 return nav.Value;
686                         return null;
687                 }
688
689                 public virtual string LookupPrefix (string namespaceUri)
690                 {
691                         XPathNavigator nav = Clone ();
692                         if (nav.NodeType != XPathNodeType.Element)
693                                 nav.MoveToParent ();
694                         if (!nav.MoveToFirstNamespace ())
695                                 return null;
696                         do {
697                                 if (nav.Value == namespaceUri)
698                                         return nav.Name;
699                         } while (nav.MoveToNextNamespace ());
700                         return null;
701                 }
702
703                 private bool MoveTo (XPathNodeIterator iter)
704                 {
705                         if (iter.MoveNext ()) {
706                                 MoveTo (iter.Current);
707                                 return true;
708                         }
709                         else
710                                 return false;
711                 }
712
713                 public virtual bool MoveToChild (XPathNodeType type)
714                 {
715                         return MoveTo (SelectChildren (type));
716                 }
717
718                 public virtual bool MoveToChild (string localName, string namespaceURI)
719                 {
720                         return MoveTo (SelectChildren (localName, namespaceURI));
721                 }
722
723                 public virtual bool MoveToNext (string localName, string namespaceURI)
724                 {
725                         XPathNavigator nav = Clone ();
726                         while (nav.MoveToNext ()) {
727                                 if (nav.LocalName == localName &&
728                                         nav.NamespaceURI == namespaceURI) {
729                                         MoveTo (nav);
730                                         return true;
731                                 }
732                         }
733                         return false;
734                 }
735
736                 public virtual bool MoveToNext (XPathNodeType type)
737                 {
738                         XPathNavigator nav = Clone ();
739                         while (nav.MoveToNext ()) {
740                                 if (type == XPathNodeType.All || nav.NodeType == type) {
741                                         MoveTo (nav);
742                                         return true;
743                                 }
744                         }
745                         return false;
746                 }
747
748                 public virtual bool MoveToFollowing (string localName,
749                         string namespaceURI)
750                 {
751                         return MoveToFollowing (localName, namespaceURI, null);
752                 }
753
754                 public virtual bool MoveToFollowing (string localName,
755                         string namespaceURI, XPathNavigator end)
756                 {
757                         if (localName == null)
758                                 throw new ArgumentNullException ("localName");
759                         if (namespaceURI == null)
760                                 throw new ArgumentNullException ("namespaceURI");
761                         localName = NameTable.Get (localName);
762                         if (localName == null)
763                                 return false;
764                         namespaceURI = NameTable.Get (namespaceURI);
765                         if (namespaceURI == null)
766                                 return false;
767
768                         XPathNavigator nav = Clone ();
769                         switch (nav.NodeType) {
770                         case XPathNodeType.Attribute:
771                         case XPathNodeType.Namespace:
772                                 nav.MoveToParent ();
773                                 break;
774                         }
775                         do {
776                                 if (!nav.MoveToFirstChild ()) {
777                                         do {
778                                                 if (!nav.MoveToNext ()) {
779                                                         if (!nav.MoveToParent ())
780                                                                 return false;
781                                                 }
782                                                 else
783                                                         break;
784                                         } while (true);
785                                 }
786                                 if (end != null && end.IsSamePosition (nav))
787                                         return false;
788                                 if (object.ReferenceEquals (localName, nav.LocalName) &&
789                                         object.ReferenceEquals (namespaceURI, nav.NamespaceURI)) {
790                                         MoveTo (nav);
791                                         return true;
792                                 }
793                         } while (true);
794                 }
795
796                 public virtual bool MoveToFollowing (XPathNodeType type)
797                 {
798                         return MoveToFollowing (type, null);
799                 }
800
801                 public virtual bool MoveToFollowing (XPathNodeType type,
802                         XPathNavigator end)
803                 {
804                         if (type == XPathNodeType.Root)
805                                 return false; // will never match
806                         XPathNavigator nav = Clone ();
807                         switch (nav.NodeType) {
808                         case XPathNodeType.Attribute:
809                         case XPathNodeType.Namespace:
810                                 nav.MoveToParent ();
811                                 break;
812                         }
813                         do {
814                                 if (!nav.MoveToFirstChild ()) {
815                                         do {
816                                                 if (!nav.MoveToNext ()) {
817                                                         if (!nav.MoveToParent ())
818                                                                 return false;
819                                                 }
820                                                 else
821                                                         break;
822                                         } while (true);
823                                 }
824                                 if (end != null && end.IsSamePosition (nav))
825                                         return false;
826                                 if (type == XPathNodeType.All || nav.NodeType == type) {
827                                         MoveTo (nav);
828                                         return true;
829                                 }
830                         } while (true);
831                 }
832
833                 public virtual XmlReader ReadSubtree ()
834                 {
835                         switch (NodeType) {
836                         case XPathNodeType.Element:
837                         case XPathNodeType.Root:
838                                 return new XPathNavigatorReader (this);
839                         default:
840                                 throw new InvalidOperationException (String.Format ("NodeType {0} is not supported to read as a subtree of an XPathNavigator.", NodeType));
841                         }
842                 }
843
844                 public virtual XPathNodeIterator Select (string xpath, IXmlNamespaceResolver nsResolver)
845                 {
846                         return Select (Compile (xpath), nsResolver);
847                 }
848
849                 public virtual XPathNavigator SelectSingleNode (string xpath)
850                 {
851                         return SelectSingleNode (xpath, null);
852                 }
853
854                 public virtual XPathNavigator SelectSingleNode (string xpath, IXmlNamespaceResolver nsResolver)
855                 {
856                         XPathExpression expr = Compile (xpath);
857                         expr.SetContext (nsResolver);
858                         return SelectSingleNode (expr);
859                 }
860
861                 public virtual XPathNavigator SelectSingleNode (XPathExpression expression)
862                 {
863                         XPathNodeIterator iter = Select (expression);
864                         if (iter.MoveNext ())
865                                 return iter.Current;
866                         else
867                                 return null;
868                 }
869
870                 // it is not very effective code but should just work
871                 public override object ValueAs (Type type, IXmlNamespaceResolver nsResolver)
872                 {
873                         return new XmlAtomicValue (Value, XmlSchemaSimpleType.XsString).ValueAs (type, nsResolver);
874                 }
875
876                 public virtual void WriteSubtree (XmlWriter writer)
877                 {
878                         writer.WriteNode (this, false);
879                 }
880
881                 static readonly char [] escape_text_chars =
882                                 new char [] {'&', '<', '>'};
883                 static readonly char [] escape_attr_chars =
884                                 new char [] {'"', '&', '<', '>', '\r', '\n'};
885
886                 static string EscapeString (string value, bool attr)
887                 {
888                         StringBuilder sb = null;
889                         char [] escape = attr ? escape_attr_chars : escape_text_chars;
890                         if (value.IndexOfAny (escape) < 0)
891                                 return value;
892                         sb = new StringBuilder (value, value.Length + 10);
893                         if (attr)
894                                 sb.Replace ("\"", "&quot;");
895                         sb.Replace ("<", "&lt;");
896                         sb.Replace (">", "&gt;");
897                         if (attr) {
898                                 sb.Replace ("\r\n", "&#10;");
899                                 sb.Replace ("\r", "&#10;");
900                                 sb.Replace ("\n", "&#10;");
901                         }
902                         return sb.ToString ();
903                 }
904
905                 public virtual string InnerXml {
906                         get {
907                                 switch (NodeType) {
908                                 case XPathNodeType.Element:
909                                 case XPathNodeType.Root:
910                                         break;
911                                 case XPathNodeType.Attribute:
912                                 case XPathNodeType.Namespace:
913                                         return EscapeString (Value, true);
914                                 case XPathNodeType.Text:
915                                 case XPathNodeType.Whitespace:
916                                 case XPathNodeType.SignificantWhitespace:
917                                         return String.Empty;
918                                 case XPathNodeType.ProcessingInstruction:
919                                 case XPathNodeType.Comment:
920                                         return Value;
921                                 }
922
923                                 XmlReader r = ReadSubtree ();
924                                 r.Read (); // start
925                                 // skip the element itself (or will reach to 
926                                 // EOF if other than element) unless writing
927                                 // doc itself
928                                 int depth = r.Depth;
929                                 if (NodeType != XPathNodeType.Root)
930                                         r.Read ();
931                                 else
932                                         depth = -1; // for Root, it should consume the entire tree, so no depth check is done.
933                                 StringWriter sw = new StringWriter ();
934                                 XmlWriterSettings s = new XmlWriterSettings ();
935                                 s.Indent = true;
936                                 s.ConformanceLevel = ConformanceLevel.Fragment;
937                                 s.OmitXmlDeclaration = true;
938                                 XmlWriter xtw = XmlWriter.Create (sw, s);
939                                 while (!r.EOF && r.Depth > depth)
940                                         xtw.WriteNode (r, false);
941                                 return sw.ToString ();
942                         }
943                         set {
944                                 DeleteChildren ();
945                                 if (NodeType == XPathNodeType.Attribute) {
946                                         SetValue (value);
947                                         return;
948                                 }
949                                 AppendChild (value);
950                         }
951                 }
952
953                 public override sealed bool IsNode {
954                         get { return true; }
955                 }
956
957                 public virtual string OuterXml {
958                         get {
959                                 switch (NodeType) {
960                                 case XPathNodeType.Attribute:
961                                         return String.Concat (
962                                                 Prefix,
963                                                 Prefix.Length > 0 ? ":" : String.Empty,
964                                                 LocalName,
965                                                 "=\"",
966                                                 EscapeString (Value, true),
967                                                 "\"");
968                                         break;
969                                 case XPathNodeType.Namespace:
970                                         return String.Concat (
971                                                 "xmlns",
972                                                 LocalName.Length > 0 ? ":" : String.Empty,
973                                                 LocalName,
974                                                 "=\"",
975                                                 EscapeString (Value, true),
976                                                 "\"");
977                                         break;
978                                 case XPathNodeType.Text:
979                                         return EscapeString (Value, false);
980                                 case XPathNodeType.Whitespace:
981                                 case XPathNodeType.SignificantWhitespace:
982                                         return Value;
983                                 }
984
985                                 XmlWriterSettings s = new XmlWriterSettings ();
986                                 s.Indent = true;
987                                 s.OmitXmlDeclaration = true;
988                                 s.ConformanceLevel = ConformanceLevel.Fragment;
989                                 StringBuilder sb = new StringBuilder ();
990                                 using (XmlWriter w = XmlWriter.Create (sb, s)) {
991                                         WriteSubtree (w);
992                                 }
993                                 return sb.ToString ();
994                         }
995                         set {
996                                 switch (NodeType) {
997                                 case XPathNodeType.Root:
998                                 case XPathNodeType.Attribute:
999                                 case XPathNodeType.Namespace:
1000                                         throw new XmlException ("Setting OuterXml Root, Attribute and Namespace is not supported.");
1001                                 }
1002
1003                                 DeleteSelf ();
1004                                 AppendChild (value);
1005                                 MoveToFirstChild ();
1006                         }
1007                 }
1008
1009                 public virtual IXmlSchemaInfo SchemaInfo {
1010                         get {
1011                                 return null;
1012                         }
1013                 }
1014
1015                 public override object TypedValue {
1016                         get {
1017                                 switch (NodeType) {
1018                                 case XPathNodeType.Element:
1019                                 case XPathNodeType.Attribute:
1020                                         if (XmlType == null)
1021                                                 break;
1022                                         XmlSchemaDatatype dt = XmlType.Datatype;
1023                                         if (dt == null)
1024                                                 break;
1025                                         return dt.ParseValue (Value, NameTable, this as IXmlNamespaceResolver);
1026                                 }
1027                                 return Value;
1028                         }
1029                 }
1030
1031                 public virtual object UnderlyingObject {
1032                         get { return null; }
1033                 }
1034
1035                 public override bool ValueAsBoolean {
1036                         get { return XQueryConvert.StringToBoolean (Value); }
1037                 }
1038
1039                 public override DateTime ValueAsDateTime {
1040                         get { return XmlConvert.ToDateTime (Value); }
1041                 }
1042
1043                 public override double ValueAsDouble {
1044                         get { return XQueryConvert.StringToDouble (Value); }
1045                 }
1046
1047                 public override int ValueAsInt {
1048                         get { return XQueryConvert.StringToInt (Value); }
1049                 }
1050
1051                 public override long ValueAsLong {
1052                         get { return XQueryConvert.StringToInteger (Value); }
1053                 }
1054
1055                 public override Type ValueType {
1056                         get {
1057                                 return SchemaInfo != null &&
1058                                         SchemaInfo.SchemaType != null &&
1059                                         SchemaInfo.SchemaType.Datatype != null ?
1060                                         SchemaInfo.SchemaType.Datatype.ValueType
1061                                         : null;
1062                         }
1063                 }
1064
1065                 public override XmlSchemaType XmlType {
1066                         get {
1067                                 if (SchemaInfo != null)
1068                                         return SchemaInfo.SchemaType;
1069                                 return null;
1070                         }
1071                 }
1072
1073                 private XmlReader CreateFragmentReader (string fragment)
1074                 {
1075                         XmlReaderSettings settings = new XmlReaderSettings ();
1076                         settings.ConformanceLevel = ConformanceLevel.Fragment;
1077                         XmlNamespaceManager nsmgr = new XmlNamespaceManager (NameTable);
1078                         foreach (KeyValuePair<string,string> nss in GetNamespacesInScope (XmlNamespaceScope.All))
1079                                 nsmgr.AddNamespace (nss.Key, nss.Value);
1080                         return XmlReader.Create (
1081                                 new StringReader (fragment),
1082                                 settings,
1083                                 new XmlParserContext (NameTable, nsmgr, null, XmlSpace.None));
1084                 }
1085
1086                 // must override it.
1087                 public virtual XmlWriter AppendChild ()
1088                 {
1089                         throw new NotSupportedException ();
1090                 }
1091
1092                 public virtual void AppendChild (
1093                         string xmlFragments)
1094                 {
1095                         AppendChild (CreateFragmentReader (xmlFragments));
1096                 }
1097
1098                 public virtual void AppendChild (
1099                         XmlReader reader)
1100                 {
1101                         XmlWriter w = AppendChild ();
1102                         while (!reader.EOF)
1103                                 w.WriteNode (reader, false);
1104                         w.Close ();
1105                 }
1106
1107                 public virtual void AppendChild (
1108                         XPathNavigator nav)
1109                 {
1110                         AppendChild (new XPathNavigatorReader (nav));
1111                 }
1112
1113                 public virtual void AppendChildElement (string prefix, string name, string ns, string value)
1114                 {
1115                         XmlWriter xw = AppendChild ();
1116                         xw.WriteStartElement (prefix, name, ns);
1117                         xw.WriteString (value);
1118                         xw.WriteEndElement ();
1119                         xw.Close ();
1120                 }
1121
1122                 public virtual void CreateAttribute (string prefix, string localName, string namespaceURI, string value)
1123                 {
1124                         using (XmlWriter w = CreateAttributes ()) {
1125                                 w.WriteAttributeString (prefix, localName, namespaceURI, value);
1126                         }
1127                 }
1128
1129                 // must override it.
1130                 public virtual XmlWriter CreateAttributes ()
1131                 {
1132                         throw new NotSupportedException ();
1133                 }
1134
1135                 // must override it.
1136                 public virtual void DeleteSelf ()
1137                 {
1138                         throw new NotSupportedException ();
1139                 }
1140
1141                 // must override it.
1142                 public virtual void DeleteRange (XPathNavigator nav)
1143                 {
1144                         throw new NotSupportedException ();
1145                 }
1146
1147                 public virtual XmlWriter ReplaceRange (XPathNavigator nav)
1148                 {
1149                         throw new NotSupportedException ();
1150                 }
1151         
1152                 public virtual XmlWriter InsertAfter ()
1153                 {
1154                         switch (NodeType) {
1155                         case XPathNodeType.Root:
1156                         case XPathNodeType.Attribute:
1157                         case XPathNodeType.Namespace:
1158                                 throw new InvalidOperationException (String.Format ("Insertion after {0} is not allowed.", NodeType));
1159                         }
1160                         XPathNavigator nav = Clone ();
1161                         if (nav.MoveToNext ())
1162                                 return nav.InsertBefore ();
1163                         else if (nav.MoveToParent ())
1164                                 return nav.AppendChild ();
1165                         else
1166                                 throw new InvalidOperationException ("Could not move to parent to insert sibling node");
1167                 }
1168
1169                 public virtual void InsertAfter (string xmlFragments)
1170                 {
1171                         InsertAfter (CreateFragmentReader (xmlFragments));
1172                 }
1173
1174                 public virtual void InsertAfter (XmlReader reader)
1175                 {
1176                         using (XmlWriter w = InsertAfter ()) {
1177                                 w.WriteNode (reader, false);
1178                         }
1179                 }
1180
1181                 public virtual void InsertAfter (XPathNavigator nav)
1182                 {
1183                         InsertAfter (new XPathNavigatorReader (nav));
1184                 }
1185
1186                 public virtual XmlWriter InsertBefore ()
1187                 {
1188                         throw new NotSupportedException ();
1189                 }
1190
1191                 public virtual void InsertBefore (string xmlFragments)
1192                 {
1193                         InsertBefore (CreateFragmentReader (xmlFragments));
1194                 }
1195
1196                 public virtual void InsertBefore (XmlReader reader)
1197                 {
1198                         using (XmlWriter w = InsertBefore ()) {
1199                                 w.WriteNode (reader, false);
1200                         }
1201                 }
1202
1203                 public virtual void InsertBefore (XPathNavigator nav)
1204                 {
1205                         InsertBefore (new XPathNavigatorReader (nav));
1206                 }
1207
1208                 public virtual void InsertElementAfter (string prefix, 
1209                         string localName, string namespaceURI, string value)
1210                 {
1211                         using (XmlWriter w = InsertAfter ()) {
1212                                 w.WriteElementString (prefix, localName, namespaceURI, value);
1213                         }
1214                 }
1215
1216                 public virtual void InsertElementBefore (string prefix, 
1217                         string localName, string namespaceURI, string value)
1218                 {
1219                         using (XmlWriter w = InsertBefore ()) {
1220                                 w.WriteElementString (prefix, localName, namespaceURI, value);
1221                         }
1222                 }
1223
1224                 public virtual XmlWriter PrependChild ()
1225                 {
1226                         XPathNavigator nav = Clone ();
1227                         if (nav.MoveToFirstChild ())
1228                                 return nav.InsertBefore ();
1229                         else
1230                                 return AppendChild ();
1231                 }
1232
1233                 public virtual void PrependChild (string xmlFragments)
1234                 {
1235                         PrependChild (CreateFragmentReader (xmlFragments));
1236                 }
1237
1238                 public virtual void PrependChild (XmlReader reader)
1239                 {
1240                         using (XmlWriter w = PrependChild ()) {
1241                                 w.WriteNode (reader, false);
1242                         }
1243                 }
1244
1245                 public virtual void PrependChild (XPathNavigator nav)
1246                 {
1247                         PrependChild (new XPathNavigatorReader (nav));
1248                 }
1249
1250                 public virtual void PrependChildElement (string prefix, 
1251                         string localName, string namespaceURI, string value)
1252                 {
1253                         using (XmlWriter w = PrependChild ()) {
1254                                 w.WriteElementString (prefix, localName, namespaceURI, value);
1255                         }
1256                 }
1257
1258                 public virtual void ReplaceSelf (string xmlFragment)
1259                 {
1260                         ReplaceSelf (CreateFragmentReader (xmlFragment));
1261                 }
1262
1263                 // must override it.
1264                 public virtual void ReplaceSelf (XmlReader reader)
1265                 {
1266                         throw new NotSupportedException ();
1267                 }
1268
1269                 public virtual void ReplaceSelf (XPathNavigator navigator)
1270                 {
1271                         ReplaceSelf (new XPathNavigatorReader (navigator));
1272                 }
1273
1274                 // Dunno the exact purpose, but maybe internal editor use
1275                 [MonoTODO]
1276                 public virtual void SetTypedValue (object value)
1277                 {
1278                         throw new NotSupportedException ();
1279                 }
1280
1281                 public virtual void SetValue (string value)
1282                 {
1283                         throw new NotSupportedException ();
1284                 }
1285
1286                 private void DeleteChildren ()
1287                 {
1288                         switch (NodeType) {
1289                         case XPathNodeType.Namespace:
1290                                 throw new InvalidOperationException ("Removing namespace node content is not supported.");
1291                         case XPathNodeType.Attribute:
1292                                 return;
1293                         case XPathNodeType.Text:
1294                         case XPathNodeType.SignificantWhitespace:
1295                         case XPathNodeType.Whitespace:
1296                         case XPathNodeType.ProcessingInstruction:
1297                         case XPathNodeType.Comment:
1298                                 DeleteSelf ();
1299                                 return;
1300                         }
1301                         if (!HasChildren)
1302                                 return;
1303                         XPathNavigator nav = Clone ();
1304                         nav.MoveToFirstChild ();
1305                         while (!nav.IsSamePosition (this))
1306                                 nav.DeleteSelf ();
1307                 }
1308 #endif
1309         }
1310 }