2004-02-05 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 using System;
10 using System.Collections;
11 using System.Xml;
12 using System.Xml.XPath;
13 using System.Xml.Xsl;
14 using System.Text;
15
16 namespace System.Xml.XPath
17 {
18         internal class XPathFunctions
19         {
20                 public static bool ToBoolean (object arg)
21                 {
22                         if (arg == null)
23                                 throw new ArgumentNullException ();
24                         if (arg is bool)
25                                 return (bool) arg;
26                         if (arg is double)
27                         {
28                                 double dArg = (double) arg;
29                                 return (dArg != 0.0 && !double.IsNaN (dArg));
30                         }
31                         if (arg is string)
32                                 return ((string) arg).Length != 0;
33                         if (arg is BaseIterator)
34                         {
35                                 BaseIterator iter = (BaseIterator) arg;
36                                 return iter.MoveNext ();
37                         }
38                         throw new ArgumentException ();
39                 }
40                 [MonoTODO]
41                 public static string ToString (object arg)
42                 {
43                         if (arg == null)
44                                 throw new ArgumentNullException ();
45                         if (arg is string)
46                                 return (string) arg;
47                         if (arg is bool)
48                                 return ((bool) arg) ? "true" : "false";
49                         if (arg is double)
50                                 return ((double) arg).ToString ("R", System.Globalization.NumberFormatInfo.InvariantInfo);
51                         if (arg is BaseIterator)
52                         {
53                                 BaseIterator iter = (BaseIterator) arg;
54                                 if (!iter.MoveNext ())
55                                         return "";
56                                 return iter.Current.Value;
57                         }
58                         throw new ArgumentException ();
59                 }
60                 [MonoTODO]
61                 public static double ToNumber (object arg)
62                 {
63                         if (arg == null)
64                                 throw new ArgumentNullException ();
65                         if (arg is BaseIterator)
66                                 arg = ToString (arg);   // follow on
67                         if (arg is string)
68                         {
69                                 string s = arg as string;
70                                 if (s.Length == 0)
71                                         return double.NaN;
72                                 try
73                                 {
74                                         return XmlConvert.ToDouble (s); // TODO: spec? convert string to number
75                                 }
76                                 catch (System.FormatException)
77                                 {
78                                         return double.NaN;
79                                 }
80                         }
81                         if (arg is double)
82                                 return (double) arg;
83                         if (arg is bool)
84                                 return Convert.ToDouble ((bool) arg);
85                         throw new ArgumentException ();
86                 }
87                 
88                 public static double ToNumber (string arg)
89                 {
90                         if (arg == null)
91                                 throw new ArgumentNullException ();
92                         if (arg.Length == 0)
93                                 return double.NaN;
94                         try {
95                                 return XmlConvert.ToDouble ((string) arg.Trim (XmlChar.WhitespaceChars));
96                         } catch (System.OverflowException) {
97                                 return double.NaN;
98                         } catch (System.FormatException) {
99                                 return double.NaN;
100                         }
101                 }
102         }
103
104         internal abstract class XPathFunction : Expression
105         {
106                 public XPathFunction (FunctionArguments args) {}
107         }
108
109
110         internal class XPathFunctionLast : XPathFunction
111         {
112                 public XPathFunctionLast (FunctionArguments args) : base (args)
113                 {
114                         if (args != null)
115                                 throw new XPathException ("last takes 0 args");
116                 }
117                 
118                 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
119
120                 public override object Evaluate (BaseIterator iter)
121                 {
122                         return (double) iter.Count;
123                 }
124
125                 public override string ToString ()
126                 {
127                         return "last()";
128                 }
129         }
130
131
132         internal class XPathFunctionPosition : XPathFunction
133         {
134                 public XPathFunctionPosition (FunctionArguments args) : base (args)
135                 {
136                         if (args != null)
137                                 throw new XPathException ("position takes 0 args");
138                 }
139                 
140                 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
141
142                 public override object Evaluate (BaseIterator iter)
143                 {
144                         return (double) iter.CurrentPosition;
145                 }
146
147                 public override string ToString ()
148                 {
149                         return "position()";
150                 }
151         }
152
153
154         internal class XPathFunctionCount : XPathFunction
155         {
156                 Expression arg0;
157                 
158                 public XPathFunctionCount (FunctionArguments args) : base (args)
159                 {
160                         if (args == null || args.Tail != null)
161                                 throw new XPathException ("count takes 1 arg");
162                         
163                         arg0 = args.Arg;
164                 }
165
166                 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
167                 
168                 public override object Evaluate (BaseIterator iter)
169                 {
170                         return (double) arg0.EvaluateNodeSet (iter).Count;
171                 }
172                 
173                 public override bool EvaluateBoolean (BaseIterator iter)
174                 {
175                         if (arg0.GetReturnType (iter) == XPathResultType.NodeSet)
176                                 return arg0.EvaluateBoolean (iter);
177                         
178                         return arg0.EvaluateNodeSet (iter).MoveNext ();
179                 }
180
181                 public override string ToString ()
182                 {
183                         return "count(" + arg0.ToString () + ")";
184                 }
185         }
186
187
188         internal class XPathFunctionId : XPathFunction
189         {
190                 Expression arg0;
191                 
192                 public XPathFunctionId (FunctionArguments args) : base (args)
193                 {
194                         if (args == null || args.Tail != null)
195                                 throw new XPathException ("id takes 1 arg");
196                         
197                         arg0 = args.Arg;
198                 }
199
200                 public Expression Id { get { return arg0; } }
201                 
202                 private static char [] rgchWhitespace = {' ', '\t', '\r', '\n'};
203                 public override XPathResultType ReturnType { get { return XPathResultType.NodeSet; }}
204
205                 public override object Evaluate (BaseIterator iter)
206                 {
207                         String strArgs;
208                         object val = arg0.Evaluate (iter);
209                         
210                         BaseIterator valItr = val as BaseIterator;
211                         if (valItr != null)
212                         {
213                                 strArgs = "";
214                                 while (valItr.MoveNext ())
215                                         strArgs += valItr.Current.Value + " ";
216                         }
217                         else
218                                 strArgs = XPathFunctions.ToString (val);
219                         
220                         XPathNavigator n = iter.Current.Clone ();
221                         ArrayList rgNodes = new ArrayList ();
222                         foreach (string strArg in strArgs.Split (rgchWhitespace))
223                         {
224                                 if (n.MoveToId (strArg)) {
225                                         rgNodes.Add (n.Clone ());
226                                 }
227                         }
228                         rgNodes.Sort (XPathNavigatorComparer.Instance);
229                         return new EnumeratorIterator (iter, rgNodes.GetEnumerator ());
230                 }
231
232                 public override string ToString ()
233                 {
234                         return "id(" + arg0.ToString () + ")";
235                 }
236         }
237
238         internal class XPathFunctionLocalName : XPathFunction
239         {
240                 Expression arg0;
241                 
242                 public XPathFunctionLocalName (FunctionArguments args) : base (args)
243                 {
244                         if (args != null) {
245                                 arg0 = args.Arg;
246                                 if (args.Tail != null)
247                                         throw new XPathException ("local-name takes 1 or zero args");
248                         }
249                 }
250                 
251                 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
252                 
253                 public override object Evaluate (BaseIterator iter)
254                 {
255                         if (arg0 == null)
256                                 return iter.Current.LocalName;
257                         
258                         BaseIterator argNs = arg0.EvaluateNodeSet (iter);
259                         if (argNs == null || !argNs.MoveNext ())
260                                 return "";
261                         return argNs.Current.LocalName;
262                 }
263
264                 public override string ToString ()
265                 {
266                         return "local-name(" + arg0.ToString () + ")";
267                 }
268         }
269
270
271         internal class XPathFunctionNamespaceUri : XPathFunction
272         {
273                 Expression arg0;
274                 
275                 public XPathFunctionNamespaceUri (FunctionArguments args) : base (args)
276                 {
277                         if (args != null) {
278                                 arg0 = args.Arg;
279                                 if (args.Tail != null)
280                                         throw new XPathException ("namespace-uri takes 1 or zero args");
281                         }
282                 }
283                 
284                 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
285                 
286                 public override object Evaluate (BaseIterator iter)
287                 {
288                         if (arg0 == null)
289                                 return iter.Current.NamespaceURI;
290                         
291                         BaseIterator argNs = arg0.EvaluateNodeSet (iter);
292                         if (argNs == null || !argNs.MoveNext ())
293                                 return "";
294                         return argNs.Current.NamespaceURI;
295                 }
296
297                 public override string ToString ()
298                 {
299                         return "namespace-uri(" + arg0.ToString () + ")";
300                 }
301         }
302
303
304         internal class XPathFunctionName : XPathFunction
305         {
306                 Expression arg0;
307                 
308                 public XPathFunctionName (FunctionArguments args) : base (args)
309                 {
310                         if (args != null) {
311                                 arg0 = args.Arg;
312                                 if (args.Tail != null)
313                                         throw new XPathException ("name takes 1 or zero args");
314                         }
315                 }
316                 
317                 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
318                 
319                 public override object Evaluate (BaseIterator iter)
320                 {
321                         if (arg0 == null)
322                                 return iter.Current.Name;
323                         
324                         BaseIterator argNs = arg0.EvaluateNodeSet (iter);
325                         if (argNs == null || !argNs.MoveNext ())
326                                 return "";
327                         return argNs.Current.Name;
328                 }
329
330                 public override string ToString ()
331                 {
332                         return "name(" + arg0.ToString () + ")";
333                 }
334         }
335
336
337         internal class XPathFunctionString : XPathFunction
338         {
339                 Expression arg0;
340                 
341                 public XPathFunctionString (FunctionArguments args) : base (args)
342                 {
343                         if (args != null) {
344                                 arg0 = args.Arg;
345                                 if (args.Tail != null)
346                                         throw new XPathException ("boolean takes 1 or zero args");
347                         }
348                 }
349                 
350                 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
351
352                 public override object Evaluate (BaseIterator iter)
353                 {
354                         if (arg0 == null)
355                                 return iter.Current.Value;
356                         return arg0.EvaluateString (iter);
357                 }
358
359                 public override string ToString ()
360                 {
361                         return "string(" + arg0.ToString () + ")";
362                 }
363         }
364
365
366         internal class XPathFunctionConcat : XPathFunction
367         {
368                 ArrayList rgs;
369                 
370                 public XPathFunctionConcat (FunctionArguments args) : base (args)
371                 {
372                         if (args == null || args.Tail == null)
373                                 throw new XPathException ("concat takes 2 or more args");
374                         
375                         args.ToArrayList (rgs = new ArrayList ());
376                 }
377                 
378                 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
379                 
380                 public override object Evaluate (BaseIterator iter)
381                 {
382                         StringBuilder sb = new StringBuilder ();
383                         
384                         int len = rgs.Count;
385                         for (int i = 0; i < len; i++)
386                                 sb.Append (((Expression)rgs[i]).EvaluateString (iter));
387                         
388                         return sb.ToString ();
389                 }
390
391                 public override string ToString ()
392                 {
393                         StringBuilder sb = new StringBuilder ();
394                         sb.Append ("concat(");
395                         for (int i = 0; i < rgs.Count - 1; i++) {
396                                 sb.Append (rgs [i].ToString ());
397                                 sb.Append (',');
398                         }
399                         sb.Append (rgs [rgs.Count - 1].ToString ());
400                         sb.Append (')');
401                         return sb.ToString ();
402                 }
403         }
404
405
406         internal class XPathFunctionStartsWith : XPathFunction
407         {
408                 Expression arg0, arg1;
409                 
410                 public XPathFunctionStartsWith (FunctionArguments args) : base (args)
411                 {
412                         if (args == null || args.Tail == null || args.Tail.Tail != null)
413                                 throw new XPathException ("starts-with takes 2 args");
414                         
415                         arg0 = args.Arg;
416                         arg1 = args.Tail.Arg;
417                 }
418                 
419                 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
420                 
421                 public override object Evaluate (BaseIterator iter)
422                 {
423                         return arg0.EvaluateString (iter).StartsWith (arg1.EvaluateString (iter));
424                 }
425
426                 public override string ToString ()
427                 {
428                         return String.Concat ("starts-with(", arg0.ToString (), ",", arg1.ToString (), ")");
429                 }
430         }
431
432
433         internal class XPathFunctionContains : XPathFunction
434         {
435                 Expression arg0, arg1;
436                 
437                 public XPathFunctionContains (FunctionArguments args) : base (args)
438                 {
439                         if (args == null || args.Tail == null || args.Tail.Tail != null)
440                                 throw new XPathException ("contains takes 2 args");
441                         
442                         arg0 = args.Arg;
443                         arg1 = args.Tail.Arg;
444                 }
445                 
446                 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
447                 
448                 public override object Evaluate (BaseIterator iter)
449                 {
450                         return arg0.EvaluateString (iter).IndexOf (arg1.EvaluateString (iter)) != -1;
451                 }
452
453                 public override string ToString ()
454                 {
455                         return String.Concat ("contains(", arg0.ToString (), ",", arg1.ToString (), ")");
456                 }
457         }
458
459
460         internal class XPathFunctionSubstringBefore : XPathFunction
461         {
462                 Expression arg0, arg1;
463                 
464                 public XPathFunctionSubstringBefore (FunctionArguments args) : base (args)
465                 {
466                         if (args == null || args.Tail == null || args.Tail.Tail != null)
467                                 throw new XPathException ("substring-before takes 2 args");
468                         
469                         arg0 = args.Arg;
470                         arg1 = args.Tail.Arg;
471                 }
472                 
473                 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
474                 
475                 public override object Evaluate (BaseIterator iter)
476                 {
477                         string str1 = arg0.EvaluateString (iter);
478                         string str2 = arg1.EvaluateString (iter);
479                         int ich = str1.IndexOf (str2);
480                         if (ich <= 0)
481                                 return "";
482                         return str1.Substring (0, ich);
483                 }
484
485                 public override string ToString ()
486                 {
487                         return String.Concat ("substring-before(", arg0.ToString (), ",", arg1.ToString (), ")");
488                 }
489         }
490
491
492         internal class XPathFunctionSubstringAfter : XPathFunction
493         {
494                 Expression arg0, arg1;
495                 
496                 public XPathFunctionSubstringAfter (FunctionArguments args) : base (args)
497                 {
498                         if (args == null || args.Tail == null || args.Tail.Tail != null)
499                                 throw new XPathException ("substring-after takes 2 args");
500                         
501                         arg0 = args.Arg;
502                         arg1 = args.Tail.Arg;
503                 }
504                 
505                 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
506                 
507                 public override object Evaluate (BaseIterator iter)
508                 {
509                         string str1 = arg0.EvaluateString (iter);
510                         string str2 = arg1.EvaluateString (iter);
511                         int ich = str1.IndexOf (str2);
512                         if (ich < 0)
513                                 return "";
514                         return str1.Substring (ich + str2.Length);
515                 }
516
517                 public override string ToString ()
518                 {
519                         return String.Concat ("substring-after(", arg0.ToString (), ",", arg1.ToString (), ")");
520                 }
521         }
522
523
524         internal class XPathFunctionSubstring : XPathFunction
525         {
526                 Expression arg0, arg1, arg2;
527                 
528                 public XPathFunctionSubstring (FunctionArguments args) : base (args)
529                 {
530                         if (args == null || args.Tail == null || (args.Tail.Tail != null && args.Tail.Tail.Tail != null))
531                                 throw new XPathException ("substring takes 2 or 3 args");
532                         
533                         arg0 = args.Arg;
534                         arg1 = args.Tail.Arg;
535                         if (args.Tail.Tail != null)
536                                 arg2= args.Tail.Tail.Arg;
537                 }
538                 
539                 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
540                 
541                 public override object Evaluate (BaseIterator iter)
542                 {
543                         string str = arg0.EvaluateString (iter);
544                         double ich = Math.Round (arg1.EvaluateNumber (iter)) - 1;
545                         if (Double.IsNaN (ich) ||
546                                 Double.IsNegativeInfinity (ich) ||
547                                 ich >= (double) str.Length)
548                                 return "";
549
550                         if (arg2 == null)
551                         {
552                                 if (ich < 0)
553                                         ich = 0.0;
554                                 return str.Substring ((int) ich);
555                         }
556                         else
557                         {
558                                 double cch = Math.Round (arg2.EvaluateNumber (iter));
559                                 if (Double.IsNaN (cch))
560                                         return "";
561                                 if (ich < 0.0 || cch < 0.0) 
562                                 {
563                                         cch = ich + cch;
564                                         if (cch <= 0.0)
565                                                 return "";
566                                         ich = 0.0;
567                                 }
568                                 double cchMax = (double) str.Length - ich;
569                                 if (cch > cchMax)
570                                         cch = cchMax;
571                                 return str.Substring ((int) ich, (int) cch);
572                         }
573                 }
574
575                 public override string ToString ()
576                 {
577                         return String.Concat (new string [] {
578                                 "substring(", arg0.ToString (), ",", arg1.ToString (), ",", arg2.ToString (), ")"});
579                 }
580         }
581
582
583         internal class XPathFunctionStringLength : XPathFunction
584         {
585                 Expression arg0;
586                 
587                 public XPathFunctionStringLength (FunctionArguments args) : base (args)
588                 {
589                         if (args != null) {
590                                 arg0 = args.Arg;
591                                 if (args.Tail != null)
592                                         throw new XPathException ("string-length takes 1 or zero args");
593                         }
594                 }
595                 
596                 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
597                 
598                 public override object Evaluate (BaseIterator iter)
599                 {
600                         string str;
601                         if (arg0 != null)
602                                 str = arg0.EvaluateString (iter);
603                         else
604                                 str = iter.Current.Value;
605                         return (double) str.Length;
606                 }
607
608                 public override string ToString ()
609                 {
610                         return String.Concat (new string [] {
611                                 "string-length(", arg0.ToString (), ")"});
612                 }
613         }
614
615
616         internal class XPathFunctionNormalizeSpace : XPathFunction
617         {
618                 Expression arg0;
619                 
620                 public XPathFunctionNormalizeSpace (FunctionArguments args) : base (args)
621                 {
622                         if (args != null) {
623                                 arg0 = args.Arg;
624                                 if (args.Tail != null)
625                                         throw new XPathException ("normalize-space takes 1 or zero args");
626                         }
627                 }
628                 
629                 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
630
631                 public override object Evaluate (BaseIterator iter)
632                 {
633                         string str;
634                         if (arg0 != null)
635                                 str = arg0.EvaluateString (iter);
636                         else
637                                 str = iter.Current.Value;
638                         System.Text.StringBuilder sb = new System.Text.StringBuilder ();
639                         bool fSpace = false;
640                         foreach (char ch in str)
641                         {
642                                 if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
643                                 {
644                                         fSpace = true;
645                                 }
646                                 else
647                                 {
648                                         if (fSpace)
649                                         {
650                                                 fSpace = false;
651                                                 if (sb.Length > 0)
652                                                         sb.Append (' ');
653                                         }
654                                         sb.Append (ch);
655                                 }
656                         }
657                         return sb.ToString ();
658                 }
659
660                 public override string ToString ()
661                 {
662                         return String.Concat (new string [] {
663                                 "normalize-space(",
664                                 arg0 != null ? arg0.ToString () : String.Empty,
665                                 ")"});
666                 }
667         }
668
669
670         internal class XPathFunctionTranslate : XPathFunction
671         {
672                 Expression arg0, arg1, arg2;
673                 
674                 public XPathFunctionTranslate (FunctionArguments args) : base (args)
675                 {
676                         if (args == null || args.Tail == null || args.Tail.Tail == null || args.Tail.Tail.Tail != null)
677                                 throw new XPathException ("translate takes 3 args");
678                         
679                         arg0 = args.Arg;
680                         arg1 = args.Tail.Arg;
681                         arg2= args.Tail.Tail.Arg;
682                 }
683                 
684                 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
685                 
686                 public override object Evaluate (BaseIterator iter)
687                 {
688                         string s0 = arg0.EvaluateString (iter);
689                         string s1 = arg1.EvaluateString (iter);
690                         string s2 = arg2.EvaluateString (iter);
691                         
692                         StringBuilder ret = new StringBuilder (s0.Length);
693                                 
694                         int pos = 0, len = s0.Length, s2len = s2.Length;
695                         
696                         while (pos < len) {
697                                 int idx = s1.IndexOf (s0 [pos]);
698                                 
699                                 if (idx != -1) {
700                                         if (idx < s2len)
701                                                 ret.Append (s2 [idx]);
702                                 }
703                                 else
704                                         ret.Append (s0 [pos]);
705                                 
706                                 pos++;
707                         }
708                         
709                         return ret.ToString ();
710                 }
711
712                 public override string ToString ()
713                 {
714                         return String.Concat (new string [] {
715                                 "string-length(",
716                                 arg0.ToString (), ",",
717                                 arg1.ToString (), ",",
718                                 arg2.ToString (), ")"});
719                 }
720         }
721
722
723         internal class XPathFunctionBoolean : XPathFunction
724         {
725                 Expression arg0;
726                 
727                 public XPathFunctionBoolean (FunctionArguments args) : base (args)
728                 {
729                         if (args != null) {
730                                 arg0 = args.Arg;
731                                 if (args.Tail != null)
732                                         throw new XPathException ("boolean takes 1 or zero args");
733                         }
734                 }
735                 
736                 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
737
738                 public override object Evaluate (BaseIterator iter)
739                 {
740                         if (arg0 == null)
741                                 return XPathFunctions.ToBoolean (iter.Current.Value);
742                         return arg0.EvaluateBoolean (iter);
743                 }
744
745                 public override string ToString ()
746                 {
747                         return String.Concat (new string [] {"boolean(", arg0.ToString (), ")"});
748                 }
749         }
750
751
752         internal class XPathFunctionNot : XPathFunction
753         {
754                 Expression arg0;
755                 
756                 public XPathFunctionNot (FunctionArguments args) : base (args)
757                 {
758                         if (args == null || args.Tail != null)
759                                 throw new XPathException ("not takes one arg");
760                         arg0 = args.Arg;
761                 }
762                 
763                 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
764
765                 public override object Evaluate (BaseIterator iter)
766                 {
767                         return !arg0.EvaluateBoolean (iter);
768                 }
769
770                 public override string ToString ()
771                 {
772                         return String.Concat (new string [] {"not(", arg0.ToString (), ")"});
773                 }
774         }
775
776
777         internal class XPathFunctionTrue : XPathFunction
778         {
779                 public XPathFunctionTrue (FunctionArguments args) : base (args)
780                 {
781                         if (args != null)
782                                 throw new XPathException ("true takes 0 args");
783                 }
784                 
785                 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
786
787                 public override object Evaluate (BaseIterator iter)
788                 {
789                         return true;
790                 }
791
792                 public override string ToString ()
793                 {
794                         return "true()";
795                 }
796         }
797
798
799         internal class XPathFunctionFalse : XPathFunction
800         {
801                 public XPathFunctionFalse (FunctionArguments args) : base (args)
802                 {
803                         if (args != null)
804                                 throw new XPathException ("false takes 0 args");
805                 }
806                 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
807
808                 public override object Evaluate (BaseIterator iter)
809                 {
810                         return false;
811                 }
812
813                 public override string ToString ()
814                 {
815                         return "false()";
816                 }
817         }
818
819
820         internal class XPathFunctionLang : XPathFunction
821         {
822                 Expression arg0;
823                 
824                 public XPathFunctionLang (FunctionArguments args) : base (args)
825                 {
826                         if (args == null || args.Tail != null)
827                                 throw new XPathException ("lang takes one arg");
828                         arg0 = args.Arg;
829                 }
830                 
831                 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
832
833                 public override object Evaluate (BaseIterator iter)
834                 {
835                         string lang = arg0.EvaluateString (iter).ToLower ();
836                         string actualLang = iter.Current.XmlLang.ToLower ();
837                         
838                         return lang == actualLang || lang == (actualLang.Split ('-')[0]);
839                 }
840
841                 public override string ToString ()
842                 {
843                         return String.Concat (new string [] {"lang(", arg0.ToString (), ")"});
844                 }
845         }
846
847
848         internal class XPathFunctionNumber : XPathFunction
849         {
850                 Expression arg0;
851                 
852                 public XPathFunctionNumber (FunctionArguments args) : base (args)
853                 {
854                         if (args != null) {
855                                 arg0 = args.Arg;
856                                 if (args.Tail != null)
857                                         throw new XPathException ("number takes 1 or zero args");
858                         }
859                 }
860                 
861                 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
862
863                 public override object Evaluate (BaseIterator iter)
864                 {
865                         if (arg0 == null)
866                                 return XPathFunctions.ToNumber (iter.Current.Value);
867                         return arg0.EvaluateNumber (iter);
868                 }
869
870                 public override string ToString ()
871                 {
872                         return String.Concat (new string [] {"number(", arg0.ToString (), ")"});
873                 }
874         }
875
876
877         internal class XPathFunctionSum : XPathFunction
878         {
879                 Expression arg0;
880                 
881                 public XPathFunctionSum (FunctionArguments args) : base (args)
882                 {
883                         if (args == null || args.Tail != null)
884                                 throw new XPathException ("sum takes one arg");
885                         arg0 = args.Arg;
886                 }
887                 
888                 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
889
890                 public override object Evaluate (BaseIterator iter)
891                 {
892                         XPathNodeIterator itr = arg0.EvaluateNodeSet (iter);
893                         
894                         double sum = 0;
895                         while (itr.MoveNext ())
896                                 sum += XPathFunctions.ToNumber (itr.Current.Value);
897                         
898                         return sum;
899                 }
900
901                 public override string ToString ()
902                 {
903                         return String.Concat (new string [] {"sum(", arg0.ToString (), ")"});
904                 }
905         }
906
907
908         internal class XPathFunctionFloor : XPathFunction
909         {
910                 Expression arg0;
911                 
912                 public XPathFunctionFloor (FunctionArguments args) : base (args)
913                 {
914                         if (args == null || args.Tail != null)
915                                 throw new XPathException ("floor takes one arg");
916                         arg0 = args.Arg;
917                 }
918                 
919                 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
920
921                 public override object Evaluate (BaseIterator iter)
922                 {
923                         return Math.Floor (arg0.EvaluateNumber (iter));
924                 }
925
926                 public override string ToString ()
927                 {
928                         return String.Concat (new string [] {"floor(", arg0.ToString (), ")"});
929                 }
930         }
931
932
933         internal class XPathFunctionCeil : XPathFunction
934         {
935                 Expression arg0;
936                 
937                 public XPathFunctionCeil (FunctionArguments args) : base (args)
938                 {
939                         if (args == null || args.Tail != null)
940                                 throw new XPathException ("ceil takes one arg");
941                         arg0 = args.Arg;
942                 }
943                 
944                 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
945
946                 public override object Evaluate (BaseIterator iter)
947                 {
948                         return Math.Ceiling (arg0.EvaluateNumber (iter));
949                 }
950
951                 public override string ToString ()
952                 {
953                         return String.Concat (new string [] {"ceil(", arg0.ToString (), ")"});
954                 }
955         }
956
957
958         internal class XPathFunctionRound : XPathFunction
959         {
960                 Expression arg0;
961                 
962                 public XPathFunctionRound (FunctionArguments args) : base (args)
963                 {
964                         if (args == null || args.Tail != null)
965                                 throw new XPathException ("round takes one arg");
966                         arg0 = args.Arg;
967                 }
968                 
969                 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
970
971                 public override object Evaluate (BaseIterator iter)
972                 {
973                         double arg = arg0.EvaluateNumber (iter);
974                         if (arg < -0.5 || arg > 0)
975                                 return Math.Floor (arg + 0.5);
976                         return Math.Round (arg);
977                 }
978
979                 public override string ToString ()
980                 {
981                         return String.Concat (new string [] {"round(", arg0.ToString (), ")"});
982                 }
983         }
984 }