2004-02-17 Atsushi Enomoto <atsushi@ximian.com>
[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 //
7 // (C) 2002 Jason Diamond  http://injektilo.org/
8 //
9
10 using System;
11 using Mono.Xml.XPath;
12
13 namespace System.Xml.XPath
14 {
15         public abstract class XPathNavigator : ICloneable
16         {
17                 #region Constructor
18
19                 protected XPathNavigator ()
20                 {
21                 }
22
23                 #endregion
24
25                 #region Properties
26
27                 public abstract string BaseURI { get; }
28
29                 public abstract bool HasAttributes { get; }
30
31                 public abstract bool HasChildren { get; }
32
33                 public abstract bool IsEmptyElement { get; }
34
35                 public abstract string LocalName { get; }
36
37                 public abstract string Name { get; }
38
39                 public abstract string NamespaceURI { get; }
40
41                 public abstract XmlNameTable NameTable { get; }
42
43                 public abstract XPathNodeType NodeType { get; }
44
45                 public abstract string Prefix { get; }
46
47                 public abstract string Value { get; }
48
49                 public abstract string XmlLang { get; }
50
51                 int Depth
52                 {
53                         get
54                         {
55                                 int cLevels = 0;
56                                 XPathNavigator nav = Clone ();
57                                 while (nav.MoveToParent ())
58                                         cLevels ++;
59                                 return cLevels;
60                         }
61                 }
62
63                 #endregion
64
65                 #region Methods
66
67                 public abstract XPathNavigator Clone ();
68
69                 public virtual XmlNodeOrder ComparePosition (XPathNavigator nav)
70                 {
71                         if (IsSamePosition (nav))
72                                 return XmlNodeOrder.Same;
73
74                         XPathNavigator nav1 = Clone ();
75                         XPathNavigator nav2 = nav.Clone ();
76
77                         int nDepth1 = nav1.Depth;
78                         int nDepth2 = nav2.Depth;
79
80                         if (nDepth1 > nDepth2)
81                         {
82                                 while (nDepth1 > nDepth2)
83                                 {
84                                         nav1.MoveToParent ();
85                                         nDepth1 --;
86                                 }
87                                 if (nav1.IsSamePosition (nav2))
88                                         return XmlNodeOrder.After;
89                         }
90                         else if (nDepth1 < nDepth2)
91                         {
92                                 while (nDepth1 < nDepth2)
93                                 {
94                                         nav2.MoveToParent ();
95                                         nDepth2 --;
96                                 }
97                                 if (nav1.IsSamePosition (nav2))
98                                         return XmlNodeOrder.Before;
99                         }
100
101                         XPathNavigator parent1 = nav1.Clone ();
102                         XPathNavigator parent2 = nav2.Clone ();
103                         while (parent1.MoveToParent () && parent2.MoveToParent ())
104                         {
105                                 if (parent1.IsSamePosition (parent2))
106                                 {
107                                         // the ordering is namespace, attribute, children
108                                         // assume that nav1 is before nav2, find counter-example
109                                         if (nav1.NodeType == XPathNodeType.Namespace)
110                                         {
111                                                 if (nav2.NodeType == XPathNodeType.Namespace)
112                                                 {
113                                                         // match namespaces
114                                                         while (nav2.MoveToNextNamespace ())
115                                                                 if (nav2.IsSamePosition (nav1))
116                                                                         return XmlNodeOrder.After;
117                                                 }
118                                         }
119                                         else if (nav1.NodeType == XPathNodeType.Attribute)
120                                         {
121                                                 if (nav2.NodeType == XPathNodeType.Namespace)
122                                                         return XmlNodeOrder.After;
123                                                 else if (nav2.NodeType == XPathNodeType.Attribute)
124                                                 {
125                                                         // match attributes
126                                                         while (nav2.MoveToNextAttribute ())
127                                                                 if (nav2.IsSamePosition (nav1))
128                                                                         return XmlNodeOrder.After;
129                                                 }
130                                         }
131                                         else
132                                         {
133                                                 switch (nav2.NodeType) {
134                                                 case XPathNodeType.Namespace:
135                                                 case XPathNodeType.Attribute:
136                                                         return XmlNodeOrder.After;
137                                                 }
138                                                 // match children
139                                                 while (nav2.MoveToNext ())
140                                                         if (nav2.IsSamePosition (nav1))
141                                                                 return XmlNodeOrder.After;
142                                         }
143                                         return XmlNodeOrder.Before;
144                                 }
145                                 nav1.MoveToParent ();
146                                 nav2.MoveToParent ();
147                         }
148                         return XmlNodeOrder.Unknown;
149                 }
150
151                 public virtual XPathExpression Compile (string xpath)
152                 {
153                         XPathParser parser = new XPathParser ();
154                         return new CompiledExpression (parser.Compile (xpath));
155                 }
156                 
157                 internal virtual XPathExpression Compile (string xpath, System.Xml.Xsl.IStaticXsltContext ctx)
158                 {
159                         XPathParser parser = new XPathParser (ctx);
160                         return new CompiledExpression (parser.Compile (xpath));
161                 }
162
163                 public virtual object Evaluate (string xpath)
164                 {
165                         return Evaluate (Compile (xpath));
166                 }
167
168                 public virtual object Evaluate (XPathExpression expr)
169                 {
170                         return Evaluate (expr, null);
171                 }
172
173                 public virtual object Evaluate (XPathExpression expr, XPathNodeIterator context)
174                 {
175                         return Evaluate (expr, context, null);
176                 }
177                 
178                 internal virtual object Evaluate (XPathExpression expr, XPathNodeIterator context, XmlNamespaceManager ctx)
179                 {
180                         CompiledExpression cexpr = (CompiledExpression) expr;
181                         if (ctx == null)
182                                 ctx = cexpr.NamespaceManager;
183                         
184                         if (context == null)
185                                 context = new NullIterator (this, ctx);
186                         BaseIterator iterContext = (BaseIterator) context;
187                         iterContext.NamespaceManager = ctx;
188                         return cexpr.Evaluate (iterContext);
189                 }
190
191                 internal XPathNodeIterator EvaluateNodeSet (XPathExpression expr, XPathNodeIterator context, XmlNamespaceManager ctx)
192                 {
193                         CompiledExpression cexpr = (CompiledExpression) expr;
194                         if (ctx == null)
195                                 ctx = cexpr.NamespaceManager;
196                         
197                         if (context == null)
198                                 context = new NullIterator (this, cexpr.NamespaceManager);
199                         BaseIterator iterContext = (BaseIterator) context;
200                         iterContext.NamespaceManager = ctx;
201                         return cexpr.EvaluateNodeSet (iterContext);
202                 }
203
204                 internal string EvaluateString (XPathExpression expr, XPathNodeIterator context, XmlNamespaceManager ctx)
205                 {
206                         CompiledExpression cexpr = (CompiledExpression) expr;
207                         if (ctx == null)
208                                 ctx = cexpr.NamespaceManager;
209                         
210                         if (context == null)
211                                 context = new NullIterator (this, cexpr.NamespaceManager);
212                         BaseIterator iterContext = (BaseIterator) context;
213                         iterContext.NamespaceManager = ctx;
214                         return cexpr.EvaluateString (iterContext);
215                 }
216
217                 internal double EvaluateNumber (XPathExpression expr, XPathNodeIterator context, XmlNamespaceManager ctx)
218                 {
219                         CompiledExpression cexpr = (CompiledExpression) expr;
220                         if (ctx == null)
221                                 ctx = cexpr.NamespaceManager;
222                         
223                         if (context == null)
224                                 context = new NullIterator (this, cexpr.NamespaceManager);
225                         BaseIterator iterContext = (BaseIterator) context;
226                         iterContext.NamespaceManager = ctx;
227                         return cexpr.EvaluateNumber (iterContext);
228                 }
229
230                 internal bool EvaluateBoolean (XPathExpression expr, XPathNodeIterator context, XmlNamespaceManager ctx)
231                 {
232                         CompiledExpression cexpr = (CompiledExpression) expr;
233                         if (ctx == null)
234                                 ctx = cexpr.NamespaceManager;
235                         
236                         if (context == null)
237                                 context = new NullIterator (this, cexpr.NamespaceManager);
238                         BaseIterator iterContext = (BaseIterator) context;
239                         iterContext.NamespaceManager = ctx;
240                         return cexpr.EvaluateBoolean (iterContext);
241                 }
242
243                 public abstract string GetAttribute (string localName, string namespaceURI);
244
245                 public abstract string GetNamespace (string name);
246                 
247                 object ICloneable.Clone ()
248                 {
249                         return Clone ();
250                 }
251
252                 public virtual bool IsDescendant (XPathNavigator nav)
253                 {
254                         if (nav != null)
255                         {
256                                 nav = nav.Clone ();
257                                 while (nav.MoveToParent ())
258                                 {
259                                         if (IsSamePosition (nav))
260                                                 return true;
261                                 }
262                         }
263                         return false;
264                 }
265
266                 public abstract bool IsSamePosition (XPathNavigator other);
267
268                 public virtual bool Matches (string xpath)
269                 {
270                         return Matches (Compile (xpath));
271                 }
272
273                 [MonoTODO]      // optimize...
274                 public virtual bool Matches (XPathExpression expr)
275                 {
276                         Expression e = ((CompiledExpression) expr).ExpressionNode;
277                         if (e is ExprRoot)
278                                 return NodeType == XPathNodeType.Root;
279                         
280                         NodeTest nt = e as NodeTest;
281                         if (nt != null) {
282                                 switch (nt.Axis.Axis) {
283                                 case Axes.Child:
284                                 case Axes.Attribute:
285                                         break;
286                                 default:
287                                         throw new XPathException ("Only child and attribute pattern are allowed for a pattern.");
288                                 }
289                                 return nt.Match (((CompiledExpression)expr).NamespaceManager, this);
290                         }
291                         if (e is ExprFilter) {
292                                 do {
293                                         e = ((ExprFilter) e).LeftHandSide;
294                                 } while (e is ExprFilter);
295                                 
296                                 if (e is NodeTest && !((NodeTest) e).Match (((CompiledExpression) expr).NamespaceManager, this))
297                                         return false;
298                         }
299
300                         XPathResultType resultType = e.ReturnType;
301                         switch (resultType) {
302                         case XPathResultType.Any:
303                         case XPathResultType.NodeSet:
304                                 break;
305                         default:
306                                 return false;
307                         }
308
309                         switch (e.EvaluatedNodeType) {
310                         case XPathNodeType.Attribute:
311                         case XPathNodeType.Namespace:
312                                 if (NodeType != e.EvaluatedNodeType)
313                                         return false;
314                                 break;
315                         }
316
317                         XPathNodeIterator nodes;
318                         nodes = this.Select (expr);
319                         while (nodes.MoveNext ()) {
320                                 if (IsSamePosition (nodes.Current))
321                                         return true;
322                         }
323
324                         // ancestors might select this node.
325
326                         XPathNavigator navigator = Clone ();
327
328                         while (navigator.MoveToParent ()) {
329                                 nodes = navigator.Select (expr);
330
331                                 while (nodes.MoveNext ()) {
332                                         if (IsSamePosition (nodes.Current))
333                                                 return true;
334                                 }
335                         }
336
337                         return false;
338                 }
339
340                 public abstract bool MoveTo (XPathNavigator other);
341
342                 public abstract bool MoveToAttribute (string localName, string namespaceURI);
343
344                 public abstract bool MoveToFirst ();
345
346                 public abstract bool MoveToFirstAttribute ();
347
348                 public abstract bool MoveToFirstChild ();
349
350                 public bool MoveToFirstNamespace ()
351                 {
352                         return MoveToFirstNamespace (XPathNamespaceScope.All);
353                 }
354
355                 public abstract bool MoveToFirstNamespace (XPathNamespaceScope namespaceScope);
356
357                 public abstract bool MoveToId (string id);
358
359                 public abstract bool MoveToNamespace (string name);
360
361                 public abstract bool MoveToNext ();
362
363                 public abstract bool MoveToNextAttribute ();
364
365                 public bool MoveToNextNamespace ()
366                 {
367                         return MoveToNextNamespace (XPathNamespaceScope.All);
368                 }
369
370                 public abstract bool MoveToNextNamespace (XPathNamespaceScope namespaceScope);
371
372                 public abstract bool MoveToParent ();
373
374                 public abstract bool MoveToPrevious ();
375
376                 public abstract void MoveToRoot ();
377
378                 public virtual XPathNodeIterator Select (string xpath)
379                 {
380                         return Select (Compile (xpath));
381                 }
382
383                 public virtual XPathNodeIterator Select (XPathExpression expr)
384                 {
385                         return Select (expr, null);
386                 }
387                 
388                 internal virtual XPathNodeIterator Select (XPathExpression expr, XmlNamespaceManager ctx)
389                 {
390                         CompiledExpression cexpr = (CompiledExpression) expr;
391                         if (ctx == null)
392                                 ctx = cexpr.NamespaceManager;
393                         
394                         BaseIterator iter = new NullIterator (this, ctx);
395                         return cexpr.EvaluateNodeSet (iter);
396                 }
397
398                 public virtual XPathNodeIterator SelectAncestors (XPathNodeType type, bool matchSelf)
399                 {
400                         Axes axis = (matchSelf) ? Axes.AncestorOrSelf : Axes.Ancestor;
401                         return SelectTest (new NodeTypeTest (axis, type));
402                 }
403
404                 public virtual XPathNodeIterator SelectAncestors (string name, string namespaceURI, bool matchSelf)
405                 {
406                         if (name == null)
407                                 throw new ArgumentNullException ("name");
408                         if (namespaceURI == null)
409                                 throw new ArgumentNullException ("namespaceURI");
410
411                         Axes axis = (matchSelf) ? Axes.AncestorOrSelf : Axes.Ancestor;
412                         XmlQualifiedName qname = new XmlQualifiedName (name, namespaceURI);
413                         return SelectTest (new NodeNameTest (axis, qname, true));
414                 }
415
416                 public virtual XPathNodeIterator SelectChildren (XPathNodeType type)
417                 {
418                         return SelectTest (new NodeTypeTest (Axes.Child, type));
419                 }
420
421                 public virtual XPathNodeIterator SelectChildren (string name, string namespaceURI)
422                 {
423                         if (name == null)
424                                 throw new ArgumentNullException ("name");
425                         if (namespaceURI == null)
426                                 throw new ArgumentNullException ("namespaceURI");
427
428                         Axes axis = Axes.Child;
429                         XmlQualifiedName qname = new XmlQualifiedName (name, namespaceURI);
430                         return SelectTest (new NodeNameTest (axis, qname, true));
431                 }
432
433                 public virtual XPathNodeIterator SelectDescendants (XPathNodeType type, bool matchSelf)
434                 {
435                         Axes axis = (matchSelf) ? Axes.DescendantOrSelf : Axes.Descendant;
436                         return SelectTest (new NodeTypeTest (axis, type));
437                 }
438
439                 public virtual XPathNodeIterator SelectDescendants (string name, string namespaceURI, bool matchSelf)
440                 {
441                         if (name == null)
442                                 throw new ArgumentNullException ("name");
443                         if (namespaceURI == null)
444                                 throw new ArgumentNullException ("namespaceURI");
445
446
447                         Axes axis = (matchSelf) ? Axes.DescendantOrSelf : Axes.Descendant;
448                         XmlQualifiedName qname = new XmlQualifiedName (name, namespaceURI);
449                         return SelectTest (new NodeNameTest (axis, qname, true));
450                 }
451
452                 internal XPathNodeIterator SelectTest (NodeTest test)
453                 {
454                         return test.EvaluateNodeSet (new NullIterator (this));
455                 }
456
457                 public override string ToString ()
458                 {
459                         return Value;
460                 }
461
462                 #endregion
463         }
464 }