* BuildEngine.cs (BuildProjectFile): Use AddProperty method to specify
[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 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;
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                 class EnumerableIterator : XPathNodeIterator
61                 {
62                         IEnumerable source;
63                         IEnumerator e;
64                         int pos;
65
66                         public EnumerableIterator (IEnumerable source, int pos)
67                         {
68                                 this.source = source;
69                                 for (int i = 0; i < pos; i++)
70                                         MoveNext ();
71                         }
72
73                         public override XPathNodeIterator Clone ()
74                         {
75                                 return new EnumerableIterator (source, pos);
76                         }
77
78                         public override bool MoveNext ()
79                         {
80                                 if (e == null)
81                                         e = source.GetEnumerator ();
82                                 if (!e.MoveNext ())
83                                         return false;
84                                 pos++;
85                                 return true;
86                         }
87
88                         public override int CurrentPosition {
89                                 get { return pos; }
90                         }
91
92                         public override XPathNavigator Current {
93                                 get { return pos == 0 ? null : (XPathNavigator) e.Current; }
94                         }
95                 }
96
97                 #region Static members
98 #if NET_2_0
99                 public static IEqualityComparer NavigatorComparer {
100                         get { return XPathNavigatorComparer.Instance; }
101                 }
102 #endif
103                 #endregion
104
105                 #region Constructor
106
107                 protected XPathNavigator ()
108                 {
109                 }
110
111                 #endregion
112
113                 #region Properties
114
115                 public abstract string BaseURI { get; }
116
117 #if NET_2_0
118                 public virtual bool CanEdit {
119                         get { return false; }
120                 }
121
122                 public virtual bool HasAttributes {
123                         get {
124                                 if (!MoveToFirstAttribute ())
125                                         return false;
126                                 MoveToParent ();
127                                 return true;
128                         }
129                 }
130
131                 public virtual bool HasChildren {
132                         get {
133                                 if (!MoveToFirstChild ())
134                                         return false;
135                                 MoveToParent ();
136                                 return true;
137                         }
138                 }
139 #else
140                 public abstract bool HasAttributes { get; }
141
142                 public abstract bool HasChildren { get; }
143 #endif
144
145                 public abstract bool IsEmptyElement { get; }
146
147                 public abstract string LocalName { get; }
148
149                 public abstract string Name { get; }
150
151                 public abstract string NamespaceURI { get; }
152
153                 public abstract XmlNameTable NameTable { get; }
154
155                 public abstract XPathNodeType NodeType { get; }
156
157                 public abstract string Prefix { get; }
158
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;
168                                 }
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;
174                         }
175                 }
176 #else
177                 public abstract string Value { get; }
178
179                 public abstract string XmlLang { get; }
180 #endif
181
182                 #endregion
183
184                 #region Methods
185
186                 public abstract XPathNavigator Clone ();
187
188                 public virtual XmlNodeOrder ComparePosition (XPathNavigator nav)
189                 {
190                         if (IsSamePosition (nav))
191                                 return XmlNodeOrder.Same;
192
193                         // quick check for direct descendant
194                         if (IsDescendant (nav))
195                                 return XmlNodeOrder.Before;
196
197                         // quick check for direct ancestor
198                         if (nav.IsDescendant (this))
199                                 return XmlNodeOrder.After;
200
201                         XPathNavigator nav1 = Clone ();
202                         XPathNavigator nav2 = nav.Clone ();
203
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);
211
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);
220
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--;
231                         }
232
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 ();
241
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;
252                         }
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;
262                         }
263                         while (nav1.MoveToNext ())
264                                 if (nav1.IsSamePosition (nav2))
265                                         return XmlNodeOrder.Before;
266                         return XmlNodeOrder.After;
267                 }
268
269                 public virtual XPathExpression Compile (string xpath)
270                 {
271                         return XPathExpression.Compile (xpath);
272                 }
273                 
274                 internal virtual XPathExpression Compile (string xpath, System.Xml.Xsl.IStaticXsltContext ctx)
275                 {
276                         return XPathExpression.Compile (xpath, null, ctx);
277                 }
278
279                 public virtual object Evaluate (string xpath)
280                 {
281                         return Evaluate (Compile (xpath));
282                 }
283
284                 public virtual object Evaluate (XPathExpression expr)
285                 {
286                         return Evaluate (expr, null);
287                 }
288
289                 public virtual object Evaluate (XPathExpression expr, XPathNodeIterator context)
290                 {
291                         return Evaluate (expr, context, null);
292                 }
293
294                 BaseIterator ToBaseIterator (XPathNodeIterator iter, NSResolver ctx)
295                 {
296                         BaseIterator i = iter as BaseIterator;
297                         if (i == null)
298                                 i = new WrapperIterator (iter, ctx);
299                         return i;
300                 }
301
302                 object Evaluate (XPathExpression expr, XPathNodeIterator context, NSResolver ctx)
303                 {
304                         CompiledExpression cexpr = (CompiledExpression) expr;
305                         if (ctx == null)
306                                 ctx = cexpr.NamespaceManager;
307                         
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);
313                 }
314
315                 internal XPathNodeIterator EvaluateNodeSet (XPathExpression expr, XPathNodeIterator context, NSResolver ctx)
316                 {
317                         CompiledExpression cexpr = (CompiledExpression) expr;
318                         if (ctx == null)
319                                 ctx = cexpr.NamespaceManager;
320                         
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);
326                 }
327
328                 internal string EvaluateString (XPathExpression expr, XPathNodeIterator context, NSResolver ctx)
329                 {
330                         CompiledExpression cexpr = (CompiledExpression) expr;
331                         if (ctx == null)
332                                 ctx = cexpr.NamespaceManager;
333                         
334                         if (context == null)
335                                 context = new NullIterator (this, cexpr.NamespaceManager);
336                         BaseIterator iterContext = ToBaseIterator (context, ctx);
337                         return cexpr.EvaluateString (iterContext);
338                 }
339
340                 internal double EvaluateNumber (XPathExpression expr, XPathNodeIterator context, NSResolver ctx)
341                 {
342                         CompiledExpression cexpr = (CompiledExpression) expr;
343                         if (ctx == null)
344                                 ctx = cexpr.NamespaceManager;
345                         
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);
351                 }
352
353                 internal bool EvaluateBoolean (XPathExpression expr, XPathNodeIterator context, NSResolver ctx)
354                 {
355                         CompiledExpression cexpr = (CompiledExpression) expr;
356                         if (ctx == null)
357                                 ctx = cexpr.NamespaceManager;
358                         
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);
364                 }
365
366 #if NET_2_0
367                 public virtual string GetAttribute (string localName, string namespaceURI)
368                 {
369                         if (!MoveToAttribute (localName, namespaceURI))
370                                 return String.Empty;
371                         string value = Value;
372                         MoveToParent ();
373                         return value;
374                 }
375
376                 public virtual string GetNamespace (string name)
377                 {
378                         if (!MoveToNamespace (name))
379                                 return String.Empty;
380                         string value = Value;
381                         MoveToParent ();
382                         return value;
383                 }
384
385 #else
386                 public abstract string GetAttribute (string localName, string namespaceURI);
387
388                 public abstract string GetNamespace (string name);
389 #endif
390                 
391                 object ICloneable.Clone ()
392                 {
393                         return Clone ();
394                 }
395
396                 public virtual bool IsDescendant (XPathNavigator nav)
397                 {
398                         if (nav != null)
399                         {
400                                 nav = nav.Clone ();
401                                 while (nav.MoveToParent ())
402                                 {
403                                         if (IsSamePosition (nav))
404                                                 return true;
405                                 }
406                         }
407                         return false;
408                 }
409
410                 public abstract bool IsSamePosition (XPathNavigator other);
411
412                 public virtual bool Matches (string xpath)
413                 {
414                         return Matches (Compile (xpath));
415                 }
416
417                 public virtual bool Matches (XPathExpression expr)
418                 {
419                         Expression e = ((CompiledExpression) expr).ExpressionNode;
420                         if (e is ExprRoot)
421                                 return NodeType == XPathNodeType.Root;
422                         
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.");
431                                 }
432                                 return nt.Match (((CompiledExpression)expr).NamespaceManager, this);
433                         }
434                         if (e is ExprFilter) {
435                                 do {
436                                         e = ((ExprFilter) e).LeftHandSide;
437                                 } while (e is ExprFilter);
438                                 
439                                 if (e is NodeTest && !((NodeTest) e).Match (((CompiledExpression) expr).NamespaceManager, this))
440                                         return false;
441                         }
442
443                         XPathResultType resultType = e.ReturnType;
444                         switch (resultType) {
445                         case XPathResultType.Any:
446                         case XPathResultType.NodeSet:
447                                 break;
448                         default:
449                                 return false;
450                         }
451
452                         switch (e.EvaluatedNodeType) {
453                         case XPathNodeType.Attribute:
454                         case XPathNodeType.Namespace:
455                                 if (NodeType != e.EvaluatedNodeType)
456                                         return false;
457                                 break;
458                         }
459
460                         XPathNodeIterator nodes;
461                         nodes = this.Select (expr);
462                         while (nodes.MoveNext ()) {
463                                 if (IsSamePosition (nodes.Current))
464                                         return true;
465                         }
466
467                         // ancestors might select this node.
468
469                         XPathNavigator navigator = Clone ();
470
471                         while (navigator.MoveToParent ()) {
472                                 nodes = navigator.Select (expr);
473
474                                 while (nodes.MoveNext ()) {
475                                         if (IsSamePosition (nodes.Current))
476                                                 return true;
477                                 }
478                         }
479
480                         return false;
481                 }
482
483                 public abstract bool MoveTo (XPathNavigator other);
484
485 #if NET_2_0
486                 public virtual bool MoveToAttribute (string localName, string namespaceURI)
487                 {
488                         if (MoveToFirstAttribute ()) {
489                                 do {
490                                         if (LocalName == localName && NamespaceURI == namespaceURI)
491                                                 return true;
492                                 } while (MoveToNextAttribute ());
493                                 MoveToParent ();
494                         }
495                         return false;
496                 }
497
498                 public virtual bool MoveToNamespace (string name)
499                 {
500                         if (MoveToFirstNamespace ()) {
501                                 do {
502                                         if (LocalName == name)
503                                                 return true;
504                                 } while (MoveToNextNamespace ());
505                                 MoveToParent ();
506                         }
507                         return false;
508                 }
509
510                 /*
511                 public virtual bool MoveToFirst ()
512                 {
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;
518                         }
519                         return false;
520                 }
521                 */
522
523                 public virtual bool MoveToFirst ()
524                 {
525                         return MoveToFirstImpl ();
526                 }
527
528                 public virtual void MoveToRoot ()
529                 {
530                         while (MoveToParent ())
531                                 ;
532                 }
533 #else
534                 public abstract bool MoveToAttribute (string localName, string namespaceURI);
535
536                 public abstract bool MoveToNamespace (string name);
537
538                 public abstract bool MoveToFirst ();
539
540                 public abstract void MoveToRoot ();
541 #endif
542
543                 internal bool MoveToFirstImpl ()
544                 {
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;
556                         }
557                 }
558
559                 public abstract bool MoveToFirstAttribute ();
560
561                 public abstract bool MoveToFirstChild ();
562
563                 public bool MoveToFirstNamespace ()
564                 {
565                         return MoveToFirstNamespace (XPathNamespaceScope.All);
566                 }
567
568                 public abstract bool MoveToFirstNamespace (XPathNamespaceScope namespaceScope);
569
570                 public abstract bool MoveToId (string id);
571
572                 public abstract bool MoveToNext ();
573
574                 public abstract bool MoveToNextAttribute ();
575
576                 public bool MoveToNextNamespace ()
577                 {
578                         return MoveToNextNamespace (XPathNamespaceScope.All);
579                 }
580
581                 public abstract bool MoveToNextNamespace (XPathNamespaceScope namespaceScope);
582
583                 public abstract bool MoveToParent ();
584
585                 public abstract bool MoveToPrevious ();
586
587                 public virtual XPathNodeIterator Select (string xpath)
588                 {
589                         return Select (Compile (xpath));
590                 }
591
592                 public virtual XPathNodeIterator Select (XPathExpression expr)
593                 {
594                         return Select (expr, null);
595                 }
596                 
597                 internal XPathNodeIterator Select (XPathExpression expr, NSResolver ctx)
598                 {
599                         CompiledExpression cexpr = (CompiledExpression) expr;
600                         if (ctx == null)
601                                 ctx = cexpr.NamespaceManager;
602                         
603                         BaseIterator iter = new NullIterator (this, ctx);
604                         return cexpr.EvaluateNodeSet (iter);
605                 }
606
607                 public virtual XPathNodeIterator SelectAncestors (XPathNodeType type, bool matchSelf)
608                 {
609                         Axes axis = (matchSelf) ? Axes.AncestorOrSelf : Axes.Ancestor;
610                         return SelectTest (new NodeTypeTest (axis, type));
611                 }
612
613                 public virtual XPathNodeIterator SelectAncestors (string name, string namespaceURI, bool matchSelf)
614                 {
615                         if (name == null)
616                                 throw new ArgumentNullException ("name");
617                         if (namespaceURI == null)
618                                 throw new ArgumentNullException ("namespaceURI");
619
620                         Axes axis = (matchSelf) ? Axes.AncestorOrSelf : Axes.Ancestor;
621                         XmlQualifiedName qname = new XmlQualifiedName (name, namespaceURI);
622                         return SelectTest (new NodeNameTest (axis, qname, true));
623                 }
624
625                 static IEnumerable EnumerateChildren (XPathNavigator n, XPathNodeType type)
626                 {
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;
640                                 }
641                         } while (nav.MoveToNext ());
642                 }
643
644                 public virtual XPathNodeIterator SelectChildren (XPathNodeType type)
645                 {
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
653                 }
654
655                 static IEnumerable EnumerateChildren (XPathNavigator n, string name, string ns)
656                 {
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;
667                                 }
668                         } while (nav.MoveToNext ());
669                 }
670
671                 public virtual XPathNodeIterator SelectChildren (string name, string namespaceURI)
672                 {
673                         if (name == null)
674                                 throw new ArgumentNullException ("name");
675                         if (namespaceURI == null)
676                                 throw new ArgumentNullException ("namespaceURI");
677
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
685                 }
686
687                 public virtual XPathNodeIterator SelectDescendants (XPathNodeType type, bool matchSelf)
688                 {
689                         Axes axis = (matchSelf) ? Axes.DescendantOrSelf : Axes.Descendant;
690                         return SelectTest (new NodeTypeTest (axis, type));
691                 }
692
693                 public virtual XPathNodeIterator SelectDescendants (string name, string namespaceURI, bool matchSelf)
694                 {
695                         if (name == null)
696                                 throw new ArgumentNullException ("name");
697                         if (namespaceURI == null)
698                                 throw new ArgumentNullException ("namespaceURI");
699
700
701                         Axes axis = (matchSelf) ? Axes.DescendantOrSelf : Axes.Descendant;
702                         XmlQualifiedName qname = new XmlQualifiedName (name, namespaceURI);
703                         return SelectTest (new NodeNameTest (axis, qname, true));
704                 }
705
706                 internal XPathNodeIterator SelectTest (NodeTest test)
707                 {
708                         return test.EvaluateNodeSet (new NullIterator (this));
709                 }
710
711                 public override string ToString ()
712                 {
713                         return Value;
714                 }
715
716                 #endregion
717
718 #if NET_2_0
719
720                 public virtual bool CheckValidity (XmlSchemaSet schemas, ValidationEventHandler handler)
721                 {
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;
734                         }
735                         return true;
736                 }
737
738                 public virtual XPathNavigator CreateNavigator ()
739                 {
740                         return Clone ();
741                 }
742
743                 public virtual object Evaluate (string xpath, IXmlNamespaceResolver nsResolver)
744                 {
745                         return Evaluate (Compile (xpath), null, nsResolver);
746                 }
747
748                 public virtual IDictionary<string, string> GetNamespacesInScope (XmlNamespaceScope scope)
749                 {
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;
766                 }
767 #endif
768
769 #if NET_2_0
770                 public
771 #else
772                 internal
773 #endif
774                 virtual string LookupNamespace (string prefix)
775                 {
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;
782                 }
783
784 #if NET_2_0
785                 public
786 #else
787                 internal
788 #endif
789                 virtual string LookupPrefix (string namespaceUri)
790                 {
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;
801                 }
802
803                 private bool MoveTo (XPathNodeIterator iter)
804                 {
805                         if (iter.MoveNext ()) {
806                                 MoveTo (iter.Current);
807                                 return true;
808                         }
809                         else
810                                 return false;
811                 }
812
813 #if NET_2_0
814                 public
815 #else
816                 internal
817 #endif
818                 virtual bool MoveToChild (XPathNodeType type)
819                 {
820                         return MoveTo (SelectChildren (type));
821                 }
822
823 #if NET_2_0
824                 public
825 #else
826                 internal
827 #endif
828                 virtual bool MoveToChild (string localName, string namespaceURI)
829                 {
830                         return MoveTo (SelectChildren (localName, namespaceURI));
831                 }
832
833 #if NET_2_0
834                 public
835 #else
836                 internal
837 #endif
838                 virtual bool MoveToNext (string localName, string namespaceURI)
839                 {
840                         XPathNavigator nav = Clone ();
841                         while (nav.MoveToNext ()) {
842                                 if (nav.LocalName == localName &&
843                                         nav.NamespaceURI == namespaceURI) {
844                                         MoveTo (nav);
845                                         return true;
846                                 }
847                         }
848                         return false;
849                 }
850
851 #if NET_2_0
852                 public
853 #else
854                 internal
855 #endif
856                 virtual bool MoveToNext (XPathNodeType type)
857                 {
858                         XPathNavigator nav = Clone ();
859                         while (nav.MoveToNext ()) {
860                                 if (type == XPathNodeType.All || nav.NodeType == type) {
861                                         MoveTo (nav);
862                                         return true;
863                                 }
864                         }
865                         return false;
866                 }
867
868 #if NET_2_0
869                 public
870 #else
871                 internal
872 #endif
873                 virtual bool MoveToFollowing (string localName,
874                         string namespaceURI)
875                 {
876                         return MoveToFollowing (localName, namespaceURI, null);
877                 }
878
879 #if NET_2_0
880                 public
881 #else
882                 internal
883 #endif
884                 virtual bool MoveToFollowing (string localName,
885                         string namespaceURI, XPathNavigator end)
886                 {
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;
897
898                         XPathNavigator nav = Clone ();
899                         switch (nav.NodeType) {
900                         case XPathNodeType.Attribute:
901                         case XPathNodeType.Namespace:
902                                 nav.MoveToParent ();
903                                 break;
904                         }
905                         do {
906                                 if (!nav.MoveToFirstChild ()) {
907                                         do {
908                                                 if (!nav.MoveToNext ()) {
909                                                         if (!nav.MoveToParent ())
910                                                                 return false;
911                                                 }
912                                                 else
913                                                         break;
914                                         } while (true);
915                                 }
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;
922                                 }
923                         } while (true);
924                 }
925
926 #if NET_2_0
927                 public
928 #else
929                 internal
930 #endif
931                 virtual bool MoveToFollowing (XPathNodeType type)
932                 {
933                         return MoveToFollowing (type, null);
934                 }
935
936 #if NET_2_0
937                 public
938 #else
939                 internal
940 #endif
941                 virtual bool MoveToFollowing (XPathNodeType type,
942                         XPathNavigator end)
943                 {
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;
952                         }
953                         do {
954                                 if (!nav.MoveToFirstChild ()) {
955                                         do {
956                                                 if (!nav.MoveToNext ()) {
957                                                         if (!nav.MoveToParent ())
958                                                                 return false;
959                                                 }
960                                                 else
961                                                         break;
962                                         } while (true);
963                                 }
964                                 if (end != null && end.IsSamePosition (nav))
965                                         return false;
966                                 if (type == XPathNodeType.All || nav.NodeType == type) {
967                                         MoveTo (nav);
968                                         return true;
969                                 }
970                         } while (true);
971                 }
972
973 #if NET_2_0
974                 public virtual XmlReader ReadSubtree ()
975                 {
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));
982                         }
983                 }
984
985                 public virtual XPathNodeIterator Select (string xpath, IXmlNamespaceResolver nsResolver)
986                 {
987                         return Select (Compile (xpath), nsResolver);
988                 }
989
990                 public virtual XPathNavigator SelectSingleNode (string xpath)
991                 {
992                         return SelectSingleNode (xpath, null);
993                 }
994
995                 public virtual XPathNavigator SelectSingleNode (string xpath, IXmlNamespaceResolver nsResolver)
996                 {
997                         XPathExpression expr = Compile (xpath);
998                         expr.SetContext (nsResolver);
999                         return SelectSingleNode (expr);
1000                 }
1001
1002                 public virtual XPathNavigator SelectSingleNode (XPathExpression expression)
1003                 {
1004                         XPathNodeIterator iter = Select (expression);
1005                         if (iter.MoveNext ())
1006                                 return iter.Current;
1007                         else
1008                                 return null;
1009                 }
1010
1011                 // it is not very effective code but should just work
1012                 public override object ValueAs (Type type, IXmlNamespaceResolver nsResolver)
1013                 {
1014                         return new XmlAtomicValue (Value, XmlSchemaSimpleType.XsString).ValueAs (type, nsResolver);
1015                 }
1016
1017                 public virtual void WriteSubtree (XmlWriter writer)
1018                 {
1019                         writer.WriteNode (this, false);
1020                 }
1021
1022                 static readonly char [] escape_text_chars =
1023                                 new char [] {'&', '<', '>'};
1024                 static readonly char [] escape_attr_chars =
1025                                 new char [] {'"', '&', '<', '>', '\r', '\n'};
1026
1027                 static string EscapeString (string value, bool attr)
1028                 {
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;");
1042                         }
1043                         return sb.ToString ();
1044                 }
1045
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;
1062                                 }
1063
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 ();
1083                         }
1084                         set {
1085                                 DeleteChildren ();
1086                                 if (NodeType == XPathNodeType.Attribute) {
1087                                         SetValue (value);
1088                                         return;
1089                                 }
1090                                 AppendChild (value);
1091                         }
1092                 }
1093
1094                 public override sealed bool IsNode {
1095                         get { return true; }
1096                 }
1097
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;
1122                                 }
1123
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);
1131                                 }
1132                                 return sb.ToString ();
1133                         }
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.");
1140                                 }
1141
1142                                 DeleteSelf ();
1143                                 AppendChild (value);
1144                                 MoveToFirstChild ();
1145                         }
1146                 }
1147
1148                 public virtual IXmlSchemaInfo SchemaInfo {
1149                         get {
1150                                 return null;
1151                         }
1152                 }
1153
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);
1165                                 }
1166                                 return Value;
1167                         }
1168                 }
1169
1170                 public virtual object UnderlyingObject {
1171                         get { return null; }
1172                 }
1173
1174                 public override bool ValueAsBoolean {
1175                         get { return XQueryConvert.StringToBoolean (Value); }
1176                 }
1177
1178                 public override DateTime ValueAsDateTime {
1179                         get { return XmlConvert.ToDateTime (Value); }
1180                 }
1181
1182                 public override double ValueAsDouble {
1183                         get { return XQueryConvert.StringToDouble (Value); }
1184                 }
1185
1186                 public override int ValueAsInt {
1187                         get { return XQueryConvert.StringToInt (Value); }
1188                 }
1189
1190                 public override long ValueAsLong {
1191                         get { return XQueryConvert.StringToInteger (Value); }
1192                 }
1193
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;
1201                         }
1202                 }
1203
1204                 public override XmlSchemaType XmlType {
1205                         get {
1206                                 if (SchemaInfo != null)
1207                                         return SchemaInfo.SchemaType;
1208                                 return null;
1209                         }
1210                 }
1211
1212                 private XmlReader CreateFragmentReader (string fragment)
1213                 {
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));
1223                 }
1224
1225                 // must override it.
1226                 public virtual XmlWriter AppendChild ()
1227                 {
1228                         throw new NotSupportedException ();
1229                 }
1230
1231                 public virtual void AppendChild (
1232                         string xmlFragments)
1233                 {
1234                         AppendChild (CreateFragmentReader (xmlFragments));
1235                 }
1236
1237                 public virtual void AppendChild (
1238                         XmlReader reader)
1239                 {
1240                         XmlWriter w = AppendChild ();
1241                         while (!reader.EOF)
1242                                 w.WriteNode (reader, false);
1243                         w.Close ();
1244                 }
1245
1246                 public virtual void AppendChild (
1247                         XPathNavigator nav)
1248                 {
1249                         AppendChild (new XPathNavigatorReader (nav));
1250                 }
1251
1252                 public virtual void AppendChildElement (string prefix, string name, string ns, string value)
1253                 {
1254                         XmlWriter xw = AppendChild ();
1255                         xw.WriteStartElement (prefix, name, ns);
1256                         xw.WriteString (value);
1257                         xw.WriteEndElement ();
1258                         xw.Close ();
1259                 }
1260
1261                 public virtual void CreateAttribute (string prefix, string localName, string namespaceURI, string value)
1262                 {
1263                         using (XmlWriter w = CreateAttributes ()) {
1264                                 w.WriteAttributeString (prefix, localName, namespaceURI, value);
1265                         }
1266                 }
1267
1268                 // must override it.
1269                 public virtual XmlWriter CreateAttributes ()
1270                 {
1271                         throw new NotSupportedException ();
1272                 }
1273
1274                 // must override it.
1275                 public virtual void DeleteSelf ()
1276                 {
1277                         throw new NotSupportedException ();
1278                 }
1279
1280                 // must override it.
1281                 public virtual void DeleteRange (XPathNavigator nav)
1282                 {
1283                         throw new NotSupportedException ();
1284                 }
1285
1286                 public virtual XmlWriter ReplaceRange (XPathNavigator nav)
1287                 {
1288                         throw new NotSupportedException ();
1289                 }
1290         
1291                 public virtual XmlWriter InsertAfter ()
1292                 {
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));
1298                         }
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");
1306                 }
1307
1308                 public virtual void InsertAfter (string xmlFragments)
1309                 {
1310                         InsertAfter (CreateFragmentReader (xmlFragments));
1311                 }
1312
1313                 public virtual void InsertAfter (XmlReader reader)
1314                 {
1315                         using (XmlWriter w = InsertAfter ()) {
1316                                 w.WriteNode (reader, false);
1317                         }
1318                 }
1319
1320                 public virtual void InsertAfter (XPathNavigator nav)
1321                 {
1322                         InsertAfter (new XPathNavigatorReader (nav));
1323                 }
1324
1325                 public virtual XmlWriter InsertBefore ()
1326                 {
1327                         throw new NotSupportedException ();
1328                 }
1329
1330                 public virtual void InsertBefore (string xmlFragments)
1331                 {
1332                         InsertBefore (CreateFragmentReader (xmlFragments));
1333                 }
1334
1335                 public virtual void InsertBefore (XmlReader reader)
1336                 {
1337                         using (XmlWriter w = InsertBefore ()) {
1338                                 w.WriteNode (reader, false);
1339                         }
1340                 }
1341
1342                 public virtual void InsertBefore (XPathNavigator nav)
1343                 {
1344                         InsertBefore (new XPathNavigatorReader (nav));
1345                 }
1346
1347                 public virtual void InsertElementAfter (string prefix, 
1348                         string localName, string namespaceURI, string value)
1349                 {
1350                         using (XmlWriter w = InsertAfter ()) {
1351                                 w.WriteElementString (prefix, localName, namespaceURI, value);
1352                         }
1353                 }
1354
1355                 public virtual void InsertElementBefore (string prefix, 
1356                         string localName, string namespaceURI, string value)
1357                 {
1358                         using (XmlWriter w = InsertBefore ()) {
1359                                 w.WriteElementString (prefix, localName, namespaceURI, value);
1360                         }
1361                 }
1362
1363                 public virtual XmlWriter PrependChild ()
1364                 {
1365                         XPathNavigator nav = Clone ();
1366                         if (nav.MoveToFirstChild ())
1367                                 return nav.InsertBefore ();
1368                         else
1369                                 return AppendChild ();
1370                 }
1371
1372                 public virtual void PrependChild (string xmlFragments)
1373                 {
1374                         PrependChild (CreateFragmentReader (xmlFragments));
1375                 }
1376
1377                 public virtual void PrependChild (XmlReader reader)
1378                 {
1379                         using (XmlWriter w = PrependChild ()) {
1380                                 w.WriteNode (reader, false);
1381                         }
1382                 }
1383
1384                 public virtual void PrependChild (XPathNavigator nav)
1385                 {
1386                         PrependChild (new XPathNavigatorReader (nav));
1387                 }
1388
1389                 public virtual void PrependChildElement (string prefix, 
1390                         string localName, string namespaceURI, string value)
1391                 {
1392                         using (XmlWriter w = PrependChild ()) {
1393                                 w.WriteElementString (prefix, localName, namespaceURI, value);
1394                         }
1395                 }
1396
1397                 public virtual void ReplaceSelf (string xmlFragment)
1398                 {
1399                         ReplaceSelf (CreateFragmentReader (xmlFragment));
1400                 }
1401
1402                 // must override it.
1403                 public virtual void ReplaceSelf (XmlReader reader)
1404                 {
1405                         throw new NotSupportedException ();
1406                 }
1407
1408                 public virtual void ReplaceSelf (XPathNavigator navigator)
1409                 {
1410                         ReplaceSelf (new XPathNavigatorReader (navigator));
1411                 }
1412
1413                 // Dunno the exact purpose, but maybe internal editor use
1414                 [MonoTODO]
1415                 public virtual void SetTypedValue (object value)
1416                 {
1417                         throw new NotSupportedException ();
1418                 }
1419
1420                 public virtual void SetValue (string value)
1421                 {
1422                         throw new NotSupportedException ();
1423                 }
1424
1425                 private void DeleteChildren ()
1426                 {
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;
1439                         }
1440                         if (!HasChildren)
1441                                 return;
1442                         XPathNavigator nav = Clone ();
1443                         nav.MoveToFirstChild ();
1444                         while (!nav.IsSamePosition (this))
1445                                 nav.DeleteSelf ();
1446                 }
1447 #endif
1448         }
1449 }