2 // System.Xml.XPath.DefaultContext & support classes
5 // Piers Haken (piersh@friskit.com)
7 // (C) 2002 Piers Haken
10 using System.Collections;
12 using System.Xml.XPath;
16 namespace System.Xml.XPath
18 internal class XPathFunctions
20 public static bool ToBoolean (object arg)
23 throw new ArgumentNullException ();
28 double dArg = (double) arg;
29 return (dArg != 0.0 && !double.IsNaN (dArg));
32 return ((string) arg).Length != 0;
33 if (arg is BaseIterator)
35 BaseIterator iter = (BaseIterator) arg;
36 return iter.MoveNext ();
38 throw new ArgumentException ();
41 public static string ToString (object arg)
44 throw new ArgumentNullException ();
48 return ((bool) arg) ? "true" : "false";
50 return ((double) arg).ToString ("R", System.Globalization.NumberFormatInfo.InvariantInfo);
51 if (arg is BaseIterator)
53 BaseIterator iter = (BaseIterator) arg;
54 if (!iter.MoveNext ())
56 return iter.Current.Value;
58 throw new ArgumentException ();
61 public static double ToNumber (object arg)
64 throw new ArgumentNullException ();
65 if (arg is BaseIterator)
66 arg = ToString (arg); // follow on
69 string s = arg as string;
74 return XmlConvert.ToDouble (s); // TODO: spec? convert string to number
76 catch (System.FormatException)
84 return Convert.ToDouble ((bool) arg);
85 throw new ArgumentException ();
88 public static double ToNumber (string arg)
91 throw new ArgumentNullException ();
95 return XmlConvert.ToDouble ((string) arg.Trim (XmlChar.WhitespaceChars));
96 } catch (System.OverflowException) {
98 } catch (System.FormatException) {
104 internal abstract class XPathFunction : Expression
106 public XPathFunction (FunctionArguments args) {}
110 internal class XPathFunctionLast : XPathFunction
112 public XPathFunctionLast (FunctionArguments args) : base (args)
115 throw new XPathException ("last takes 0 args");
118 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
120 public override object Evaluate (BaseIterator iter)
122 return (double) iter.Count;
125 public override string ToString ()
132 internal class XPathFunctionPosition : XPathFunction
134 public XPathFunctionPosition (FunctionArguments args) : base (args)
137 throw new XPathException ("position takes 0 args");
140 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
142 public override object Evaluate (BaseIterator iter)
144 return (double) iter.CurrentPosition;
147 public override string ToString ()
154 internal class XPathFunctionCount : XPathFunction
158 public XPathFunctionCount (FunctionArguments args) : base (args)
160 if (args == null || args.Tail != null)
161 throw new XPathException ("count takes 1 arg");
166 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
168 public override object Evaluate (BaseIterator iter)
170 return (double) arg0.EvaluateNodeSet (iter).Count;
173 public override bool EvaluateBoolean (BaseIterator iter)
175 if (arg0.GetReturnType (iter) == XPathResultType.NodeSet)
176 return arg0.EvaluateBoolean (iter);
178 return arg0.EvaluateNodeSet (iter).MoveNext ();
181 public override string ToString ()
183 return "count(" + arg0.ToString () + ")";
188 internal class XPathFunctionId : XPathFunction
192 public XPathFunctionId (FunctionArguments args) : base (args)
194 if (args == null || args.Tail != null)
195 throw new XPathException ("id takes 1 arg");
200 public Expression Id { get { return arg0; } }
202 private static char [] rgchWhitespace = {' ', '\t', '\r', '\n'};
203 public override XPathResultType ReturnType { get { return XPathResultType.NodeSet; }}
205 public override object Evaluate (BaseIterator iter)
208 object val = arg0.Evaluate (iter);
210 BaseIterator valItr = val as BaseIterator;
214 while (valItr.MoveNext ())
215 strArgs += valItr.Current.Value + " ";
218 strArgs = XPathFunctions.ToString (val);
220 XPathNavigator n = iter.Current.Clone ();
221 ArrayList rgNodes = new ArrayList ();
222 foreach (string strArg in strArgs.Split (rgchWhitespace))
224 if (n.MoveToId (strArg)) {
225 rgNodes.Add (n.Clone ());
228 rgNodes.Sort (XPathNavigatorComparer.Instance);
229 return new EnumeratorIterator (iter, rgNodes.GetEnumerator ());
232 public override string ToString ()
234 return "id(" + arg0.ToString () + ")";
238 internal class XPathFunctionLocalName : XPathFunction
242 public XPathFunctionLocalName (FunctionArguments args) : base (args)
246 if (args.Tail != null)
247 throw new XPathException ("local-name takes 1 or zero args");
251 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
253 public override object Evaluate (BaseIterator iter)
256 return iter.Current.LocalName;
258 BaseIterator argNs = arg0.EvaluateNodeSet (iter);
259 if (argNs == null || !argNs.MoveNext ())
261 return argNs.Current.LocalName;
264 public override string ToString ()
266 return "local-name(" + arg0.ToString () + ")";
271 internal class XPathFunctionNamespaceUri : XPathFunction
275 public XPathFunctionNamespaceUri (FunctionArguments args) : base (args)
279 if (args.Tail != null)
280 throw new XPathException ("namespace-uri takes 1 or zero args");
284 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
286 public override object Evaluate (BaseIterator iter)
289 return iter.Current.NamespaceURI;
291 BaseIterator argNs = arg0.EvaluateNodeSet (iter);
292 if (argNs == null || !argNs.MoveNext ())
294 return argNs.Current.NamespaceURI;
297 public override string ToString ()
299 return "namespace-uri(" + arg0.ToString () + ")";
304 internal class XPathFunctionName : XPathFunction
308 public XPathFunctionName (FunctionArguments args) : base (args)
312 if (args.Tail != null)
313 throw new XPathException ("name takes 1 or zero args");
317 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
319 public override object Evaluate (BaseIterator iter)
322 return iter.Current.Name;
324 BaseIterator argNs = arg0.EvaluateNodeSet (iter);
325 if (argNs == null || !argNs.MoveNext ())
327 return argNs.Current.Name;
330 public override string ToString ()
332 return "name(" + arg0.ToString () + ")";
337 internal class XPathFunctionString : XPathFunction
341 public XPathFunctionString (FunctionArguments args) : base (args)
345 if (args.Tail != null)
346 throw new XPathException ("boolean takes 1 or zero args");
350 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
352 public override object Evaluate (BaseIterator iter)
355 return iter.Current.Value;
356 return arg0.EvaluateString (iter);
359 public override string ToString ()
361 return "string(" + arg0.ToString () + ")";
366 internal class XPathFunctionConcat : XPathFunction
370 public XPathFunctionConcat (FunctionArguments args) : base (args)
372 if (args == null || args.Tail == null)
373 throw new XPathException ("concat takes 2 or more args");
375 args.ToArrayList (rgs = new ArrayList ());
378 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
380 public override object Evaluate (BaseIterator iter)
382 StringBuilder sb = new StringBuilder ();
385 for (int i = 0; i < len; i++)
386 sb.Append (((Expression)rgs[i]).EvaluateString (iter));
388 return sb.ToString ();
391 public override string ToString ()
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 ());
399 sb.Append (rgs [rgs.Count - 1].ToString ());
401 return sb.ToString ();
406 internal class XPathFunctionStartsWith : XPathFunction
408 Expression arg0, arg1;
410 public XPathFunctionStartsWith (FunctionArguments args) : base (args)
412 if (args == null || args.Tail == null || args.Tail.Tail != null)
413 throw new XPathException ("starts-with takes 2 args");
416 arg1 = args.Tail.Arg;
419 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
421 public override object Evaluate (BaseIterator iter)
423 return arg0.EvaluateString (iter).StartsWith (arg1.EvaluateString (iter));
426 public override string ToString ()
428 return String.Concat ("starts-with(", arg0.ToString (), ",", arg1.ToString (), ")");
433 internal class XPathFunctionContains : XPathFunction
435 Expression arg0, arg1;
437 public XPathFunctionContains (FunctionArguments args) : base (args)
439 if (args == null || args.Tail == null || args.Tail.Tail != null)
440 throw new XPathException ("contains takes 2 args");
443 arg1 = args.Tail.Arg;
446 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
448 public override object Evaluate (BaseIterator iter)
450 return arg0.EvaluateString (iter).IndexOf (arg1.EvaluateString (iter)) != -1;
453 public override string ToString ()
455 return String.Concat ("contains(", arg0.ToString (), ",", arg1.ToString (), ")");
460 internal class XPathFunctionSubstringBefore : XPathFunction
462 Expression arg0, arg1;
464 public XPathFunctionSubstringBefore (FunctionArguments args) : base (args)
466 if (args == null || args.Tail == null || args.Tail.Tail != null)
467 throw new XPathException ("substring-before takes 2 args");
470 arg1 = args.Tail.Arg;
473 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
475 public override object Evaluate (BaseIterator iter)
477 string str1 = arg0.EvaluateString (iter);
478 string str2 = arg1.EvaluateString (iter);
479 int ich = str1.IndexOf (str2);
482 return str1.Substring (0, ich);
485 public override string ToString ()
487 return String.Concat ("substring-before(", arg0.ToString (), ",", arg1.ToString (), ")");
492 internal class XPathFunctionSubstringAfter : XPathFunction
494 Expression arg0, arg1;
496 public XPathFunctionSubstringAfter (FunctionArguments args) : base (args)
498 if (args == null || args.Tail == null || args.Tail.Tail != null)
499 throw new XPathException ("substring-after takes 2 args");
502 arg1 = args.Tail.Arg;
505 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
507 public override object Evaluate (BaseIterator iter)
509 string str1 = arg0.EvaluateString (iter);
510 string str2 = arg1.EvaluateString (iter);
511 int ich = str1.IndexOf (str2);
514 return str1.Substring (ich + str2.Length);
517 public override string ToString ()
519 return String.Concat ("substring-after(", arg0.ToString (), ",", arg1.ToString (), ")");
524 internal class XPathFunctionSubstring : XPathFunction
526 Expression arg0, arg1, arg2;
528 public XPathFunctionSubstring (FunctionArguments args) : base (args)
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");
534 arg1 = args.Tail.Arg;
535 if (args.Tail.Tail != null)
536 arg2= args.Tail.Tail.Arg;
539 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
541 public override object Evaluate (BaseIterator iter)
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)
554 return str.Substring ((int) ich);
558 double cch = Math.Round (arg2.EvaluateNumber (iter));
559 if (Double.IsNaN (cch))
561 if (ich < 0.0 || cch < 0.0)
568 double cchMax = (double) str.Length - ich;
571 return str.Substring ((int) ich, (int) cch);
575 public override string ToString ()
577 return String.Concat (new string [] {
578 "substring(", arg0.ToString (), ",", arg1.ToString (), ",", arg2.ToString (), ")"});
583 internal class XPathFunctionStringLength : XPathFunction
587 public XPathFunctionStringLength (FunctionArguments args) : base (args)
591 if (args.Tail != null)
592 throw new XPathException ("string-length takes 1 or zero args");
596 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
598 public override object Evaluate (BaseIterator iter)
602 str = arg0.EvaluateString (iter);
604 str = iter.Current.Value;
605 return (double) str.Length;
608 public override string ToString ()
610 return String.Concat (new string [] {
611 "string-length(", arg0.ToString (), ")"});
616 internal class XPathFunctionNormalizeSpace : XPathFunction
620 public XPathFunctionNormalizeSpace (FunctionArguments args) : base (args)
624 if (args.Tail != null)
625 throw new XPathException ("normalize-space takes 1 or zero args");
629 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
631 public override object Evaluate (BaseIterator iter)
635 str = arg0.EvaluateString (iter);
637 str = iter.Current.Value;
638 System.Text.StringBuilder sb = new System.Text.StringBuilder ();
640 foreach (char ch in str)
642 if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
657 return sb.ToString ();
660 public override string ToString ()
662 return String.Concat (new string [] {
664 arg0 != null ? arg0.ToString () : String.Empty,
670 internal class XPathFunctionTranslate : XPathFunction
672 Expression arg0, arg1, arg2;
674 public XPathFunctionTranslate (FunctionArguments args) : base (args)
676 if (args == null || args.Tail == null || args.Tail.Tail == null || args.Tail.Tail.Tail != null)
677 throw new XPathException ("translate takes 3 args");
680 arg1 = args.Tail.Arg;
681 arg2= args.Tail.Tail.Arg;
684 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
686 public override object Evaluate (BaseIterator iter)
688 string s0 = arg0.EvaluateString (iter);
689 string s1 = arg1.EvaluateString (iter);
690 string s2 = arg2.EvaluateString (iter);
692 StringBuilder ret = new StringBuilder (s0.Length);
694 int pos = 0, len = s0.Length, s2len = s2.Length;
697 int idx = s1.IndexOf (s0 [pos]);
701 ret.Append (s2 [idx]);
704 ret.Append (s0 [pos]);
709 return ret.ToString ();
712 public override string ToString ()
714 return String.Concat (new string [] {
716 arg0.ToString (), ",",
717 arg1.ToString (), ",",
718 arg2.ToString (), ")"});
723 internal class XPathFunctionBoolean : XPathFunction
727 public XPathFunctionBoolean (FunctionArguments args) : base (args)
731 if (args.Tail != null)
732 throw new XPathException ("boolean takes 1 or zero args");
736 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
738 public override object Evaluate (BaseIterator iter)
741 return XPathFunctions.ToBoolean (iter.Current.Value);
742 return arg0.EvaluateBoolean (iter);
745 public override string ToString ()
747 return String.Concat (new string [] {"boolean(", arg0.ToString (), ")"});
752 internal class XPathFunctionNot : XPathFunction
756 public XPathFunctionNot (FunctionArguments args) : base (args)
758 if (args == null || args.Tail != null)
759 throw new XPathException ("not takes one arg");
763 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
765 public override object Evaluate (BaseIterator iter)
767 return !arg0.EvaluateBoolean (iter);
770 public override string ToString ()
772 return String.Concat (new string [] {"not(", arg0.ToString (), ")"});
777 internal class XPathFunctionTrue : XPathFunction
779 public XPathFunctionTrue (FunctionArguments args) : base (args)
782 throw new XPathException ("true takes 0 args");
785 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
787 public override object Evaluate (BaseIterator iter)
792 public override string ToString ()
799 internal class XPathFunctionFalse : XPathFunction
801 public XPathFunctionFalse (FunctionArguments args) : base (args)
804 throw new XPathException ("false takes 0 args");
806 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
808 public override object Evaluate (BaseIterator iter)
813 public override string ToString ()
820 internal class XPathFunctionLang : XPathFunction
824 public XPathFunctionLang (FunctionArguments args) : base (args)
826 if (args == null || args.Tail != null)
827 throw new XPathException ("lang takes one arg");
831 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
833 public override object Evaluate (BaseIterator iter)
835 string lang = arg0.EvaluateString (iter).ToLower ();
836 string actualLang = iter.Current.XmlLang.ToLower ();
838 return lang == actualLang || lang == (actualLang.Split ('-')[0]);
841 public override string ToString ()
843 return String.Concat (new string [] {"lang(", arg0.ToString (), ")"});
848 internal class XPathFunctionNumber : XPathFunction
852 public XPathFunctionNumber (FunctionArguments args) : base (args)
856 if (args.Tail != null)
857 throw new XPathException ("number takes 1 or zero args");
861 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
863 public override object Evaluate (BaseIterator iter)
866 return XPathFunctions.ToNumber (iter.Current.Value);
867 return arg0.EvaluateNumber (iter);
870 public override string ToString ()
872 return String.Concat (new string [] {"number(", arg0.ToString (), ")"});
877 internal class XPathFunctionSum : XPathFunction
881 public XPathFunctionSum (FunctionArguments args) : base (args)
883 if (args == null || args.Tail != null)
884 throw new XPathException ("sum takes one arg");
888 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
890 public override object Evaluate (BaseIterator iter)
892 XPathNodeIterator itr = arg0.EvaluateNodeSet (iter);
895 while (itr.MoveNext ())
896 sum += XPathFunctions.ToNumber (itr.Current.Value);
901 public override string ToString ()
903 return String.Concat (new string [] {"sum(", arg0.ToString (), ")"});
908 internal class XPathFunctionFloor : XPathFunction
912 public XPathFunctionFloor (FunctionArguments args) : base (args)
914 if (args == null || args.Tail != null)
915 throw new XPathException ("floor takes one arg");
919 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
921 public override object Evaluate (BaseIterator iter)
923 return Math.Floor (arg0.EvaluateNumber (iter));
926 public override string ToString ()
928 return String.Concat (new string [] {"floor(", arg0.ToString (), ")"});
933 internal class XPathFunctionCeil : XPathFunction
937 public XPathFunctionCeil (FunctionArguments args) : base (args)
939 if (args == null || args.Tail != null)
940 throw new XPathException ("ceil takes one arg");
944 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
946 public override object Evaluate (BaseIterator iter)
948 return Math.Ceiling (arg0.EvaluateNumber (iter));
951 public override string ToString ()
953 return String.Concat (new string [] {"ceil(", arg0.ToString (), ")"});
958 internal class XPathFunctionRound : XPathFunction
962 public XPathFunctionRound (FunctionArguments args) : base (args)
964 if (args == null || args.Tail != null)
965 throw new XPathException ("round takes one arg");
969 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
971 public override object Evaluate (BaseIterator iter)
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);
979 public override string ToString ()
981 return String.Concat (new string [] {"round(", arg0.ToString (), ")"});