In .:
[mono.git] / mcs / class / System.XML / System.Xml.XPath / DefaultContext.cs
1 //
2 // System.Xml.XPath.DefaultContext & support classes
3 //
4 // Author:
5 //   Piers Haken (piersh@friskit.com)
6 //
7 // (C) 2002 Piers Haken
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30 using System;
31 using System.Collections;
32 using System.Globalization;
33 using System.Xml;
34 using System.Xml.XPath;
35 using System.Xml.Xsl;
36 using System.Text;
37
38 namespace System.Xml.XPath
39 {
40         internal class XPathFunctions
41         {
42                 public static bool ToBoolean (object arg)
43                 {
44                         if (arg == null)
45                                 throw new ArgumentNullException ();
46                         if (arg is bool)
47                                 return (bool) arg;
48                         if (arg is double)
49                         {
50                                 double dArg = (double) arg;
51                                 return (dArg != 0.0 && !double.IsNaN (dArg));
52                         }
53                         if (arg is string)
54                                 return ((string) arg).Length != 0;
55                         if (arg is BaseIterator)
56                         {
57                                 BaseIterator iter = (BaseIterator) arg;
58                                 return iter.MoveNext ();
59                         }
60                         if (arg is XPathNavigator)
61                         {
62                                 return ToBoolean (((XPathNavigator) arg).SelectChildren (XPathNodeType.All));
63                         }
64                         throw new ArgumentException ();
65                 }
66
67                 public static bool ToBoolean (bool b)
68                 {
69                         return b;
70                 }
71
72                 public static bool ToBoolean (double d)
73                 {
74                         return d != 0.0 && d != double.NaN;
75                 }
76
77                 public static bool ToBoolean (string s)
78                 {
79                         return s != null && s.Length > 0;
80                 }
81
82                 public static bool ToBoolean (BaseIterator iter)
83                 {
84                         return iter != null && iter.MoveNext ();
85                 }
86
87                 public static string ToString (object arg)
88                 {
89                         if (arg == null)
90                                 throw new ArgumentNullException ();
91                         if (arg is string)
92                                 return (string) arg;
93                         if (arg is bool)
94                                 return ((bool) arg) ? "true" : "false";
95                         if (arg is double)
96                                 return ToString ((double) arg);
97                         if (arg is BaseIterator)
98                         {
99                                 BaseIterator iter = (BaseIterator) arg;
100                                 if (!iter.MoveNext ())
101                                         return "";
102                                 return iter.Current.Value;
103                         }
104                         if (arg is XPathNavigator)
105                         {
106                                 return ((XPathNavigator) arg).Value;
107                         }
108                         throw new ArgumentException ();
109                 }
110
111                 public static string ToString (double d)
112                 {
113                         // See XPath 1.0 section 4.2
114                         if (d == Double.NegativeInfinity)
115                                 return "-Infinity";
116                         if (d == Double.PositiveInfinity)
117                                 return "Infinity";
118                         // FIXME:
119                         // Return string in roundtrip format (currently it
120                         // rather breaks things, so we don't use it until
121                         // System.Double gets fixed.)
122 #if TARGET_JVM
123                         return d.ToString ("R", System.Globalization.NumberFormatInfo.InvariantInfo);
124 #else
125                         return ((double) d).ToString (System.Globalization.NumberFormatInfo.InvariantInfo);
126 #endif
127                 }
128
129                 public static double ToNumber (object arg)
130                 {
131                         if (arg == null)
132                                 throw new ArgumentNullException ();
133                         if (arg is BaseIterator || arg is XPathNavigator)
134                                 arg = ToString (arg);   // follow on
135                         if (arg is string) {
136                                 string s = arg as string;
137                                 return ToNumber (s); // use explicit overload
138                         }
139                         if (arg is double)
140                                 return (double) arg;
141                         if (arg is bool)
142                                 return Convert.ToDouble ((bool) arg);
143                         throw new ArgumentException ();
144                 }
145                 
146                 public static double ToNumber (string arg)
147                 {
148                         if (arg == null)
149                                 throw new ArgumentNullException ();
150                         string s = arg.Trim (XmlChar.WhitespaceChars);
151                         if (s.Length == 0)
152                                 return double.NaN;
153                         try {
154                                 return XmlConvert.ToDouble (s);
155                         } catch (System.OverflowException) {
156                                 return double.NaN;
157                         } catch (System.FormatException) {
158                                 return double.NaN;
159                         }
160                 }
161         }
162
163         internal abstract class XPathFunction : Expression
164         {
165                 public XPathFunction (FunctionArguments args) {}
166         }
167
168
169         internal class XPathFunctionLast : XPathFunction
170         {
171                 public XPathFunctionLast (FunctionArguments args) : base (args)
172                 {
173                         if (args != null)
174                                 throw new XPathException ("last takes 0 args");
175                 }
176                 
177                 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
178
179                 internal override bool Peer {
180                         get { return true; }
181                 }
182
183                 public override object Evaluate (BaseIterator iter)
184                 {
185                         return (double) iter.Count;
186                 }
187
188                 public override string ToString ()
189                 {
190                         return "last()";
191                 }
192
193                 internal override bool IsPositional {
194                         get { return true; }
195                 }
196         }
197
198
199         internal class XPathFunctionPosition : XPathFunction
200         {
201                 public XPathFunctionPosition (FunctionArguments args) : base (args)
202                 {
203                         if (args != null)
204                                 throw new XPathException ("position takes 0 args");
205                 }
206                 
207                 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
208
209                 internal override bool Peer {
210                         get { return true; }
211                 }
212
213                 public override object Evaluate (BaseIterator iter)
214                 {
215                         return (double) iter.CurrentPosition;
216                 }
217
218                 public override string ToString ()
219                 {
220                         return "position()";
221                 }
222
223                 internal override bool IsPositional {
224                         get { return true; }
225                 }
226         }
227
228
229         internal class XPathFunctionCount : XPathFunction
230         {
231                 Expression arg0;
232                 
233                 public XPathFunctionCount (FunctionArguments args) : base (args)
234                 {
235                         if (args == null || args.Tail != null)
236                                 throw new XPathException ("count takes 1 arg");
237                         
238                         arg0 = args.Arg;
239                 }
240
241                 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
242
243                 internal override bool Peer {
244                         get { return arg0.Peer; }
245                 }
246
247                 public override object Evaluate (BaseIterator iter)
248                 {
249                         return (double) arg0.EvaluateNodeSet (iter).Count;
250                 }
251                 
252                 public override bool EvaluateBoolean (BaseIterator iter)
253                 {
254                         if (arg0.GetReturnType (iter) == XPathResultType.NodeSet)
255                                 return arg0.EvaluateBoolean (iter);
256                         
257                         return arg0.EvaluateNodeSet (iter).MoveNext ();
258                 }
259
260                 public override string ToString ()
261                 {
262                         return "count(" + arg0.ToString () + ")";
263                 }
264         }
265
266
267         internal class XPathFunctionId : XPathFunction
268         {
269                 Expression arg0;
270                 
271                 public XPathFunctionId (FunctionArguments args) : base (args)
272                 {
273                         if (args == null || args.Tail != null)
274                                 throw new XPathException ("id takes 1 arg");
275                         
276                         arg0 = args.Arg;
277                 }
278
279                 public Expression Id { get { return arg0; } }
280                 
281                 private static char [] rgchWhitespace = {' ', '\t', '\r', '\n'};
282                 public override XPathResultType ReturnType { get { return XPathResultType.NodeSet; }}
283
284                 internal override bool Peer {
285                         get { return arg0.Peer; }
286                 }
287
288                 public override object Evaluate (BaseIterator iter)
289                 {
290                         String strArgs;
291                         object val = arg0.Evaluate (iter);
292                         
293                         BaseIterator valItr = val as BaseIterator;
294                         if (valItr != null)
295                         {
296                                 strArgs = "";
297                                 while (valItr.MoveNext ())
298                                         strArgs += valItr.Current.Value + " ";
299                         }
300                         else
301                                 strArgs = XPathFunctions.ToString (val);
302                         
303                         XPathNavigator n = iter.Current.Clone ();
304                         ArrayList rgNodes = new ArrayList ();
305                         string [] ids = strArgs.Split (rgchWhitespace);
306                         for (int i = 0; i < ids.Length; i++)
307                                 if (n.MoveToId (ids [i]))
308                                         rgNodes.Add (n.Clone ());
309
310                         rgNodes.Sort (XPathNavigatorComparer.Instance);
311                         return new ListIterator (iter, rgNodes);
312                 }
313
314                 public override string ToString ()
315                 {
316                         return "id(" + arg0.ToString () + ")";
317                 }
318         }
319
320         internal class XPathFunctionLocalName : XPathFunction
321         {
322                 Expression arg0;
323                 
324                 public XPathFunctionLocalName (FunctionArguments args) : base (args)
325                 {
326                         if (args != null) {
327                                 arg0 = args.Arg;
328                                 if (args.Tail != null)
329                                         throw new XPathException ("local-name takes 1 or zero args");
330                         }
331                 }
332                 
333                 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
334
335                 internal override bool Peer {
336                         get { return arg0 != null ? arg0.Peer : true; }
337                 }
338                 
339                 public override object Evaluate (BaseIterator iter)
340                 {
341                         if (arg0 == null)
342                                 return iter.Current.LocalName;
343                         
344                         BaseIterator argNs = arg0.EvaluateNodeSet (iter);
345                         if (argNs == null || !argNs.MoveNext ())
346                                 return "";
347                         return argNs.Current.LocalName;
348                 }
349
350                 public override string ToString ()
351                 {
352                         return "local-name(" + arg0.ToString () + ")";
353                 }
354         }
355
356
357         internal class XPathFunctionNamespaceUri : XPathFunction
358         {
359                 Expression arg0;
360                 
361                 public XPathFunctionNamespaceUri (FunctionArguments args) : base (args)
362                 {
363                         if (args != null) {
364                                 arg0 = args.Arg;
365                                 if (args.Tail != null)
366                                         throw new XPathException ("namespace-uri takes 1 or zero args");
367                         }
368                 }
369
370                 internal override bool Peer {
371                         get { return arg0 != null ? arg0.Peer : true; }
372                 }
373                 
374                 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
375                 
376                 public override object Evaluate (BaseIterator iter)
377                 {
378                         if (arg0 == null)
379                                 return iter.Current.NamespaceURI;
380                         
381                         BaseIterator argNs = arg0.EvaluateNodeSet (iter);
382                         if (argNs == null || !argNs.MoveNext ())
383                                 return "";
384                         return argNs.Current.NamespaceURI;
385                 }
386
387                 public override string ToString ()
388                 {
389                         return "namespace-uri(" + arg0.ToString () + ")";
390                 }
391         }
392
393
394         internal class XPathFunctionName : XPathFunction
395         {
396                 Expression arg0;
397                 
398                 public XPathFunctionName (FunctionArguments args) : base (args)
399                 {
400                         if (args != null) {
401                                 arg0 = args.Arg;
402                                 if (args.Tail != null)
403                                         throw new XPathException ("name takes 1 or zero args");
404                         }
405                 }
406                 
407                 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
408
409                 internal override bool Peer {
410                         get { return arg0 != null ? arg0.Peer : true; }
411                 }
412                 
413                 public override object Evaluate (BaseIterator iter)
414                 {
415                         if (arg0 == null)
416                                 return iter.Current.Name;
417                         
418                         BaseIterator argNs = arg0.EvaluateNodeSet (iter);
419                         if (argNs == null || !argNs.MoveNext ())
420                                 return "";
421                         return argNs.Current.Name;
422                 }
423
424                 public override string ToString ()
425                 {
426                         return String.Concat ("name(",
427                                  arg0 != null ? arg0.ToString () : String.Empty,
428                                  ")");
429                 }
430         }
431
432
433         internal class XPathFunctionString : XPathFunction
434         {
435                 Expression arg0;
436                 
437                 public XPathFunctionString (FunctionArguments args) : base (args)
438                 {
439                         if (args != null) {
440                                 arg0 = args.Arg;
441                                 if (args.Tail != null)
442                                         throw new XPathException ("string takes 1 or zero args");
443                         }
444                 }
445                 
446                 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
447
448                 internal override bool Peer {
449                         get { return arg0 != null ? arg0.Peer : true; }
450                 }
451
452                 public override object Evaluate (BaseIterator iter)
453                 {
454                         if (arg0 == null)
455                                 return iter.Current.Value;
456                         return arg0.EvaluateString (iter);
457                 }
458
459                 public override string ToString ()
460                 {
461                         return "string(" + arg0.ToString () + ")";
462                 }
463         }
464
465
466         internal class XPathFunctionConcat : XPathFunction
467         {
468                 ArrayList rgs;
469                 
470                 public XPathFunctionConcat (FunctionArguments args) : base (args)
471                 {
472                         if (args == null || args.Tail == null)
473                                 throw new XPathException ("concat takes 2 or more args");
474                         
475                         args.ToArrayList (rgs = new ArrayList ());
476                 }
477                 
478                 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
479
480                 internal override bool Peer {
481                         get {
482                                 for (int i = 0; i < rgs.Count; i++)
483                                         if (!((Expression) rgs [i]).Peer)
484                                                 return false;
485                                 return true;
486                         }
487                 }
488                 
489                 public override object Evaluate (BaseIterator iter)
490                 {
491                         StringBuilder sb = new StringBuilder ();
492                         
493                         int len = rgs.Count;
494                         for (int i = 0; i < len; i++)
495                                 sb.Append (((Expression)rgs[i]).EvaluateString (iter));
496                         
497                         return sb.ToString ();
498                 }
499
500                 public override string ToString ()
501                 {
502                         StringBuilder sb = new StringBuilder ();
503                         sb.Append ("concat(");
504                         for (int i = 0; i < rgs.Count - 1; i++) {
505                                 sb.AppendFormat (CultureInfo.InvariantCulture, "{0}", rgs [i].ToString ());
506                                 sb.Append (',');
507                         }
508                         sb.AppendFormat (CultureInfo.InvariantCulture, "{0}", rgs [rgs.Count - 1].ToString ());
509                         sb.Append (')');
510                         return sb.ToString ();
511                 }
512         }
513
514
515         internal class XPathFunctionStartsWith : XPathFunction
516         {
517                 Expression arg0, arg1;
518                 
519                 public XPathFunctionStartsWith (FunctionArguments args) : base (args)
520                 {
521                         if (args == null || args.Tail == null || args.Tail.Tail != null)
522                                 throw new XPathException ("starts-with takes 2 args");
523                         
524                         arg0 = args.Arg;
525                         arg1 = args.Tail.Arg;
526                 }
527                 
528                 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
529
530                 internal override bool Peer {
531                         get { return arg0.Peer && arg1.Peer; }
532                 }
533                 
534                 public override object Evaluate (BaseIterator iter)
535                 {
536                         return arg0.EvaluateString (iter).StartsWith (arg1.EvaluateString (iter));
537                 }
538
539                 public override string ToString ()
540                 {
541                         return String.Concat ("starts-with(", arg0.ToString (), ",", arg1.ToString (), ")");
542                 }
543         }
544
545
546         internal class XPathFunctionContains : XPathFunction
547         {
548                 Expression arg0, arg1;
549                 
550                 public XPathFunctionContains (FunctionArguments args) : base (args)
551                 {
552                         if (args == null || args.Tail == null || args.Tail.Tail != null)
553                                 throw new XPathException ("contains takes 2 args");
554                         
555                         arg0 = args.Arg;
556                         arg1 = args.Tail.Arg;
557                 }
558                 
559                 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
560
561                 internal override bool Peer {
562                         get { return arg0.Peer && arg1.Peer; }
563                 }
564                 
565                 public override object Evaluate (BaseIterator iter)
566                 {
567                         return arg0.EvaluateString (iter).IndexOf (arg1.EvaluateString (iter)) != -1;
568                 }
569
570                 public override string ToString ()
571                 {
572                         return String.Concat ("contains(", arg0.ToString (), ",", arg1.ToString (), ")");
573                 }
574         }
575
576
577         internal class XPathFunctionSubstringBefore : XPathFunction
578         {
579                 Expression arg0, arg1;
580                 
581                 public XPathFunctionSubstringBefore (FunctionArguments args) : base (args)
582                 {
583                         if (args == null || args.Tail == null || args.Tail.Tail != null)
584                                 throw new XPathException ("substring-before takes 2 args");
585                         
586                         arg0 = args.Arg;
587                         arg1 = args.Tail.Arg;
588                 }
589                 
590                 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
591
592                 internal override bool Peer {
593                         get { return arg0.Peer && arg1.Peer; }
594                 }
595                 
596                 public override object Evaluate (BaseIterator iter)
597                 {
598                         string str1 = arg0.EvaluateString (iter);
599                         string str2 = arg1.EvaluateString (iter);
600                         int ich = str1.IndexOf (str2);
601                         if (ich <= 0)
602                                 return "";
603                         return str1.Substring (0, ich);
604                 }
605
606                 public override string ToString ()
607                 {
608                         return String.Concat ("substring-before(", arg0.ToString (), ",", arg1.ToString (), ")");
609                 }
610         }
611
612
613         internal class XPathFunctionSubstringAfter : XPathFunction
614         {
615                 Expression arg0, arg1;
616                 
617                 public XPathFunctionSubstringAfter (FunctionArguments args) : base (args)
618                 {
619                         if (args == null || args.Tail == null || args.Tail.Tail != null)
620                                 throw new XPathException ("substring-after takes 2 args");
621                         
622                         arg0 = args.Arg;
623                         arg1 = args.Tail.Arg;
624                 }
625                 
626                 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
627
628                 internal override bool Peer {
629                         get { return arg0.Peer && arg1.Peer; }
630                 }
631                 
632                 public override object Evaluate (BaseIterator iter)
633                 {
634                         string str1 = arg0.EvaluateString (iter);
635                         string str2 = arg1.EvaluateString (iter);
636                         int ich = str1.IndexOf (str2);
637                         if (ich < 0)
638                                 return "";
639                         return str1.Substring (ich + str2.Length);
640                 }
641
642                 public override string ToString ()
643                 {
644                         return String.Concat ("substring-after(", arg0.ToString (), ",", arg1.ToString (), ")");
645                 }
646         }
647
648
649         internal class XPathFunctionSubstring : XPathFunction
650         {
651                 Expression arg0, arg1, arg2;
652                 
653                 public XPathFunctionSubstring (FunctionArguments args) : base (args)
654                 {
655                         if (args == null || args.Tail == null || (args.Tail.Tail != null && args.Tail.Tail.Tail != null))
656                                 throw new XPathException ("substring takes 2 or 3 args");
657                         
658                         arg0 = args.Arg;
659                         arg1 = args.Tail.Arg;
660                         if (args.Tail.Tail != null)
661                                 arg2= args.Tail.Tail.Arg;
662                 }
663                 
664                 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
665
666                 internal override bool Peer {
667                         get { return arg0.Peer && arg1.Peer && (arg2 != null ? arg2.Peer : true); }
668                 }
669                 
670                 public override object Evaluate (BaseIterator iter)
671                 {
672                         string str = arg0.EvaluateString (iter);
673                         double ich = Math.Round (arg1.EvaluateNumber (iter)) - 1;
674                         if (Double.IsNaN (ich) ||
675                                 Double.IsNegativeInfinity (ich) ||
676                                 ich >= (double) str.Length)
677                                 return "";
678
679                         if (arg2 == null)
680                         {
681                                 if (ich < 0)
682                                         ich = 0.0;
683                                 return str.Substring ((int) ich);
684                         }
685                         else
686                         {
687                                 double cch = Math.Round (arg2.EvaluateNumber (iter));
688                                 if (Double.IsNaN (cch))
689                                         return "";
690                                 if (ich < 0.0 || cch < 0.0) 
691                                 {
692                                         cch = ich + cch;
693                                         if (cch <= 0.0)
694                                                 return "";
695                                         ich = 0.0;
696                                 }
697                                 double cchMax = (double) str.Length - ich;
698                                 if (cch > cchMax)
699                                         cch = cchMax;
700                                 return str.Substring ((int) ich, (int) cch);
701                         }
702                 }
703
704                 public override string ToString ()
705                 {
706                         return String.Concat (new string [] {
707                                 "substring(", arg0.ToString (), ",", arg1.ToString (), ",", arg2.ToString (), ")"});
708                 }
709         }
710
711
712         internal class XPathFunctionStringLength : XPathFunction
713         {
714                 Expression arg0;
715                 
716                 public XPathFunctionStringLength (FunctionArguments args) : base (args)
717                 {
718                         if (args != null) {
719                                 arg0 = args.Arg;
720                                 if (args.Tail != null)
721                                         throw new XPathException ("string-length takes 1 or zero args");
722                         }
723                 }
724                 
725                 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
726
727                 internal override bool Peer {
728                         get { return arg0 != null ? arg0.Peer : true; }
729                 }
730                 
731                 public override object Evaluate (BaseIterator iter)
732                 {
733                         string str;
734                         if (arg0 != null)
735                                 str = arg0.EvaluateString (iter);
736                         else
737                                 str = iter.Current.Value;
738                         return (double) str.Length;
739                 }
740
741                 public override string ToString ()
742                 {
743                         return String.Concat (new string [] {
744                                 "string-length(", arg0.ToString (), ")"});
745                 }
746         }
747
748
749         internal class XPathFunctionNormalizeSpace : XPathFunction
750         {
751                 Expression arg0;
752                 
753                 public XPathFunctionNormalizeSpace (FunctionArguments args) : base (args)
754                 {
755                         if (args != null) {
756                                 arg0 = args.Arg;
757                                 if (args.Tail != null)
758                                         throw new XPathException ("normalize-space takes 1 or zero args");
759                         }
760                 }
761                 
762                 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
763
764                 internal override bool Peer {
765                         get { return arg0 !=null ? arg0.Peer : true; }
766                 }
767
768                 public override object Evaluate (BaseIterator iter)
769                 {
770                         string str;
771                         if (arg0 != null)
772                                 str = arg0.EvaluateString (iter);
773                         else
774                                 str = iter.Current.Value;
775                         System.Text.StringBuilder sb = new System.Text.StringBuilder ();
776                         bool fSpace = false;
777                         for (int i = 0; i < str.Length; i++) {
778                                 char ch = str [i];
779                                 if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
780                                 {
781                                         fSpace = true;
782                                 }
783                                 else
784                                 {
785                                         if (fSpace)
786                                         {
787                                                 fSpace = false;
788                                                 if (sb.Length > 0)
789                                                         sb.Append (' ');
790                                         }
791                                         sb.Append (ch);
792                                 }
793                         }
794                         return sb.ToString ();
795                 }
796
797                 public override string ToString ()
798                 {
799                         return String.Concat (new string [] {
800                                 "normalize-space(",
801                                 arg0 != null ? arg0.ToString () : String.Empty,
802                                 ")"});
803                 }
804         }
805
806
807         internal class XPathFunctionTranslate : XPathFunction
808         {
809                 Expression arg0, arg1, arg2;
810                 
811                 public XPathFunctionTranslate (FunctionArguments args) : base (args)
812                 {
813                         if (args == null || args.Tail == null || args.Tail.Tail == null || args.Tail.Tail.Tail != null)
814                                 throw new XPathException ("translate takes 3 args");
815                         
816                         arg0 = args.Arg;
817                         arg1 = args.Tail.Arg;
818                         arg2= args.Tail.Tail.Arg;
819                 }
820                 
821                 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
822
823                 internal override bool Peer {
824                         get { return arg0.Peer && arg1.Peer && arg2.Peer; }
825                 }
826                 
827                 public override object Evaluate (BaseIterator iter)
828                 {
829                         string s0 = arg0.EvaluateString (iter);
830                         string s1 = arg1.EvaluateString (iter);
831                         string s2 = arg2.EvaluateString (iter);
832                         
833                         StringBuilder ret = new StringBuilder (s0.Length);
834                                 
835                         int pos = 0, len = s0.Length, s2len = s2.Length;
836                         
837                         while (pos < len) {
838                                 int idx = s1.IndexOf (s0 [pos]);
839                                 
840                                 if (idx != -1) {
841                                         if (idx < s2len)
842                                                 ret.Append (s2 [idx]);
843                                 }
844                                 else
845                                         ret.Append (s0 [pos]);
846                                 
847                                 pos++;
848                         }
849                         
850                         return ret.ToString ();
851                 }
852
853                 public override string ToString ()
854                 {
855                         return String.Concat (new string [] {
856                                 "string-length(",
857                                 arg0.ToString (), ",",
858                                 arg1.ToString (), ",",
859                                 arg2.ToString (), ")"});
860                 }
861         }
862
863         internal abstract class XPathBooleanFunction : XPathFunction
864         {
865                 public XPathBooleanFunction (FunctionArguments args) : base (args)
866                 {
867                 }
868
869                 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
870
871                 public override object StaticValue {
872                         get { return StaticValueAsBoolean; }
873                 }
874         }
875
876         internal class XPathFunctionBoolean : XPathBooleanFunction
877         {
878                 Expression arg0;
879                 
880                 public XPathFunctionBoolean (FunctionArguments args) : base (args)
881                 {
882                         if (args != null) {
883                                 arg0 = args.Arg;
884                                 if (args.Tail != null)
885                                         throw new XPathException ("boolean takes 1 or zero args");
886                         }
887                 }
888                 
889                 internal override bool Peer {
890                         get { return arg0 != null ? arg0.Peer : true; }
891                 }
892
893                 public override object Evaluate (BaseIterator iter)
894                 {
895                         if (arg0 == null)
896                                 return XPathFunctions.ToBoolean (iter.Current.Value);
897                         return arg0.EvaluateBoolean (iter);
898                 }
899
900                 public override string ToString ()
901                 {
902                         return String.Concat (new string [] {"boolean(", arg0.ToString (), ")"});
903                 }
904         }
905
906
907         internal class XPathFunctionNot : XPathBooleanFunction
908         {
909                 Expression arg0;
910                 
911                 public XPathFunctionNot (FunctionArguments args) : base (args)
912                 {
913                         if (args == null || args.Tail != null)
914                                 throw new XPathException ("not takes one arg");
915                         arg0 = args.Arg;
916                 }
917                 
918                 internal override bool Peer {
919                         get { return arg0.Peer; }
920                 }
921
922                 public override object Evaluate (BaseIterator iter)
923                 {
924                         return !arg0.EvaluateBoolean (iter);
925                 }
926
927                 public override string ToString ()
928                 {
929                         return String.Concat (new string [] {"not(", arg0.ToString (), ")"});
930                 }
931         }
932
933
934         internal class XPathFunctionTrue : XPathBooleanFunction
935         {
936                 public XPathFunctionTrue (FunctionArguments args) : base (args)
937                 {
938                         if (args != null)
939                                 throw new XPathException ("true takes 0 args");
940                 }
941
942                 public override bool HasStaticValue {
943                         get { return true; }
944                 }
945
946                 public override bool StaticValueAsBoolean {
947                         get { return true; }
948                 }
949                 
950                 internal override bool Peer {
951                         get { return true; }
952                 }
953
954                 public override object Evaluate (BaseIterator iter)
955                 {
956                         return true;
957                 }
958
959                 public override string ToString ()
960                 {
961                         return "true()";
962                 }
963         }
964
965
966         internal class XPathFunctionFalse : XPathBooleanFunction
967         {
968                 public XPathFunctionFalse (FunctionArguments args) : base (args)
969                 {
970                         if (args != null)
971                                 throw new XPathException ("false takes 0 args");
972                 }
973
974                 public override bool HasStaticValue {
975                         get { return true; }
976                 }
977
978                 public override bool StaticValueAsBoolean {
979                         get { return false; }
980                 }
981
982                 internal override bool Peer {
983                         get { return true; }
984                 }
985
986                 public override object Evaluate (BaseIterator iter)
987                 {
988                         return false;
989                 }
990
991                 public override string ToString ()
992                 {
993                         return "false()";
994                 }
995         }
996
997
998         internal class XPathFunctionLang : XPathFunction
999         {
1000                 Expression arg0;
1001                 
1002                 public XPathFunctionLang (FunctionArguments args) : base (args)
1003                 {
1004                         if (args == null || args.Tail != null)
1005                                 throw new XPathException ("lang takes one arg");
1006                         arg0 = args.Arg;
1007                 }
1008
1009                 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
1010
1011                 internal override bool Peer {
1012                         get { return arg0.Peer; }
1013                 }
1014
1015                 public override object Evaluate (BaseIterator iter)
1016                 {
1017                         return EvaluateBoolean (iter);
1018                 }
1019
1020                 public override bool EvaluateBoolean (BaseIterator iter)
1021                 {
1022                         string lang = arg0.EvaluateString (iter).ToLower (CultureInfo.InvariantCulture);
1023                         string actualLang = iter.Current.XmlLang.ToLower (CultureInfo.InvariantCulture);
1024                         
1025                         return lang == actualLang || lang == (actualLang.Split ('-')[0]);
1026                 }
1027
1028                 public override string ToString ()
1029                 {
1030                         return String.Concat (new string [] {"lang(", arg0.ToString (), ")"});
1031                 }
1032         }
1033
1034         internal abstract class XPathNumericFunction : XPathFunction
1035         {
1036                 internal XPathNumericFunction (FunctionArguments args)
1037                         : base (args)
1038                 {
1039                 }
1040
1041                 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
1042
1043                 public override object StaticValue {
1044                         get { return StaticValueAsNumber; }
1045                 }
1046         }
1047
1048         internal class XPathFunctionNumber : XPathNumericFunction
1049         {
1050                 Expression arg0;
1051                 
1052                 public XPathFunctionNumber (FunctionArguments args) : base (args)
1053                 {
1054                         if (args != null) {
1055                                 arg0 = args.Arg;
1056                                 if (args.Tail != null)
1057                                         throw new XPathException ("number takes 1 or zero args");
1058                         }
1059                 }
1060
1061                 public override Expression Optimize ()
1062                 {
1063                         if (arg0 == null)
1064                                 return this;
1065                         arg0 = arg0.Optimize ();
1066                         return !arg0.HasStaticValue ?
1067                                 (Expression) this :
1068                                 new ExprNumber (StaticValueAsNumber);
1069                 }
1070
1071                 public override bool HasStaticValue {
1072                         get { return arg0 != null && arg0.HasStaticValue; }
1073                 }
1074
1075                 public override double StaticValueAsNumber {
1076                         get { return arg0 != null ? arg0.StaticValueAsNumber : 0; }
1077                 }
1078
1079                 internal override bool Peer {
1080                         get { return arg0 != null ? arg0.Peer : true; }
1081                 }
1082
1083                 public override object Evaluate (BaseIterator iter)
1084                 {
1085                         if (arg0 == null)
1086                                 return XPathFunctions.ToNumber (iter.Current.Value);
1087                         return arg0.EvaluateNumber (iter);
1088                 }
1089
1090                 public override string ToString ()
1091                 {
1092                         return String.Concat (new string [] {"number(", arg0.ToString (), ")"});
1093                 }
1094         }
1095
1096
1097         internal class XPathFunctionSum : XPathNumericFunction
1098         {
1099                 Expression arg0;
1100                 
1101                 public XPathFunctionSum (FunctionArguments args) : base (args)
1102                 {
1103                         if (args == null || args.Tail != null)
1104                                 throw new XPathException ("sum takes one arg");
1105                         arg0 = args.Arg;
1106                 }
1107                 
1108                 internal override bool Peer {
1109                         get { return arg0.Peer; }
1110                 }
1111
1112                 public override object Evaluate (BaseIterator iter)
1113                 {
1114                         XPathNodeIterator itr = arg0.EvaluateNodeSet (iter);
1115                         
1116                         double sum = 0;
1117                         while (itr.MoveNext ())
1118                                 sum += XPathFunctions.ToNumber (itr.Current.Value);
1119                         
1120                         return sum;
1121                 }
1122
1123                 public override string ToString ()
1124                 {
1125                         return String.Concat (new string [] {"sum(", arg0.ToString (), ")"});
1126                 }
1127         }
1128
1129
1130         internal class XPathFunctionFloor : XPathNumericFunction
1131         {
1132                 Expression arg0;
1133                 
1134                 public XPathFunctionFloor (FunctionArguments args) : base (args)
1135                 {
1136                         if (args == null || args.Tail != null)
1137                                 throw new XPathException ("floor takes one arg");
1138                         arg0 = args.Arg;
1139                 }
1140
1141                 public override bool HasStaticValue {
1142                         get { return arg0.HasStaticValue; }
1143                 }
1144
1145                 public override double StaticValueAsNumber {
1146                         get { return HasStaticValue ? Math.Floor (arg0.StaticValueAsNumber) : 0; }
1147                 }
1148                 
1149                 internal override bool Peer {
1150                         get { return arg0.Peer; }
1151                 }
1152
1153                 public override object Evaluate (BaseIterator iter)
1154                 {
1155                         return Math.Floor (arg0.EvaluateNumber (iter));
1156                 }
1157
1158                 public override string ToString ()
1159                 {
1160                         return String.Concat (new string [] {"floor(", arg0.ToString (), ")"});
1161                 }
1162         }
1163
1164
1165         internal class XPathFunctionCeil : XPathNumericFunction
1166         {
1167                 Expression arg0;
1168                 
1169                 public XPathFunctionCeil (FunctionArguments args) : base (args)
1170                 {
1171                         if (args == null || args.Tail != null)
1172                                 throw new XPathException ("ceil takes one arg");
1173                         arg0 = args.Arg;
1174                 }
1175
1176                 public override bool HasStaticValue {
1177                         get { return arg0.HasStaticValue; }
1178                 }
1179
1180                 public override double StaticValueAsNumber {
1181                         get { return HasStaticValue ? Math.Ceiling (arg0.StaticValueAsNumber) : 0; }
1182                 }
1183                 
1184                 internal override bool Peer {
1185                         get { return arg0.Peer; }
1186                 }
1187
1188                 public override object Evaluate (BaseIterator iter)
1189                 {
1190                         return Math.Ceiling (arg0.EvaluateNumber (iter));
1191                 }
1192
1193                 public override string ToString ()
1194                 {
1195                         return String.Concat (new string [] {"ceil(", arg0.ToString (), ")"});
1196                 }
1197         }
1198
1199
1200         internal class XPathFunctionRound : XPathNumericFunction
1201         {
1202                 Expression arg0;
1203                 
1204                 public XPathFunctionRound (FunctionArguments args) : base (args)
1205                 {
1206                         if (args == null || args.Tail != null)
1207                                 throw new XPathException ("round takes one arg");
1208                         arg0 = args.Arg;
1209                 }
1210                 
1211                 public override bool HasStaticValue {
1212                         get { return arg0.HasStaticValue; }
1213                 }
1214
1215                 public override double StaticValueAsNumber {
1216                         get { return HasStaticValue ? Round (arg0.StaticValueAsNumber) : 0; }
1217                 }
1218
1219                 internal override bool Peer {
1220                         get { return arg0.Peer; }
1221                 }
1222
1223                 public override object Evaluate (BaseIterator iter)
1224                 {
1225                         return Round (arg0.EvaluateNumber (iter));
1226                 }
1227
1228                 private double Round (double arg)
1229                 {
1230                         if (arg < -0.5 || arg > 0)
1231                                 return Math.Floor (arg + 0.5);
1232                         return Math.Round (arg);
1233                 }
1234
1235                 public override string ToString ()
1236                 {
1237                         return String.Concat (new string [] {"round(", arg0.ToString (), ")"});
1238                 }
1239         }
1240 }