2 // System.Xml.XPath.DefaultContext & support classes
5 // Piers Haken (piersh@friskit.com)
7 // (C) 2002 Piers Haken
10 using System.Collections;
11 using System.Globalization;
\r
13 using System.Xml.XPath;
17 namespace System.Xml.XPath
19 internal class XPathFunctions
21 public static bool ToBoolean (object arg)
24 throw new ArgumentNullException ();
29 double dArg = (double) arg;
30 return (dArg != 0.0 && !double.IsNaN (dArg));
33 return ((string) arg).Length != 0;
34 if (arg is BaseIterator)
36 BaseIterator iter = (BaseIterator) arg;
37 return iter.MoveNext ();
39 if (arg is XPathNavigator)
41 return ToBoolean (((XPathNavigator) arg).SelectChildren (XPathNodeType.All));
43 throw new ArgumentException ();
46 public static bool ToBoolean (bool b)
51 public static bool ToBoolean (double d)
53 return d != 0.0 && d != double.NaN;
56 public static bool ToBoolean (string s)
58 return s != null && s.Length > 0;
61 public static bool ToBoolean (BaseIterator iter)
63 return iter != null && iter.MoveNext ();
66 public static string ToString (object arg)
69 throw new ArgumentNullException ();
73 return ((bool) arg) ? "true" : "false";
75 return ((double) arg).ToString ("R", System.Globalization.NumberFormatInfo.InvariantInfo);
76 if (arg is BaseIterator)
78 BaseIterator iter = (BaseIterator) arg;
79 if (!iter.MoveNext ())
81 return iter.Current.Value;
83 if (arg is XPathNavigator)
85 return ((XPathNavigator) arg).Value;
87 throw new ArgumentException ();
90 public static double ToNumber (object arg)
93 throw new ArgumentNullException ();
94 if (arg is BaseIterator || arg is XPathNavigator)
95 arg = ToString (arg); // follow on
97 string s = arg as string;
98 return ToNumber (s); // use explicit overload
103 return Convert.ToDouble ((bool) arg);
104 throw new ArgumentException ();
107 public static double ToNumber (string arg)
110 throw new ArgumentNullException ();
111 string s = arg.Trim (XmlChar.WhitespaceChars);
115 return XmlConvert.ToDouble (s);
116 } catch (System.OverflowException) {
118 } catch (System.FormatException) {
124 internal abstract class XPathFunction : Expression
126 public XPathFunction (FunctionArguments args) {}
130 internal class XPathFunctionLast : XPathFunction
132 public XPathFunctionLast (FunctionArguments args) : base (args)
135 throw new XPathException ("last takes 0 args");
138 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
140 public override object Evaluate (BaseIterator iter)
142 return (double) iter.Count;
145 public override string ToString ()
150 internal override bool IsPositional {
156 internal class XPathFunctionPosition : XPathFunction
158 public XPathFunctionPosition (FunctionArguments args) : base (args)
161 throw new XPathException ("position takes 0 args");
164 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
166 public override object Evaluate (BaseIterator iter)
168 return (double) iter.CurrentPosition;
171 public override string ToString ()
176 internal override bool IsPositional {
182 internal class XPathFunctionCount : XPathFunction
186 public XPathFunctionCount (FunctionArguments args) : base (args)
188 if (args == null || args.Tail != null)
189 throw new XPathException ("count takes 1 arg");
194 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
196 public override object Evaluate (BaseIterator iter)
198 return (double) arg0.EvaluateNodeSet (iter).Count;
201 public override bool EvaluateBoolean (BaseIterator iter)
203 if (arg0.GetReturnType (iter) == XPathResultType.NodeSet)
204 return arg0.EvaluateBoolean (iter);
206 return arg0.EvaluateNodeSet (iter).MoveNext ();
209 public override string ToString ()
211 return "count(" + arg0.ToString () + ")";
216 internal class XPathFunctionId : XPathFunction
220 public XPathFunctionId (FunctionArguments args) : base (args)
222 if (args == null || args.Tail != null)
223 throw new XPathException ("id takes 1 arg");
228 public Expression Id { get { return arg0; } }
230 private static char [] rgchWhitespace = {' ', '\t', '\r', '\n'};
231 public override XPathResultType ReturnType { get { return XPathResultType.NodeSet; }}
233 public override object Evaluate (BaseIterator iter)
236 object val = arg0.Evaluate (iter);
238 BaseIterator valItr = val as BaseIterator;
242 while (valItr.MoveNext ())
243 strArgs += valItr.Current.Value + " ";
246 strArgs = XPathFunctions.ToString (val);
248 XPathNavigator n = iter.Current.Clone ();
249 ArrayList rgNodes = new ArrayList ();
250 string [] ids = strArgs.Split (rgchWhitespace);
251 for (int i = 0; i < ids.Length; i++)
252 if (n.MoveToId (ids [i]))
253 rgNodes.Add (n.Clone ());
255 rgNodes.Sort (XPathNavigatorComparer.Instance);
256 return new ListIterator (iter, rgNodes, true);
259 public override string ToString ()
261 return "id(" + arg0.ToString () + ")";
265 internal class XPathFunctionLocalName : XPathFunction
269 public XPathFunctionLocalName (FunctionArguments args) : base (args)
273 if (args.Tail != null)
274 throw new XPathException ("local-name takes 1 or zero args");
278 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
280 public override object Evaluate (BaseIterator iter)
283 return iter.Current.LocalName;
285 BaseIterator argNs = arg0.EvaluateNodeSet (iter);
286 if (argNs == null || !argNs.MoveNext ())
288 return argNs.Current.LocalName;
291 public override string ToString ()
293 return "local-name(" + arg0.ToString () + ")";
298 internal class XPathFunctionNamespaceUri : XPathFunction
302 public XPathFunctionNamespaceUri (FunctionArguments args) : base (args)
306 if (args.Tail != null)
307 throw new XPathException ("namespace-uri takes 1 or zero args");
311 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
313 public override object Evaluate (BaseIterator iter)
316 return iter.Current.NamespaceURI;
318 BaseIterator argNs = arg0.EvaluateNodeSet (iter);
319 if (argNs == null || !argNs.MoveNext ())
321 return argNs.Current.NamespaceURI;
324 public override string ToString ()
326 return "namespace-uri(" + arg0.ToString () + ")";
331 internal class XPathFunctionName : XPathFunction
335 public XPathFunctionName (FunctionArguments args) : base (args)
339 if (args.Tail != null)
340 throw new XPathException ("name takes 1 or zero args");
344 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
346 public override object Evaluate (BaseIterator iter)
349 return iter.Current.Name;
351 BaseIterator argNs = arg0.EvaluateNodeSet (iter);
352 if (argNs == null || !argNs.MoveNext ())
354 return argNs.Current.Name;
357 public override string ToString ()
359 return "name(" + arg0.ToString () + ")";
364 internal class XPathFunctionString : XPathFunction
368 public XPathFunctionString (FunctionArguments args) : base (args)
372 if (args.Tail != null)
373 throw new XPathException ("boolean takes 1 or zero args");
377 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
379 public override object Evaluate (BaseIterator iter)
382 return iter.Current.Value;
383 return arg0.EvaluateString (iter);
386 public override string ToString ()
388 return "string(" + arg0.ToString () + ")";
393 internal class XPathFunctionConcat : XPathFunction
397 public XPathFunctionConcat (FunctionArguments args) : base (args)
399 if (args == null || args.Tail == null)
400 throw new XPathException ("concat takes 2 or more args");
402 args.ToArrayList (rgs = new ArrayList ());
405 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
407 public override object Evaluate (BaseIterator iter)
409 StringBuilder sb = new StringBuilder ();
412 for (int i = 0; i < len; i++)
413 sb.Append (((Expression)rgs[i]).EvaluateString (iter));
415 return sb.ToString ();
418 public override string ToString ()
420 StringBuilder sb = new StringBuilder ();
421 sb.Append ("concat(");
422 for (int i = 0; i < rgs.Count - 1; i++) {
423 sb.AppendFormat (CultureInfo.InvariantCulture, "{0}", rgs [i].ToString ());
426 sb.AppendFormat (CultureInfo.InvariantCulture, "{0}", rgs [rgs.Count - 1].ToString ());
428 return sb.ToString ();
433 internal class XPathFunctionStartsWith : XPathFunction
435 Expression arg0, arg1;
437 public XPathFunctionStartsWith (FunctionArguments args) : base (args)
439 if (args == null || args.Tail == null || args.Tail.Tail != null)
440 throw new XPathException ("starts-with 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).StartsWith (arg1.EvaluateString (iter));
453 public override string ToString ()
455 return String.Concat ("starts-with(", arg0.ToString (), ",", arg1.ToString (), ")");
460 internal class XPathFunctionContains : XPathFunction
462 Expression arg0, arg1;
464 public XPathFunctionContains (FunctionArguments args) : base (args)
466 if (args == null || args.Tail == null || args.Tail.Tail != null)
467 throw new XPathException ("contains takes 2 args");
470 arg1 = args.Tail.Arg;
473 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
475 public override object Evaluate (BaseIterator iter)
477 return arg0.EvaluateString (iter).IndexOf (arg1.EvaluateString (iter)) != -1;
480 public override string ToString ()
482 return String.Concat ("contains(", arg0.ToString (), ",", arg1.ToString (), ")");
487 internal class XPathFunctionSubstringBefore : XPathFunction
489 Expression arg0, arg1;
491 public XPathFunctionSubstringBefore (FunctionArguments args) : base (args)
493 if (args == null || args.Tail == null || args.Tail.Tail != null)
494 throw new XPathException ("substring-before takes 2 args");
497 arg1 = args.Tail.Arg;
500 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
502 public override object Evaluate (BaseIterator iter)
504 string str1 = arg0.EvaluateString (iter);
505 string str2 = arg1.EvaluateString (iter);
506 int ich = str1.IndexOf (str2);
509 return str1.Substring (0, ich);
512 public override string ToString ()
514 return String.Concat ("substring-before(", arg0.ToString (), ",", arg1.ToString (), ")");
519 internal class XPathFunctionSubstringAfter : XPathFunction
521 Expression arg0, arg1;
523 public XPathFunctionSubstringAfter (FunctionArguments args) : base (args)
525 if (args == null || args.Tail == null || args.Tail.Tail != null)
526 throw new XPathException ("substring-after takes 2 args");
529 arg1 = args.Tail.Arg;
532 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
534 public override object Evaluate (BaseIterator iter)
536 string str1 = arg0.EvaluateString (iter);
537 string str2 = arg1.EvaluateString (iter);
538 int ich = str1.IndexOf (str2);
541 return str1.Substring (ich + str2.Length);
544 public override string ToString ()
546 return String.Concat ("substring-after(", arg0.ToString (), ",", arg1.ToString (), ")");
551 internal class XPathFunctionSubstring : XPathFunction
553 Expression arg0, arg1, arg2;
555 public XPathFunctionSubstring (FunctionArguments args) : base (args)
557 if (args == null || args.Tail == null || (args.Tail.Tail != null && args.Tail.Tail.Tail != null))
558 throw new XPathException ("substring takes 2 or 3 args");
561 arg1 = args.Tail.Arg;
562 if (args.Tail.Tail != null)
563 arg2= args.Tail.Tail.Arg;
566 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
568 public override object Evaluate (BaseIterator iter)
570 string str = arg0.EvaluateString (iter);
571 double ich = Math.Round (arg1.EvaluateNumber (iter)) - 1;
572 if (Double.IsNaN (ich) ||
573 Double.IsNegativeInfinity (ich) ||
574 ich >= (double) str.Length)
581 return str.Substring ((int) ich);
585 double cch = Math.Round (arg2.EvaluateNumber (iter));
586 if (Double.IsNaN (cch))
588 if (ich < 0.0 || cch < 0.0)
595 double cchMax = (double) str.Length - ich;
598 return str.Substring ((int) ich, (int) cch);
602 public override string ToString ()
604 return String.Concat (new string [] {
605 "substring(", arg0.ToString (), ",", arg1.ToString (), ",", arg2.ToString (), ")"});
610 internal class XPathFunctionStringLength : XPathFunction
614 public XPathFunctionStringLength (FunctionArguments args) : base (args)
618 if (args.Tail != null)
619 throw new XPathException ("string-length takes 1 or zero args");
623 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
625 public override object Evaluate (BaseIterator iter)
629 str = arg0.EvaluateString (iter);
631 str = iter.Current.Value;
632 return (double) str.Length;
635 public override string ToString ()
637 return String.Concat (new string [] {
638 "string-length(", arg0.ToString (), ")"});
643 internal class XPathFunctionNormalizeSpace : XPathFunction
647 public XPathFunctionNormalizeSpace (FunctionArguments args) : base (args)
651 if (args.Tail != null)
652 throw new XPathException ("normalize-space takes 1 or zero args");
656 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
658 public override object Evaluate (BaseIterator iter)
662 str = arg0.EvaluateString (iter);
664 str = iter.Current.Value;
665 System.Text.StringBuilder sb = new System.Text.StringBuilder ();
667 for (int i = 0; i < str.Length; i++) {
669 if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
684 return sb.ToString ();
687 public override string ToString ()
689 return String.Concat (new string [] {
691 arg0 != null ? arg0.ToString () : String.Empty,
697 internal class XPathFunctionTranslate : XPathFunction
699 Expression arg0, arg1, arg2;
701 public XPathFunctionTranslate (FunctionArguments args) : base (args)
703 if (args == null || args.Tail == null || args.Tail.Tail == null || args.Tail.Tail.Tail != null)
704 throw new XPathException ("translate takes 3 args");
707 arg1 = args.Tail.Arg;
708 arg2= args.Tail.Tail.Arg;
711 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
713 public override object Evaluate (BaseIterator iter)
715 string s0 = arg0.EvaluateString (iter);
716 string s1 = arg1.EvaluateString (iter);
717 string s2 = arg2.EvaluateString (iter);
719 StringBuilder ret = new StringBuilder (s0.Length);
721 int pos = 0, len = s0.Length, s2len = s2.Length;
724 int idx = s1.IndexOf (s0 [pos]);
728 ret.Append (s2 [idx]);
731 ret.Append (s0 [pos]);
736 return ret.ToString ();
739 public override string ToString ()
741 return String.Concat (new string [] {
743 arg0.ToString (), ",",
744 arg1.ToString (), ",",
745 arg2.ToString (), ")"});
750 internal class XPathFunctionBoolean : XPathFunction
754 public XPathFunctionBoolean (FunctionArguments args) : base (args)
758 if (args.Tail != null)
759 throw new XPathException ("boolean takes 1 or zero args");
763 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
765 public override object Evaluate (BaseIterator iter)
768 return XPathFunctions.ToBoolean (iter.Current.Value);
769 return arg0.EvaluateBoolean (iter);
772 public override string ToString ()
774 return String.Concat (new string [] {"boolean(", arg0.ToString (), ")"});
779 internal class XPathFunctionNot : XPathFunction
783 public XPathFunctionNot (FunctionArguments args) : base (args)
785 if (args == null || args.Tail != null)
786 throw new XPathException ("not takes one arg");
790 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
792 public override object Evaluate (BaseIterator iter)
794 return !arg0.EvaluateBoolean (iter);
797 public override string ToString ()
799 return String.Concat (new string [] {"not(", arg0.ToString (), ")"});
804 internal class XPathFunctionTrue : XPathFunction
806 public XPathFunctionTrue (FunctionArguments args) : base (args)
809 throw new XPathException ("true takes 0 args");
812 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
814 public override object Evaluate (BaseIterator iter)
819 public override string ToString ()
826 internal class XPathFunctionFalse : XPathFunction
828 public XPathFunctionFalse (FunctionArguments args) : base (args)
831 throw new XPathException ("false takes 0 args");
833 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
835 public override object Evaluate (BaseIterator iter)
840 public override string ToString ()
847 internal class XPathFunctionLang : XPathFunction
851 public XPathFunctionLang (FunctionArguments args) : base (args)
853 if (args == null || args.Tail != null)
854 throw new XPathException ("lang takes one arg");
858 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
860 public override object Evaluate (BaseIterator iter)
862 string lang = arg0.EvaluateString (iter).ToLower (CultureInfo.InvariantCulture);
863 string actualLang = iter.Current.XmlLang.ToLower (CultureInfo.InvariantCulture);
865 return lang == actualLang || lang == (actualLang.Split ('-')[0]);
868 public override string ToString ()
870 return String.Concat (new string [] {"lang(", arg0.ToString (), ")"});
875 internal class XPathFunctionNumber : XPathFunction
879 public XPathFunctionNumber (FunctionArguments args) : base (args)
883 if (args.Tail != null)
884 throw new XPathException ("number takes 1 or zero args");
888 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
890 public override object Evaluate (BaseIterator iter)
893 return XPathFunctions.ToNumber (iter.Current.Value);
894 return arg0.EvaluateNumber (iter);
897 public override string ToString ()
899 return String.Concat (new string [] {"number(", arg0.ToString (), ")"});
904 internal class XPathFunctionSum : XPathFunction
908 public XPathFunctionSum (FunctionArguments args) : base (args)
910 if (args == null || args.Tail != null)
911 throw new XPathException ("sum takes one arg");
915 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
917 public override object Evaluate (BaseIterator iter)
919 XPathNodeIterator itr = arg0.EvaluateNodeSet (iter);
922 while (itr.MoveNext ())
923 sum += XPathFunctions.ToNumber (itr.Current.Value);
928 public override string ToString ()
930 return String.Concat (new string [] {"sum(", arg0.ToString (), ")"});
935 internal class XPathFunctionFloor : XPathFunction
939 public XPathFunctionFloor (FunctionArguments args) : base (args)
941 if (args == null || args.Tail != null)
942 throw new XPathException ("floor takes one arg");
946 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
948 public override object Evaluate (BaseIterator iter)
950 return Math.Floor (arg0.EvaluateNumber (iter));
953 public override string ToString ()
955 return String.Concat (new string [] {"floor(", arg0.ToString (), ")"});
960 internal class XPathFunctionCeil : XPathFunction
964 public XPathFunctionCeil (FunctionArguments args) : base (args)
966 if (args == null || args.Tail != null)
967 throw new XPathException ("ceil takes one arg");
971 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
973 public override object Evaluate (BaseIterator iter)
975 return Math.Ceiling (arg0.EvaluateNumber (iter));
978 public override string ToString ()
980 return String.Concat (new string [] {"ceil(", arg0.ToString (), ")"});
985 internal class XPathFunctionRound : XPathFunction
989 public XPathFunctionRound (FunctionArguments args) : base (args)
991 if (args == null || args.Tail != null)
992 throw new XPathException ("round takes one arg");
996 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
998 public override object Evaluate (BaseIterator iter)
1000 double arg = arg0.EvaluateNumber (iter);
1001 if (arg < -0.5 || arg > 0)
1002 return Math.Floor (arg + 0.5);
1003 return Math.Round (arg);
1006 public override string ToString ()
1008 return String.Concat (new string [] {"round(", arg0.ToString (), ")"});