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