2004-03-01 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / System.Xml.XPath / Expression.cs
1 //
2 // System.Xml.XPath.XPathExpression support classes
3 //
4 // Author:
5 //   Piers Haken (piersh@friskit.com)
6 //
7 // (C) 2002 Piers Haken
8 //
9 using System;
10 using System.IO;
11 using System.Collections;
12 using System.Xml;
13 using System.Xml.XPath;
14 using System.Xml.Xsl;
15 using System.Globalization;
16 using Mono.Xml.XPath;
17
18 namespace System.Xml.XPath
19 {
20 #if XPATH_DEBUG
21         internal class CompiledExpression : Test.Xml.XPath.XPathExpression
22 #else
23         internal class CompiledExpression : XPathExpression
24 #endif
25         {
26                 protected XmlNamespaceManager _nsm;
27                 protected Expression _expr;
28                 XPathSorters _sorters;
29
30                 public CompiledExpression (Expression expr)
31                 {
32                         _expr = expr;
33                 }
34                 private CompiledExpression (CompiledExpression other)
35                 {
36                         _nsm = other._nsm;
37                         _expr = other._expr;
38                 }
39 #if XPATH_DEBUG
40                 public override Test.Xml.XPath.XPathExpression Clone () { return new CompiledExpression (this); }
41 #else
42                 public override XPathExpression Clone () { return new CompiledExpression (this); }
43 #endif
44                 
45                 public Expression ExpressionNode { get { return _expr; }}
46
47                 public override void SetContext (XmlNamespaceManager nsManager)
48                 {
49                         _nsm = nsManager;
50                 }
51                 internal XmlNamespaceManager NamespaceManager { get { return _nsm; } }
52                 public override String Expression { get { return _expr.ToString (); }}
53                 public override XPathResultType ReturnType { get { return _expr.ReturnType; }}
54
55                 public object Evaluate (BaseIterator iter)
56                 {
57                         if (_sorters != null)
58                                 return EvaluateNodeSet (iter);
59
60                         try
61                         {
62                                 return _expr.Evaluate (iter);
63                         }
64                         catch (XPathException)
65                         {
66                                 throw;
67                         }
68                         catch (Exception e)
69                         {
70                                 throw new XPathException ("Error during evaluation", e);
71                         }
72                 }
73                 public XPathNodeIterator EvaluateNodeSet (BaseIterator iter)
74                 {
75                         try
76                         {
77                                 BaseIterator iterResults = (BaseIterator) _expr.EvaluateNodeSet (iter);
78                                 if (_sorters != null)
79                                         return _sorters.Sort (iterResults);
80                                 return iterResults;
81                         }
82                         catch (XPathException)
83                         {
84                                 throw;
85                         }
86                         catch (Exception e)
87                         {
88                                 throw new XPathException ("Error during evaluation", e);
89                         }
90                 }
91                 public double EvaluateNumber (BaseIterator iter)
92                 {
93                         try
94                         {
95                                 return _expr.EvaluateNumber (iter);
96                         }
97                         catch (XPathException)
98                         {
99                                 throw;
100                         }
101                         catch (Exception e)
102                         {
103                                 throw new XPathException ("Error during evaluation", e);
104                         }
105                 }
106                 public string EvaluateString (BaseIterator iter)
107                 {
108                         try
109                         {
110                                 return _expr.EvaluateString (iter);
111                         }
112                         catch (XPathException)
113                         {
114                                 throw;
115                         }
116                         catch (Exception e)
117                         {
118                                 throw new XPathException ("Error during evaluation", e);
119                         }
120                 }
121                 public bool EvaluateBoolean (BaseIterator iter)
122                 {
123                         try
124                         {
125                                 return _expr.EvaluateBoolean (iter);
126                         }
127                         catch (XPathException)
128                         {
129                                 throw;
130                         }
131                         catch (Exception e)
132                         {
133                                 throw new XPathException ("Error during evaluation", e);
134                         }
135                 }
136
137                 public override void AddSort (Object obj, IComparer cmp)
138                 {
139                         if (_sorters == null)
140                                 _sorters = new XPathSorters ();
141                         _sorters.Add (obj, cmp);
142                 }
143                 public override void AddSort(object expr, XmlSortOrder orderSort, XmlCaseOrder orderCase, string lang, XmlDataType dataType)
144                 {
145                         if (_sorters == null)
146                                 _sorters = new XPathSorters ();
147                         _sorters.Add (expr, orderSort, orderCase, lang, dataType);
148                 }
149
150                 class XPathSorters : IComparer
151                 {
152                         readonly ArrayList _rgSorters = new ArrayList ();
153
154                         public void Add (object expr, IComparer cmp)
155                         {
156                                 _rgSorters.Add (new XPathSorter (expr, cmp));
157                         }
158
159                         public void Add (object expr, XmlSortOrder orderSort, XmlCaseOrder orderCase, string lang, XmlDataType dataType)
160                         {
161                                 _rgSorters.Add (new XPathSorter (expr, orderSort, orderCase, lang, dataType));
162                         }
163
164                         public BaseIterator Sort (BaseIterator iter)
165                         {
166                                 ArrayList rgElts = new ArrayList ();
167                                 int cSorters = _rgSorters.Count;
168                                 while (iter.MoveNext ())
169                                 {
170                                         XPathSortElement elt = new XPathSortElement ();
171                                         elt.Navigator = iter.Current.Clone ();
172                                         elt.Values = new object [cSorters];
173                                         for (int iSorter = 0; iSorter < _rgSorters.Count; ++iSorter)
174                                         {
175                                                 XPathSorter sorter = (XPathSorter) _rgSorters [iSorter];
176                                                 elt.Values [iSorter] = sorter.Evaluate (iter);
177                                         }
178                                         rgElts.Add (elt);
179                                 }
180                                 rgElts.Sort (this);
181                                 XPathNavigator [] rgResults = new XPathNavigator [rgElts.Count];
182                                 for (int iResult = 0; iResult < rgElts.Count; ++iResult)
183                                 {
184                                         XPathSortElement elt = (XPathSortElement) rgElts [iResult];
185                                         rgResults [iResult] = elt.Navigator;
186                                 }
187                                 return new ListIterator (iter, rgResults, false);
188                         }
189
190                         class XPathSortElement
191                         {
192                                 public XPathNavigator Navigator;
193                                 public object [] Values;
194                         }
195
196                         int IComparer.Compare (object o1, object o2)
197                         {
198                                 XPathSortElement elt1 = (XPathSortElement) o1;
199                                 XPathSortElement elt2 = (XPathSortElement) o2;
200                                 for (int iSorter = 0; iSorter < _rgSorters.Count; ++iSorter)
201                                 {
202                                         XPathSorter sorter = (XPathSorter) _rgSorters [iSorter];
203                                         int cmp = sorter.Compare (elt1.Values [iSorter], elt2.Values [iSorter]);
204                                         if (cmp != 0)
205                                                 return cmp;
206                                 }
207                                 switch (elt1.Navigator.ComparePosition (elt2.Navigator)) {
208                                 case XmlNodeOrder.Same:
209                                         return 0;
210                                 case XmlNodeOrder.After:
211                                         return 1;
212                                 default:
213                                         return -1;
214                                 }
215                         }
216
217                         class XPathSorter
218                         {
219                                 readonly Expression _expr;
220                                 readonly IComparer _cmp;
221                                 readonly XmlDataType _type;
222
223                                 public XPathSorter (object expr, IComparer cmp)
224                                 {
225                                         _expr = ExpressionFromObject (expr);
226                                         _cmp = cmp;
227                                         _type = XmlDataType.Text;
228                                 }
229
230                                 public XPathSorter (object expr, XmlSortOrder orderSort, XmlCaseOrder orderCase, string lang, XmlDataType dataType)
231                                 {
232                                         _expr = ExpressionFromObject (expr);
233                                         _type = dataType;
234                                         if (dataType == XmlDataType.Number)
235                                                 _cmp = new XPathNumberComparer (orderSort);
236                                         else
237                                                 _cmp = new XPathTextComparer (orderSort, orderCase, lang);
238                                 }
239
240                                 static Expression ExpressionFromObject (object expr)
241                                 {
242                                         if (expr is CompiledExpression)
243                                                 return ((CompiledExpression) expr)._expr;
244                                         if (expr is string)
245                                                 return new XPathParser ().Compile ((string)expr);
246                                         
247                                         throw new XPathException ("Invalid query object");
248                                 }
249
250                                 public object Evaluate (BaseIterator iter)
251                                 {
252                                         if (_type == XmlDataType.Number)
253                                                 return _expr.EvaluateNumber (iter);
254                                         return _expr.EvaluateString (iter);
255                                 }
256
257                                 public int Compare (object o1, object o2)
258                                 {
259                                         return _cmp.Compare (o1, o2);
260                                 }
261
262                                 class XPathNumberComparer : IComparer
263                                 {
264                                         int _nMulSort;
265
266                                         public XPathNumberComparer (XmlSortOrder orderSort)
267                                         {
268                                                 _nMulSort = (orderSort == XmlSortOrder.Ascending) ? 1 : -1;
269                                         }
270
271                                         int IComparer.Compare (object o1, object o2)
272                                         {
273                                                 double num1 = (double) o1;
274                                                 double num2 = (double) o2;
275                                                 if (num1 < num2)
276                                                         return -_nMulSort;
277                                                 if (num1 > num2)
278                                                         return _nMulSort;
279                                                 if (num1 == num2)
280                                                         return 0;
281                                                 if (double.IsNaN (num1))
282                                                         return (double.IsNaN (num2)) ? 0 : -_nMulSort;
283                                                 return _nMulSort;
284                                         }
285                                 }
286
287                                 class XPathTextComparer : IComparer
288                                 {
289                                         int _nMulSort;
290                                         int _nMulCase;
291                                         XmlCaseOrder _orderCase;
292                                         CultureInfo _ci;
293
294                                         public XPathTextComparer (XmlSortOrder orderSort, XmlCaseOrder orderCase, string strLang)
295                                         {
296                                                 _orderCase = orderCase;
297                                                 _nMulCase = (orderCase == XmlCaseOrder.UpperFirst) ? -1 : 1;
298
299                                                 _nMulSort = (orderSort == XmlSortOrder.Ascending) ? 1 : -1;
300
301                                                 if (strLang == null || strLang == "")
302                                                         _ci = CultureInfo.CurrentCulture;       // TODO: defer until evaluation?
303                                                 else
304                                                         _ci = new CultureInfo (strLang);
305                                         }
306
307                                         int IComparer.Compare (object o1, object o2)
308                                         {
309                                                 string str1 = (string) o1;
310                                                 string str2 = (string) o2;
311                                                 int cmp = String.Compare (str1, str2, true, _ci);
312                                                 if (cmp != 0 || _orderCase == XmlCaseOrder.None)
313                                                         return cmp * _nMulSort;
314                                                 return _nMulSort * _nMulCase * String.Compare (str1, str2, false, _ci);
315                                         }
316                                 }
317                         }
318                 }
319         }
320
321
322         /// <summary>
323         /// Summary description for Expression.
324         /// </summary>
325         internal abstract class Expression
326         {
327                 public Expression ()
328                 {
329                 }
330                 public abstract XPathResultType ReturnType { get; }
331                 public virtual XPathResultType GetReturnType (BaseIterator iter) { return ReturnType; }
332                 public abstract object Evaluate (BaseIterator iter);
333
334                 public virtual BaseIterator EvaluateNodeSet (BaseIterator iter)
335                 {
336                         XPathResultType type = GetReturnType (iter);
337                         if (type == XPathResultType.NodeSet ||
338                                 type == XPathResultType.Any)
339                         {
340                                 BaseIterator iterResult = Evaluate (iter) as BaseIterator;
341                                 if (iterResult != null)
342                                         return iterResult;
343                         }
344                         throw new XPathException ("expected nodeset: "+ToString ());
345                 }
346
347                 protected static XPathResultType GetReturnType (object obj)
348                 {
349                         if (obj is string)
350                                 return XPathResultType.String;
351                         if (obj is bool)
352                                 return XPathResultType.Boolean;
353                         if (obj is XPathNodeIterator)
354                                 return XPathResultType.NodeSet;
355                         if (obj is double || obj is int)
356                                 return XPathResultType.Number;
357                         throw new XPathException ("invalid node type: "+obj.GetType ().ToString ());
358                 }
359
360                 internal virtual XPathNodeType EvaluatedNodeType {
361                         get { return XPathNodeType.All; }
362                 }
363
364                 internal virtual bool NeedAbsoluteMatching {
365                         get { return false; }
366                 }
367
368                 internal virtual bool IsPositional {
369                         get { return false; }
370                 }
371
372                 [MonoTODO]
373                 public virtual double EvaluateNumber (BaseIterator iter)
374                 {
375                         object result;
376                         XPathResultType type = GetReturnType (iter);
377                         if (type == XPathResultType.NodeSet)
378                         {
379                                 result = EvaluateString (iter);
380                                 type = XPathResultType.String;
381                         }
382                         else
383                                 result = Evaluate (iter);
384
385                         if (type == XPathResultType.Any)
386                                 type = GetReturnType (result);
387
388                         switch (type)
389                         {
390                                 case XPathResultType.Number:
391                                         return (double)result;
392                                 case XPathResultType.Boolean:
393                                         return ((bool) result) ? 1.0 : 0.0;
394                                 case XPathResultType.NodeSet:
395                                         return XPathFunctions.ToNumber (EvaluateString (iter));
396                                 case XPathResultType.String:
397                                         return XPathFunctions.ToNumber ((string) result);       // TODO: spec? convert string to number
398                                 default:
399                                         throw new XPathException ("invalid node type"); // TODO: handle other types
400                         }
401                 }
402                 [MonoTODO]
403                 public virtual string EvaluateString (BaseIterator iter)
404                 {
405                         object result = Evaluate (iter);
406                         XPathResultType type = GetReturnType (iter);
407                         if (type == XPathResultType.Any)
408                                 type = GetReturnType (result);
409                         switch (type)
410                         {
411                                 case XPathResultType.Number:
412                                         return (string) XmlConvert.ToString ((double)result);   // TODO: spec? convert number to string
413                                 case XPathResultType.Boolean:
414                                         return ((bool) result) ? "true" : "false";
415                                 case XPathResultType.String:
416                                         return (string) result;
417                                 case XPathResultType.NodeSet:
418                                 {
419                                         BaseIterator iterResult = (BaseIterator) result;
420                                         if (iterResult == null || !iterResult.MoveNext ())
421                                                 return "";
422                                         return iterResult.Current.Value;
423                                 }
424                                 default:
425                                         throw new XPathException ("invalid node type"); // TODO: handle other types
426                         }
427                 }
428                 [MonoTODO]
429                 public virtual bool EvaluateBoolean (BaseIterator iter)
430                 {
431                         object result = Evaluate (iter);
432                         XPathResultType type = GetReturnType (iter);
433                         if (type == XPathResultType.Any)
434                                 type = GetReturnType (result);
435                         switch (type)
436                         {
437                                 case XPathResultType.Number:
438                                 {
439                                         double num = Convert.ToDouble (result);
440                                         return (num != 0.0 && num != -0.0 && !Double.IsNaN (num));
441                                 }
442                                 case XPathResultType.Boolean:
443                                         return (bool) result;
444                                 case XPathResultType.String:
445                                         return ((string) result).Length != 0;
446                                 case XPathResultType.NodeSet:
447                                 {
448                                         BaseIterator iterResult = (BaseIterator) result;
449                                         return (iterResult != null && iterResult.MoveNext ());
450                                 }
451                                 default:
452                                         throw new XPathException ("invalid node type"); // TODO: handle other types
453                         }
454                 }
455                 public object EvaluateAs (BaseIterator iter, XPathResultType type)
456                 {
457                         switch (type)
458                         {
459                         case XPathResultType.Boolean:
460                                 return EvaluateBoolean (iter);
461                         case XPathResultType.NodeSet:
462                                 return EvaluateNodeSet (iter);
463                         case XPathResultType.String:
464                                 return EvaluateString (iter);
465                         case XPathResultType.Number:
466                                 return EvaluateNumber (iter);
467                         }
468                         return Evaluate (iter);
469                 }
470
471                 public virtual bool RequireSorting { get { return false; } }
472         }
473
474         internal abstract class ExprBinary : Expression
475         {
476                 protected Expression _left, _right;
477
478                 public ExprBinary (Expression left, Expression right)
479                 {
480                         _left = left;
481                         _right = right;
482                 }
483                 public override String ToString ()
484                 {
485                         return _left.ToString () + ' ' + Operator + ' ' + _right.ToString ();
486                 }
487                 protected abstract String Operator { get; }
488
489                 internal override XPathNodeType EvaluatedNodeType {
490                         get {
491                                 if (_left.EvaluatedNodeType == _right.EvaluatedNodeType)
492                                         return _left.EvaluatedNodeType;
493                                 else
494                                         return XPathNodeType.All;
495                         }
496                 }
497
498                 internal override bool IsPositional {
499                         get { return _left.IsPositional || _right.IsPositional; }
500                 }
501         }
502
503         internal abstract class ExprBoolean : ExprBinary
504         {
505                 public ExprBoolean (Expression left, Expression right) : base (left, right) {}
506                 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
507                 public override object Evaluate (BaseIterator iter)
508                 {
509                         return EvaluateBoolean (iter);
510                 }
511                 public override double EvaluateNumber (BaseIterator iter)
512                 {
513                         return EvaluateBoolean (iter) ? 1 : 0;
514                 }
515                 
516                 public override string EvaluateString (BaseIterator iter)
517                 {
518                         return EvaluateBoolean (iter) ? "true" : "false";
519                 }
520         }
521
522         internal class ExprOR : ExprBoolean
523         {
524                 public ExprOR (Expression left, Expression right) : base (left, right) {}
525                 protected override String Operator { get { return "or"; }}
526                 public override bool EvaluateBoolean (BaseIterator iter)
527                 {
528                         if (_left.EvaluateBoolean (iter))
529                                 return true;
530                         return _right.EvaluateBoolean (iter);
531                 }
532         }
533
534         internal class ExprAND : ExprBoolean
535         {
536                 public ExprAND (Expression left, Expression right) : base (left, right) {}
537                 protected override String Operator { get { return "and"; }}
538                 public override bool EvaluateBoolean (BaseIterator iter)
539                 {
540                         if (!_left.EvaluateBoolean (iter))
541                                 return false;
542                         return _right.EvaluateBoolean (iter);
543                 }
544         }
545
546         internal abstract class EqualityExpr : ExprBoolean
547         {
548                 bool trueVal;
549                 public EqualityExpr (Expression left, Expression right, bool trueVal) : base (left, right)
550                 {
551                         this.trueVal = trueVal;
552                 }
553                 [MonoTODO]
554                 public override bool EvaluateBoolean (BaseIterator iter)
555                 {
556                         XPathResultType typeL = _left.GetReturnType (iter);
557                         XPathResultType typeR = _right.GetReturnType (iter);
558
559                         // TODO: avoid double evaluations
560                         if (typeL == XPathResultType.Any)
561                                 typeL = GetReturnType (_left.Evaluate (iter));
562                         if (typeR == XPathResultType.Any)
563                                 typeR = GetReturnType (_right.Evaluate (iter));
564
565                         if (typeL == XPathResultType.NodeSet || typeR == XPathResultType.NodeSet)
566                         {
567                                 Expression left, right;
568                                 if (typeL != XPathResultType.NodeSet)
569                                 {
570                                         left = _right;
571                                         right = _left;
572                                         XPathResultType typeTmp = typeL;
573                                         typeL = typeR;
574                                         typeR = typeTmp;
575                                 }
576                                 else
577                                 {
578                                         left = _left;
579                                         right = _right;
580                                 }
581                                 if (typeR == XPathResultType.Boolean)
582                                 {
583                                         return left.EvaluateBoolean (iter) == right.EvaluateBoolean (iter) == trueVal;
584                                 }
585                                 else
586                                 {
587                                         BaseIterator iterL = left.EvaluateNodeSet (iter);
588                                         if (typeR == XPathResultType.Number)
589                                         {
590                                                 double dR = right.EvaluateNumber (iter);
591                                                 while (iterL.MoveNext ())
592                                                         if (XPathFunctions.ToNumber (iterL.Current.Value) == dR == trueVal)
593                                                                 return true;
594                                         }
595                                         else if (typeR == XPathResultType.String)
596                                         {
597                                                 string strR = right.EvaluateString (iter);
598                                                 while (iterL.MoveNext ())
599                                                         if (iterL.Current.Value == strR == trueVal)
600                                                                 return true;
601                                         }
602                                         else if (typeR == XPathResultType.NodeSet)
603                                         {
604                                                 BaseIterator iterR = right.EvaluateNodeSet (iter);
605                                                 ArrayList rgNodesL = new ArrayList ();
606                                                 while (iterL.MoveNext ())
607                                                         rgNodesL.Add (XPathFunctions.ToString (iterL.Current.Value));
608                                                 while (iterR.MoveNext ())
609                                                 {
610                                                         string strR = XPathFunctions.ToString (iterR.Current.Value);
611                                                         for (int l = 0; l < rgNodesL.Count; l++)
612                                                                 if ((strR == (string) rgNodesL [l]) == trueVal)
613                                                                         return true;
614                                                 }
615                                         }
616                                         return false;
617                                 }
618                         }
619                         else if (typeL == XPathResultType.Boolean || typeR == XPathResultType.Boolean)
620                                 return _left.EvaluateBoolean (iter) == _right.EvaluateBoolean (iter) == trueVal;
621                         else if (typeL == XPathResultType.Number || typeR == XPathResultType.Number)
622                                 return _left.EvaluateNumber (iter) == _right.EvaluateNumber (iter) == trueVal;
623                         else
624                                 return _left.EvaluateString (iter) == _right.EvaluateString (iter) == trueVal;
625                 }
626         }
627         
628         internal class ExprEQ : EqualityExpr
629         {
630                 public ExprEQ (Expression left, Expression right) : base (left, right, true) {}
631                 protected override String Operator { get { return "="; }}
632         }
633
634         internal class ExprNE : EqualityExpr
635         {
636                 public ExprNE (Expression left, Expression right) : base (left, right, false) {}
637                 protected override String Operator { get { return "!="; }}
638         }
639
640         internal abstract class RelationalExpr : ExprBoolean
641         {
642                 public RelationalExpr (Expression left, Expression right) : base (left, right) {}
643                 [MonoTODO]
644                 public override bool EvaluateBoolean (BaseIterator iter)
645                 {
646                         XPathResultType typeL = _left.GetReturnType (iter);
647                         XPathResultType typeR = _right.GetReturnType (iter);
648
649                         // TODO: avoid double evaluations
650                         if (typeL == XPathResultType.Any)
651                                 typeL = GetReturnType (_left.Evaluate (iter));
652                         if (typeR == XPathResultType.Any)
653                                 typeR = GetReturnType (_right.Evaluate (iter));
654
655                         if (typeL == XPathResultType.NodeSet || typeR == XPathResultType.NodeSet)
656                         {
657                                 bool fReverse = false;
658                                 Expression left, right;
659                                 if (typeL != XPathResultType.NodeSet)
660                                 {
661                                         fReverse = true;
662                                         left = _right;
663                                         right = _left;
664                                         XPathResultType typeTmp = typeL;
665                                         typeL = typeR;
666                                         typeR = typeTmp;
667                                 }
668                                 else
669                                 {
670                                         left = _left;
671                                         right = _right;
672                                 }
673                                 if (typeR == XPathResultType.Boolean)
674                                 {
675                                         bool fL = left.EvaluateBoolean (iter);
676                                         bool fR = right.EvaluateBoolean (iter);
677                                         return Compare (Convert.ToDouble (fL), Convert.ToDouble (fR), fReverse);
678                                 }
679                                 else
680                                 {
681                                         BaseIterator iterL = left.EvaluateNodeSet (iter);
682                                         if (typeR == XPathResultType.Number || typeR == XPathResultType.String)
683                                         {
684                                                 double dR = right.EvaluateNumber (iter);
685                                                 while (iterL.MoveNext ())
686                                                         if (Compare (XPathFunctions.ToNumber (iterL.Current.Value), dR, fReverse))
687                                                                 return true;
688                                         }
689                                         else if (typeR == XPathResultType.NodeSet)
690                                         {
691                                                 BaseIterator iterR = right.EvaluateNodeSet (iter);
692                                                 ArrayList rgNodesL = new ArrayList ();
693                                                 while (iterL.MoveNext ())
694                                                         rgNodesL.Add (XPathFunctions.ToNumber (iterL.Current.Value));
695                                                 while (iterR.MoveNext ())
696                                                 {
697                                                         double numR = XPathFunctions.ToNumber (iterR.Current.Value);
698                                                         for (int l = 0; l < rgNodesL.Count; l++)
699                                                                 if (Compare (numR, (double) rgNodesL [l]))
700                                                                         return true;
701                                                 }
702                                         }
703                                         return false;
704                                 }
705                         }
706                         else
707                                 return Compare (_left.EvaluateNumber (iter), _right.EvaluateNumber (iter));
708                 }
709                 public abstract bool Compare (double arg1, double arg2);
710                 public bool Compare (double arg1, double arg2, bool fReverse)
711                 {
712                         if (fReverse)
713                                 return Compare (arg2, arg1);
714                         else
715                                 return Compare (arg1, arg2);
716                 }
717         }
718
719         internal class ExprGT : RelationalExpr
720         {
721                 public ExprGT (Expression left, Expression right) : base (left, right) {}
722                 protected override String Operator { get { return ">"; }}
723                 public override bool Compare (double arg1, double arg2)
724                 {
725                         return arg1 > arg2;
726                 }
727         }
728
729         internal class ExprGE : RelationalExpr
730         {
731                 public ExprGE (Expression left, Expression right) : base (left, right) {}
732                 protected override String Operator { get { return ">="; }}
733                 public override bool Compare (double arg1, double arg2)
734                 {
735                         return arg1 >= arg2;
736                 }
737         }
738
739         internal class ExprLT : RelationalExpr
740         {
741                 public ExprLT (Expression left, Expression right) : base (left, right) {}
742                 protected override String Operator { get { return "<"; }}
743                 public override bool Compare (double arg1, double arg2)
744                 {
745                         return arg1 < arg2;
746                 }
747         }
748
749         internal class ExprLE : RelationalExpr
750         {
751                 public ExprLE (Expression left, Expression right) : base (left, right) {}
752                 protected override String Operator { get { return "<="; }}
753                 public override bool Compare (double arg1, double arg2)
754                 {
755                         return arg1 <= arg2;
756                 }
757         }
758
759         internal abstract class ExprNumeric : ExprBinary
760         {
761                 public ExprNumeric (Expression left, Expression right) : base (left, right) {}
762                 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
763                 
764                 public override object Evaluate (BaseIterator iter)
765                 {
766                         return EvaluateNumber (iter);
767                 }
768         }
769
770         internal class ExprPLUS : ExprNumeric
771         {
772                 public ExprPLUS (Expression left, Expression right) : base (left, right) {}
773                 protected override String Operator { get { return "+"; }}
774                 public override double EvaluateNumber (BaseIterator iter)
775                 {
776                         return _left.EvaluateNumber (iter) + _right.EvaluateNumber (iter);
777                 }
778         }
779
780         internal class ExprMINUS : ExprNumeric
781         {
782                 public ExprMINUS (Expression left, Expression right) : base (left, right) {}
783                 protected override String Operator { get { return "-"; }}
784                 public override double EvaluateNumber (BaseIterator iter)
785                 {
786                         return _left.EvaluateNumber (iter) - _right.EvaluateNumber (iter);
787                 }
788         }
789
790         internal class ExprMULT : ExprNumeric
791         {
792                 public ExprMULT (Expression left, Expression right) : base (left, right) {}
793                 protected override String Operator { get { return "*"; }}
794                 public override double EvaluateNumber (BaseIterator iter)
795                 {
796                         return _left.EvaluateNumber (iter) * _right.EvaluateNumber (iter);
797                 }
798         }
799
800         internal class ExprDIV : ExprNumeric
801         {
802                 public ExprDIV (Expression left, Expression right) : base (left, right) {}
803                 protected override String Operator { get { return " div "; }}
804                 public override double EvaluateNumber (BaseIterator iter)
805                 {
806                         return _left.EvaluateNumber (iter) / _right.EvaluateNumber (iter);
807                 }
808         }
809
810         internal class ExprMOD : ExprNumeric
811         {
812                 public ExprMOD (Expression left, Expression right) : base (left, right) {}
813                 protected override String Operator { get { return "%"; }}
814                 [MonoTODO]
815                 public override double EvaluateNumber (BaseIterator iter)
816                 {
817                         return _left.EvaluateNumber (iter) % _right.EvaluateNumber (iter);      // TODO: spec?
818                 }
819         }
820
821         internal class ExprNEG : Expression
822         {
823                 Expression _expr;
824                 public ExprNEG (Expression expr)
825                 {
826                         _expr = expr;
827                 }
828                 public override String ToString () { return "- " + _expr.ToString (); }
829                 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
830                 public override object Evaluate (BaseIterator iter)
831                 {
832                         return - _expr.EvaluateNumber (iter);
833                 }
834                 
835                 public override double EvaluateNumber (BaseIterator iter)
836                 {
837                         return - _expr.EvaluateNumber (iter);
838                 }
839
840                 internal override bool IsPositional {
841                         get { return _expr.IsPositional; }
842                 }
843         }
844
845
846         internal abstract class NodeSet : Expression
847         {
848                 public override XPathResultType ReturnType { get { return XPathResultType.NodeSet; }}
849         }
850
851         internal class ExprUNION : NodeSet
852         {
853                 public readonly Expression left, right;
854                 public ExprUNION (Expression left, Expression right)
855                 {
856                         this.left = left;
857                         this.right = right;
858                 }
859                 public override String ToString () { return left.ToString ()+ " | " + right.ToString (); }
860                 public override object Evaluate (BaseIterator iter)
861                 {
862                         BaseIterator iterLeft = left.EvaluateNodeSet (iter);
863                         BaseIterator iterRight = right.EvaluateNodeSet (iter);
864                         return new UnionIterator (iter, iterLeft, iterRight);
865                 }
866
867                 internal override bool NeedAbsoluteMatching {
868                         get { return left.NeedAbsoluteMatching || right.NeedAbsoluteMatching; }
869                 }
870
871                 internal override XPathNodeType EvaluatedNodeType {
872                         get { return left.EvaluatedNodeType == right.EvaluatedNodeType ? left.EvaluatedNodeType : XPathNodeType.All; }
873                 }
874
875                 internal override bool IsPositional {
876                         get { return left.IsPositional || right.IsPositional; }
877                 }
878         }
879
880         internal class ExprSLASH : NodeSet
881         {
882                 public readonly Expression left;
883                 public readonly NodeSet right;
884                 public ExprSLASH (Expression left, NodeSet right)
885                 {
886                         this.left = left;
887                         this.right = right;
888                 }
889                 public override String ToString () { return left.ToString ()+ "/" + right.ToString (); }
890                 public override object Evaluate (BaseIterator iter)
891                 {
892                         BaseIterator iterLeft = left.EvaluateNodeSet (iter);
893                         return new SlashIterator (iterLeft, right);
894                 }
895
896                 public override bool RequireSorting { get { return left.RequireSorting || right.RequireSorting; } }
897
898                 internal override bool NeedAbsoluteMatching {
899                         get { return true; }
900                 }
901
902                 internal override XPathNodeType EvaluatedNodeType {
903                         get { return right.EvaluatedNodeType; }
904                 }
905
906                 internal override bool IsPositional {
907                         get { return left.IsPositional || right.IsPositional; }
908                 }
909         }
910         
911         internal class ExprSLASH2 : NodeSet {
912                 public readonly Expression left;
913                 public readonly NodeSet right;
914                         
915                 static NodeTest DescendantOrSelfStar = new NodeTypeTest (Axes.DescendantOrSelf, XPathNodeType.All);
916
917                 public ExprSLASH2 (Expression left, NodeSet right)
918                 {
919                         this.left = left;
920                         this.right = right;
921                 }
922                 public override String ToString () { return left.ToString ()+ "//" + right.ToString (); }
923                 public override object Evaluate (BaseIterator iter)
924                 {
925                         return new SlashIterator (
926                                 new SlashIterator (
927                                         left.EvaluateNodeSet (iter),
928                                         DescendantOrSelfStar
929                                 ),
930                                 right
931                         );
932                 }
933
934                 public override bool RequireSorting { get { return left.RequireSorting || right.RequireSorting; } }
935
936                 internal override bool NeedAbsoluteMatching {
937                         get { return true; }
938                 }
939
940                 internal override XPathNodeType EvaluatedNodeType {
941                         get { return right.EvaluatedNodeType; }
942                 }
943
944                 internal override bool IsPositional {
945                         get { return left.IsPositional || right.IsPositional; }
946                 }
947         }
948
949         internal class ExprRoot : NodeSet
950         {
951                 public override String ToString () { return ""; }
952                 public override object Evaluate (BaseIterator iter)
953                 {
954                         XPathNavigator navRoot = iter.Current.Clone ();
955                         navRoot.MoveToRoot ();
956                         return new SelfIterator (navRoot, iter.NamespaceManager);
957                 }
958
959                 internal override bool NeedAbsoluteMatching {
960                         get { return false; }
961                 }
962
963                 internal override XPathNodeType EvaluatedNodeType {
964                         get { return XPathNodeType.Root; }
965                 }
966         }
967
968         internal enum Axes
969         {
970                 Ancestor,
971                 AncestorOrSelf,
972                 Attribute,
973                 Child,
974                 Descendant,
975                 DescendantOrSelf,
976                 Following,
977                 FollowingSibling,
978                 Namespace,
979                 Parent,
980                 Preceding,
981                 PrecedingSibling,
982                 Self,
983         }
984
985         internal class AxisSpecifier
986         {
987                 protected Axes _axis;
988                 public AxisSpecifier (Axes axis)
989                 {
990                         _axis = axis;
991                 }
992                 public XPathNodeType NodeType
993                 {
994                         get
995                         {
996                                 switch (_axis)
997                                 {
998                                         case Axes.Namespace:
999                                                 return XPathNodeType.Namespace;
1000                                         case Axes.Attribute:
1001                                                 return XPathNodeType.Attribute;
1002                                         default:
1003                                                 return XPathNodeType.Element;
1004                                 }
1005                         }
1006                 }
1007                 public override string ToString ()
1008                 {
1009                         switch (_axis)
1010                         {
1011                                 case Axes.Ancestor:
1012                                         return "ancestor";
1013                                 case Axes.AncestorOrSelf:
1014                                         return "ancestor-or-self";
1015                                 case Axes.Attribute:
1016                                         return "attribute";
1017                                 case Axes.Child:
1018                                         return "child";
1019                                 case Axes.Descendant:
1020                                         return "descendant";
1021                                 case Axes.DescendantOrSelf:
1022                                         return "descendant-or-self";
1023                                 case Axes.Following:
1024                                         return "following";
1025                                 case Axes.FollowingSibling:
1026                                         return "following-sibling";
1027                                 case Axes.Namespace:
1028                                         return "namespace";
1029                                 case Axes.Parent:
1030                                         return "parent";
1031                                 case Axes.Preceding:
1032                                         return "preceeding";
1033                                 case Axes.PrecedingSibling:
1034                                         return "preceeding-sibling";
1035                                 case Axes.Self:
1036                                         return "self";
1037                                 default:
1038                                         throw new IndexOutOfRangeException ();
1039                         }
1040                 }
1041                 public Axes Axis { get { return _axis; }}
1042                 public virtual SimpleIterator Evaluate (BaseIterator iter)
1043                 {
1044                         switch (_axis)
1045                         {
1046                                 case Axes.Ancestor:
1047                                         return new AncestorIterator (iter);
1048                                 case Axes.AncestorOrSelf:
1049                                         return new AncestorOrSelfIterator (iter);
1050                                 case Axes.Attribute:
1051                                         return new AttributeIterator (iter);
1052                                 case Axes.Child:
1053                                         return new ChildIterator (iter);
1054                                 case Axes.Descendant:
1055                                         return new DescendantIterator (iter);
1056                                 case Axes.DescendantOrSelf:
1057                                         return new DescendantOrSelfIterator (iter);
1058                                 case Axes.Following:
1059                                         return new FollowingIterator (iter);
1060                                 case Axes.FollowingSibling:
1061                                         return new FollowingSiblingIterator (iter);
1062                                 case Axes.Namespace:
1063                                         return new NamespaceIterator (iter);
1064                                 case Axes.Parent:
1065                                         return new ParentIterator (iter);
1066                                 case Axes.Preceding:
1067                                         return new PrecedingIterator (iter);
1068                                 case Axes.PrecedingSibling:
1069                                         return new PrecedingSiblingIterator (iter);
1070                                 case Axes.Self:
1071                                         return new SelfIterator (iter);
1072                                 default:
1073                                         throw new IndexOutOfRangeException ();
1074                         }
1075                 }
1076         }
1077
1078         internal abstract class NodeTest : NodeSet
1079         {
1080                 protected AxisSpecifier _axis;
1081                 public NodeTest (Axes axis)
1082                 {
1083                         _axis = new AxisSpecifier (axis);
1084                 }
1085                 public abstract bool Match (XmlNamespaceManager nsm, XPathNavigator nav);
1086                 public AxisSpecifier Axis { get { return _axis; }}
1087                 public override object Evaluate (BaseIterator iter)
1088                 {
1089                         SimpleIterator iterAxis = _axis.Evaluate (iter);
1090                         return new AxisIterator (iterAxis, this);
1091                 }
1092                 
1093                 public abstract void GetInfo (out string name, out string ns, out XPathNodeType nodetype, XmlNamespaceManager nsm);
1094
1095                 public override bool RequireSorting {
1096                         get {
1097                                 switch (_axis.Axis) {
1098                                 case Axes.Ancestor:
1099                                 case Axes.AncestorOrSelf:
1100                                 case Axes.Preceding:
1101                                 case Axes.PrecedingSibling:
1102                                 case Axes.Namespace:
1103                                         return true;
1104                                 default:
1105                                         return false;
1106                                 }
1107                         }
1108
1109                 }
1110
1111                 internal override XPathNodeType EvaluatedNodeType {
1112                         get { return _axis.NodeType; }
1113                 }
1114         }
1115
1116         internal class NodeTypeTest : NodeTest
1117         {
1118                 public readonly XPathNodeType type;
1119                 protected String _param;
1120                 public NodeTypeTest (Axes axis) : base (axis)
1121                 {
1122                         this.type = _axis.NodeType;
1123                 }
1124                 public NodeTypeTest (Axes axis, XPathNodeType type) : base (axis)
1125                 {
1126                         this.type = type;
1127                 }
1128                 [MonoTODO]
1129                 public NodeTypeTest (Axes axis, XPathNodeType type, String param) : base (axis)
1130                 {
1131                         this.type = type;
1132                         _param = param;
1133                         if (param != null && type != XPathNodeType.ProcessingInstruction)
1134                                 throw new XPathException ("No argument allowed for "+ToString (type)+"() test");        // TODO: better description
1135                 }
1136
1137                 public override String ToString ()
1138                 {
1139                         String strType = ToString (type);
1140                         if (type == XPathNodeType.ProcessingInstruction && _param != null)
1141                                 strType += "('" + _param + "')";
1142                         else
1143                                 strType += "()";
1144
1145                         return _axis.ToString () + "::" + strType;
1146                 }
1147
1148                 private static String ToString (XPathNodeType type)
1149                 {
1150                         switch (type)
1151                         {
1152                                 case XPathNodeType.Comment:
1153                                         return "comment";
1154                                 case XPathNodeType.Text:
1155                                         return "text";
1156                                 case XPathNodeType.ProcessingInstruction:
1157                                         return "processing-instruction";
1158                                 case XPathNodeType.All:
1159                                 case XPathNodeType.Attribute:
1160                                 case XPathNodeType.Element:
1161                                 case XPathNodeType.Namespace:
1162                                         return "node";
1163                                 default:
1164                                         return "node-type [" + type.ToString () + "]";
1165                         }
1166                 }
1167
1168                 public override bool Match (XmlNamespaceManager nsm, XPathNavigator nav)
1169                 {
1170                         XPathNodeType nodeType = nav.NodeType;
1171                         switch (type)
1172                         {
1173                                 case XPathNodeType.All:
1174                                         return true;
1175
1176                                 case XPathNodeType.ProcessingInstruction:
1177                                         if (nodeType != XPathNodeType.ProcessingInstruction)
1178                                                 return false;
1179                                         if (_param != null && nav.Name != _param)
1180                                                 return false;
1181                                         return true;
1182                                 
1183                                 default:
1184                                         return type == nodeType;
1185                         }
1186                 }
1187                 
1188                 public override void GetInfo (out string name, out string ns, out XPathNodeType nodetype, XmlNamespaceManager nsm)
1189                 {
1190                         name = _param;
1191                         ns = null;
1192                         nodetype = type;
1193                 }
1194         }
1195
1196         internal class NodeNameTest : NodeTest
1197         {
1198                 protected XmlQualifiedName _name;
1199                 protected readonly bool resolvedName = false;
1200                 public NodeNameTest (Axes axis, XmlQualifiedName name, IStaticXsltContext ctx) : base (axis)
1201                 {
1202                         if (ctx != null) {
1203                                 name = ctx.LookupQName (name.ToString ());
1204                                 resolvedName = true;
1205                         }
1206                         _name = name;
1207                 }
1208                 
1209                 public NodeNameTest (Axes axis, XmlQualifiedName name, bool resolvedName) : base (axis)
1210                 {
1211                         _name = name;
1212                         resolvedName = resolvedName;
1213                 }
1214                 public override String ToString () { return _axis.ToString () + "::" + _name.ToString (); }
1215                 
1216                 public XmlQualifiedName Name { get { return _name; } }
1217                 [MonoTODO]
1218                 public override bool Match (XmlNamespaceManager nsm, XPathNavigator nav)
1219                 {
1220                         // must be the correct node type
1221                         if (nav.NodeType != _axis.NodeType)
1222                                 return false;
1223
1224                         if (_name.Name != "")
1225                         {
1226                                 // test the local part of the name first
1227                                 if (_name.Name != nav.LocalName)
1228                                         return false;
1229                         }
1230
1231                         // get the prefix for the given name
1232                         String strURI1 = "";
1233                         if (nsm != null && _name.Namespace != "")
1234                         {
1235                                 if (resolvedName)
1236                                         strURI1 = _name.Namespace;
1237                                 else
1238                                         strURI1 = nsm.LookupNamespace (_name.Namespace);        // TODO: check to see if this returns null or ""
1239                                 if (strURI1 == null)
1240                                         throw new XPathException ("Invalid namespace prefix: "+_name.Namespace);
1241                         }
1242
1243                         string strURI = nav.NamespaceURI;
1244                         if (strURI == null && strURI1 == "")    // TODO: remove when bug #26855 fixed
1245                                 return true;
1246
1247                         // test the prefixes
1248                         return strURI1 == nav.NamespaceURI;
1249                 }
1250                 
1251                 public override void GetInfo (out string name, out string ns, out XPathNodeType nodetype, XmlNamespaceManager nsm)
1252                 {
1253                         // must be the correct node type
1254                         nodetype = _axis.NodeType;
1255                         
1256                         if (_name.Name != "")
1257                                 name = _name.Name;
1258                         else
1259                                 name = null;
1260                         ns = "";
1261                         if (nsm != null && _name.Namespace != "") {
1262                                 if (resolvedName)
1263                                         ns = _name.Namespace;
1264                                 else
1265                                         ns = nsm.LookupNamespace (_name.Namespace);     // TODO: check to see if this returns null or ""
1266                                 if (ns == null)
1267                                         throw new XPathException ("Invalid namespace prefix: "+_name.Namespace);
1268                         }
1269                 }
1270         }
1271
1272         internal class ExprFilter : NodeSet
1273         {
1274                 public readonly Expression expr, pred;
1275                 
1276                 public ExprFilter (Expression expr, Expression pred)
1277                 {
1278                         this.expr = expr;
1279                         this.pred = pred;
1280                 }
1281                 
1282                 internal Expression LeftHandSide {get{return expr;}}
1283                 public override String ToString () { return "(" + expr.ToString () + ")[" + pred.ToString () + "]"; }
1284                 public override object Evaluate (BaseIterator iter)
1285                 {
1286                         BaseIterator iterExpr = expr.EvaluateNodeSet (iter);
1287                         return new PredicateIterator (iterExpr, pred);
1288                 }
1289
1290                 internal override bool NeedAbsoluteMatching {
1291                         get { return expr.NeedAbsoluteMatching; }
1292                 }
1293
1294                 internal override XPathNodeType EvaluatedNodeType {
1295                         get { return expr.EvaluatedNodeType; }
1296                 }
1297
1298                 internal override bool IsPositional {
1299                         get {
1300                                 if (pred.ReturnType == XPathResultType.Number)
1301                                         return true;
1302                                 return expr.IsPositional || pred.IsPositional;
1303                         }
1304                 }
1305         }
1306
1307         internal class ExprNumber : Expression
1308         {
1309                 protected double _value;
1310                 public ExprNumber (double value)
1311                 {
1312                         _value = value;
1313                 }
1314                 public override String ToString () { return _value.ToString (); }
1315                 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
1316                 public override object Evaluate (BaseIterator iter)
1317                 {
1318                         return _value;
1319                 }
1320                 
1321                 public override double EvaluateNumber (BaseIterator iter)
1322                 {
1323                         return _value;
1324                 }
1325
1326                 internal override bool IsPositional {
1327                         get { return false; }
1328                 }
1329         }
1330
1331         internal class ExprLiteral : Expression
1332         {
1333                 protected String _value;
1334                 public ExprLiteral (String value)
1335                 {
1336                         _value = value;
1337                 }
1338                 public string Value { get { return _value; } }
1339                 public override String ToString () { return "'" + _value + "'"; }
1340                 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
1341                 public override object Evaluate (BaseIterator iter)
1342                 {
1343                         return _value;
1344                 }
1345                 
1346                 public override string EvaluateString (BaseIterator iter)
1347                 {
1348                         return _value;
1349                 }
1350         }
1351
1352         internal class ExprVariable : Expression
1353         {
1354                 protected XmlQualifiedName _name;
1355                 protected bool resolvedName = false;
1356                 public ExprVariable (XmlQualifiedName name, IStaticXsltContext ctx)
1357                 {
1358                         if (ctx != null) {
1359                                 name = ctx.LookupQName (name.ToString ());
1360                                 resolvedName = true;
1361                         }
1362                         
1363                         _name = name;
1364                 }
1365                 public override String ToString () { return "$" + _name.ToString (); }
1366                 public override XPathResultType ReturnType { get { return XPathResultType.Any; }}
1367                 public override XPathResultType GetReturnType (BaseIterator iter)
1368                 {
1369                         return XPathResultType.Any;
1370                 }
1371                 
1372                 public override object Evaluate (BaseIterator iter)
1373                 {
1374                         IXsltContextVariable var = null;
1375                         
1376                         XsltContext context = iter.NamespaceManager as XsltContext;
1377                         if (context != null) {
1378                                 if (resolvedName)
1379                                         var = context.ResolveVariable (_name);
1380                                 else
1381                                         var = context.ResolveVariable (_name.Namespace, _name.Name);
1382                         }
1383                         
1384                         if (var == null)
1385                                 throw new XPathException ("variable "+_name.ToString ()+" not found");
1386                         object objResult = var.Evaluate (context);
1387                         XPathNodeIterator iterResult = objResult as XPathNodeIterator;
1388                         if (iterResult != null)
1389                                 return iterResult.Clone ();
1390                         return objResult;
1391                 }
1392         }
1393
1394         internal class ExprParens : Expression
1395         {
1396                 protected Expression _expr;
1397                 public ExprParens (Expression expr)
1398                 {
1399                         _expr = expr;
1400                 }
1401                 public override String ToString () { return "(" + _expr.ToString () + ")"; }
1402                 public override XPathResultType ReturnType { get { return _expr.ReturnType; }}
1403                 public override object Evaluate (BaseIterator iter)
1404                 {
1405                         object o = (_expr.Evaluate (iter));
1406                         BaseIterator predBase = o as BaseIterator;
1407                         if (predBase != null)
1408                                 return new ParensIterator (predBase);
1409                         else
1410                                 return o;
1411                 }
1412
1413                 internal override bool NeedAbsoluteMatching {
1414                         get { return _expr.NeedAbsoluteMatching; }
1415                 }
1416
1417                 internal override XPathNodeType EvaluatedNodeType {
1418                         get { return _expr.EvaluatedNodeType; }
1419                 }
1420
1421                 internal override bool IsPositional {
1422                         get { return _expr.IsPositional; }
1423                 }
1424         }
1425
1426         internal class FunctionArguments
1427         {
1428                 protected Expression _arg;
1429                 protected FunctionArguments _tail;
1430                 public FunctionArguments (Expression arg, FunctionArguments tail)
1431                 {
1432                         _arg = arg;
1433                         _tail = tail;
1434                 }
1435                 public Expression Arg
1436                 {
1437                         get { return _arg; }
1438                 }
1439                 public FunctionArguments Tail
1440                 {
1441                         get { return _tail; }
1442                 }
1443                 
1444                 public void ToArrayList (ArrayList a)
1445                 {
1446                         FunctionArguments cur = this;
1447                         
1448                         do {
1449                                 a.Add (cur._arg);
1450                                 cur = cur._tail;
1451                         } while (cur != null);
1452                         
1453                 }
1454         }
1455
1456         internal class ExprFunctionCall : Expression
1457         {
1458                 protected readonly XmlQualifiedName _name;
1459                 protected readonly bool resolvedName = false;
1460                 protected readonly ArrayList _args = new ArrayList ();
1461                 public ExprFunctionCall (XmlQualifiedName name, FunctionArguments args, IStaticXsltContext ctx)
1462                 {
1463                         if (ctx != null) {
1464                                 name = ctx.LookupQName (name.ToString ());
1465                                 resolvedName = true;
1466                         }
1467                         
1468                         _name = name;
1469                         if (args != null)
1470                                 args.ToArrayList (_args);
1471                 }
1472                 
1473                 public static Expression Factory (XmlQualifiedName name, FunctionArguments args, IStaticXsltContext ctx)
1474                 {
1475                         if (name.Namespace != null && name.Namespace != "")
1476                                 return new ExprFunctionCall (name, args, ctx);
1477                         
1478                         switch (name.Name) {
1479                                 case "last": return new XPathFunctionLast (args);
1480                                 case "position": return new XPathFunctionPosition (args);
1481                                 case "count": return new XPathFunctionCount (args);
1482                                 case "id": return new XPathFunctionId (args);
1483                                 case "local-name": return new XPathFunctionLocalName (args);
1484                                 case "namespace-uri": return new XPathFunctionNamespaceUri (args);
1485                                 case "name": return new XPathFunctionName (args);
1486                                 case "string": return new XPathFunctionString (args);
1487                                 case "concat": return new XPathFunctionConcat (args);
1488                                 case "starts-with": return new XPathFunctionStartsWith (args);
1489                                 case "contains": return new XPathFunctionContains (args);
1490                                 case "substring-before": return new XPathFunctionSubstringBefore (args);
1491                                 case "substring-after": return new XPathFunctionSubstringAfter (args);
1492                                 case "substring": return new XPathFunctionSubstring (args);
1493                                 case "string-length": return new XPathFunctionStringLength (args);
1494                                 case "normalize-space": return new XPathFunctionNormalizeSpace (args);
1495                                 case "translate": return new XPathFunctionTranslate (args);
1496                                 case "boolean": return new XPathFunctionBoolean (args);
1497                                 case "not": return new XPathFunctionNot (args);
1498                                 case "true": return new XPathFunctionTrue (args);
1499                                 case "false": return new XPathFunctionFalse (args);
1500                                 case "lang": return new XPathFunctionLang (args);
1501                                 case "number": return new XPathFunctionNumber (args);
1502                                 case "sum": return new XPathFunctionSum (args);
1503                                 case "floor": return new XPathFunctionFloor (args);
1504                                 case "ceiling": return new XPathFunctionCeil (args);
1505                                 case "round": return new XPathFunctionRound (args);
1506                         }
1507                         return new ExprFunctionCall (name, args, ctx);
1508                 }
1509                 
1510                 public override String ToString ()
1511                 {
1512                         String strArgs = "";
1513                         for (int i = 0; i < _args.Count; i++) {
1514                                 Expression arg = (Expression) _args [i];
1515                                 if (strArgs != "")
1516                                         strArgs += ", ";
1517                                 strArgs += arg.ToString ();
1518                         }
1519                         return _name.ToString () + '(' + strArgs + ')';
1520                 }
1521                 public override XPathResultType ReturnType { get { return XPathResultType.Any; }}
1522                 public override XPathResultType GetReturnType (BaseIterator iter)
1523                 {
1524                         return XPathResultType.Any;
1525                 }
1526                 
1527                 private XPathResultType [] GetArgTypes (BaseIterator iter)
1528                 {
1529                         // TODO: can we cache these? what if the types depend on the nsm?
1530                         XPathResultType [] rgArgs = new XPathResultType [_args.Count];
1531                         for (int iArg = 0; iArg < _args.Count; iArg++)
1532                                 rgArgs [iArg] = ((Expression) _args [iArg]).GetReturnType (iter);
1533                         return rgArgs;
1534                 }
1535                 public override object Evaluate (BaseIterator iter)
1536                 {
1537                         XPathResultType [] rgTypes = GetArgTypes (iter);
1538                         IXsltContextFunction func = null;
1539                         XsltContext context = iter.NamespaceManager as XsltContext;
1540                         if (context != null) {
1541                                 if (resolvedName)
1542                                         func = context.ResolveFunction (_name, rgTypes);
1543                                 else
1544                                         func = context.ResolveFunction (_name.Namespace, _name.Name, rgTypes);
1545                         }
1546
1547                         if (func == null)
1548                                 throw new XPathException ("function "+_name.ToString ()+" not found");
1549
1550                         object [] rgArgs = new object [_args.Count];
1551                         if (func.Maxargs != 0)
1552                         {
1553                                 XPathResultType [] rgFuncTypes = func.ArgTypes;
1554                                 for (int iArg = 0; iArg < _args.Count; iArg ++)
1555                                 {
1556                                         XPathResultType typeArg;
1557                                         if (rgFuncTypes == null)
1558                                                 typeArg = XPathResultType.Any;
1559                                         else if (iArg < rgFuncTypes.Length)
1560                                                 typeArg = rgFuncTypes [iArg];
1561                                         else
1562                                                 typeArg = rgFuncTypes [rgFuncTypes.Length - 1];
1563
1564                                         Expression arg = (Expression) _args [iArg];
1565                                         object result = arg.EvaluateAs (iter, typeArg);
1566                                         rgArgs [iArg] = result;
1567                                 }
1568                         }
1569                         return func.Invoke (context, rgArgs, iter.Current);
1570                 }
1571         }
1572 }