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 bool ToBoolean (bool b)
46 public static bool ToBoolean (double d)
48 return d != 0.0 && d != double.NaN;
51 public static bool ToBoolean (string s)
53 return s != null && s.Length > 0;
56 public static bool ToBoolean (BaseIterator iter)
58 return iter != null && iter.MoveNext ();
62 public static string ToString (object arg)
65 throw new ArgumentNullException ();
69 return ((bool) arg) ? "true" : "false";
71 return ((double) arg).ToString ("R", System.Globalization.NumberFormatInfo.InvariantInfo);
72 if (arg is BaseIterator)
74 BaseIterator iter = (BaseIterator) arg;
75 if (!iter.MoveNext ())
77 return iter.Current.Value;
79 throw new ArgumentException ();
82 public static double ToNumber (object arg)
85 throw new ArgumentNullException ();
86 if (arg is BaseIterator)
87 arg = ToString (arg); // follow on
89 string s = arg as string;
90 return ToNumber (s); // use explicit overload
95 return Convert.ToDouble ((bool) arg);
96 throw new ArgumentException ();
99 public static double ToNumber (string arg)
102 throw new ArgumentNullException ();
103 string s = arg.Trim (XmlChar.WhitespaceChars);
107 return XmlConvert.ToDouble (s);
108 } catch (System.OverflowException) {
110 } catch (System.FormatException) {
116 internal abstract class XPathFunction : Expression
118 public XPathFunction (FunctionArguments args) {}
122 internal class XPathFunctionLast : XPathFunction
124 public XPathFunctionLast (FunctionArguments args) : base (args)
127 throw new XPathException ("last takes 0 args");
130 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
132 public override object Evaluate (BaseIterator iter)
134 return (double) iter.Count;
137 public override string ToString ()
142 internal override bool IsPositional {
148 internal class XPathFunctionPosition : XPathFunction
150 public XPathFunctionPosition (FunctionArguments args) : base (args)
153 throw new XPathException ("position takes 0 args");
156 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
158 public override object Evaluate (BaseIterator iter)
160 return (double) iter.CurrentPosition;
163 public override string ToString ()
168 internal override bool IsPositional {
174 internal class XPathFunctionCount : XPathFunction
178 public XPathFunctionCount (FunctionArguments args) : base (args)
180 if (args == null || args.Tail != null)
181 throw new XPathException ("count takes 1 arg");
186 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
188 public override object Evaluate (BaseIterator iter)
190 return (double) arg0.EvaluateNodeSet (iter).Count;
193 public override bool EvaluateBoolean (BaseIterator iter)
195 if (arg0.GetReturnType (iter) == XPathResultType.NodeSet)
196 return arg0.EvaluateBoolean (iter);
198 return arg0.EvaluateNodeSet (iter).MoveNext ();
201 public override string ToString ()
203 return "count(" + arg0.ToString () + ")";
208 internal class XPathFunctionId : XPathFunction
212 public XPathFunctionId (FunctionArguments args) : base (args)
214 if (args == null || args.Tail != null)
215 throw new XPathException ("id takes 1 arg");
220 public Expression Id { get { return arg0; } }
222 private static char [] rgchWhitespace = {' ', '\t', '\r', '\n'};
223 public override XPathResultType ReturnType { get { return XPathResultType.NodeSet; }}
225 public override object Evaluate (BaseIterator iter)
228 object val = arg0.Evaluate (iter);
230 BaseIterator valItr = val as BaseIterator;
234 while (valItr.MoveNext ())
235 strArgs += valItr.Current.Value + " ";
238 strArgs = XPathFunctions.ToString (val);
240 XPathNavigator n = iter.Current.Clone ();
241 ArrayList rgNodes = new ArrayList ();
242 string [] ids = strArgs.Split (rgchWhitespace);
243 for (int i = 0; i < ids.Length; i++)
244 if (n.MoveToId (ids [i]))
245 rgNodes.Add (n.Clone ());
247 rgNodes.Sort (XPathNavigatorComparer.Instance);
248 return new ListIterator (iter, rgNodes, true);
251 public override string ToString ()
253 return "id(" + arg0.ToString () + ")";
257 internal class XPathFunctionLocalName : XPathFunction
261 public XPathFunctionLocalName (FunctionArguments args) : base (args)
265 if (args.Tail != null)
266 throw new XPathException ("local-name takes 1 or zero args");
270 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
272 public override object Evaluate (BaseIterator iter)
275 return iter.Current.LocalName;
277 BaseIterator argNs = arg0.EvaluateNodeSet (iter);
278 if (argNs == null || !argNs.MoveNext ())
280 return argNs.Current.LocalName;
283 public override string ToString ()
285 return "local-name(" + arg0.ToString () + ")";
290 internal class XPathFunctionNamespaceUri : XPathFunction
294 public XPathFunctionNamespaceUri (FunctionArguments args) : base (args)
298 if (args.Tail != null)
299 throw new XPathException ("namespace-uri takes 1 or zero args");
303 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
305 public override object Evaluate (BaseIterator iter)
308 return iter.Current.NamespaceURI;
310 BaseIterator argNs = arg0.EvaluateNodeSet (iter);
311 if (argNs == null || !argNs.MoveNext ())
313 return argNs.Current.NamespaceURI;
316 public override string ToString ()
318 return "namespace-uri(" + arg0.ToString () + ")";
323 internal class XPathFunctionName : XPathFunction
327 public XPathFunctionName (FunctionArguments args) : base (args)
331 if (args.Tail != null)
332 throw new XPathException ("name takes 1 or zero args");
336 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
338 public override object Evaluate (BaseIterator iter)
341 return iter.Current.Name;
343 BaseIterator argNs = arg0.EvaluateNodeSet (iter);
344 if (argNs == null || !argNs.MoveNext ())
346 return argNs.Current.Name;
349 public override string ToString ()
351 return "name(" + arg0.ToString () + ")";
356 internal class XPathFunctionString : XPathFunction
360 public XPathFunctionString (FunctionArguments args) : base (args)
364 if (args.Tail != null)
365 throw new XPathException ("boolean takes 1 or zero args");
369 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
371 public override object Evaluate (BaseIterator iter)
374 return iter.Current.Value;
375 return arg0.EvaluateString (iter);
378 public override string ToString ()
380 return "string(" + arg0.ToString () + ")";
385 internal class XPathFunctionConcat : XPathFunction
389 public XPathFunctionConcat (FunctionArguments args) : base (args)
391 if (args == null || args.Tail == null)
392 throw new XPathException ("concat takes 2 or more args");
394 args.ToArrayList (rgs = new ArrayList ());
397 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
399 public override object Evaluate (BaseIterator iter)
401 StringBuilder sb = new StringBuilder ();
404 for (int i = 0; i < len; i++)
405 sb.Append (((Expression)rgs[i]).EvaluateString (iter));
407 return sb.ToString ();
410 public override string ToString ()
412 StringBuilder sb = new StringBuilder ();
413 sb.Append ("concat(");
414 for (int i = 0; i < rgs.Count - 1; i++) {
415 sb.Append (rgs [i].ToString ());
418 sb.Append (rgs [rgs.Count - 1].ToString ());
420 return sb.ToString ();
425 internal class XPathFunctionStartsWith : XPathFunction
427 Expression arg0, arg1;
429 public XPathFunctionStartsWith (FunctionArguments args) : base (args)
431 if (args == null || args.Tail == null || args.Tail.Tail != null)
432 throw new XPathException ("starts-with takes 2 args");
435 arg1 = args.Tail.Arg;
438 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
440 public override object Evaluate (BaseIterator iter)
442 return arg0.EvaluateString (iter).StartsWith (arg1.EvaluateString (iter));
445 public override string ToString ()
447 return String.Concat ("starts-with(", arg0.ToString (), ",", arg1.ToString (), ")");
452 internal class XPathFunctionContains : XPathFunction
454 Expression arg0, arg1;
456 public XPathFunctionContains (FunctionArguments args) : base (args)
458 if (args == null || args.Tail == null || args.Tail.Tail != null)
459 throw new XPathException ("contains takes 2 args");
462 arg1 = args.Tail.Arg;
465 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
467 public override object Evaluate (BaseIterator iter)
469 return arg0.EvaluateString (iter).IndexOf (arg1.EvaluateString (iter)) != -1;
472 public override string ToString ()
474 return String.Concat ("contains(", arg0.ToString (), ",", arg1.ToString (), ")");
479 internal class XPathFunctionSubstringBefore : XPathFunction
481 Expression arg0, arg1;
483 public XPathFunctionSubstringBefore (FunctionArguments args) : base (args)
485 if (args == null || args.Tail == null || args.Tail.Tail != null)
486 throw new XPathException ("substring-before takes 2 args");
489 arg1 = args.Tail.Arg;
492 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
494 public override object Evaluate (BaseIterator iter)
496 string str1 = arg0.EvaluateString (iter);
497 string str2 = arg1.EvaluateString (iter);
498 int ich = str1.IndexOf (str2);
501 return str1.Substring (0, ich);
504 public override string ToString ()
506 return String.Concat ("substring-before(", arg0.ToString (), ",", arg1.ToString (), ")");
511 internal class XPathFunctionSubstringAfter : XPathFunction
513 Expression arg0, arg1;
515 public XPathFunctionSubstringAfter (FunctionArguments args) : base (args)
517 if (args == null || args.Tail == null || args.Tail.Tail != null)
518 throw new XPathException ("substring-after takes 2 args");
521 arg1 = args.Tail.Arg;
524 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
526 public override object Evaluate (BaseIterator iter)
528 string str1 = arg0.EvaluateString (iter);
529 string str2 = arg1.EvaluateString (iter);
530 int ich = str1.IndexOf (str2);
533 return str1.Substring (ich + str2.Length);
536 public override string ToString ()
538 return String.Concat ("substring-after(", arg0.ToString (), ",", arg1.ToString (), ")");
543 internal class XPathFunctionSubstring : XPathFunction
545 Expression arg0, arg1, arg2;
547 public XPathFunctionSubstring (FunctionArguments args) : base (args)
549 if (args == null || args.Tail == null || (args.Tail.Tail != null && args.Tail.Tail.Tail != null))
550 throw new XPathException ("substring takes 2 or 3 args");
553 arg1 = args.Tail.Arg;
554 if (args.Tail.Tail != null)
555 arg2= args.Tail.Tail.Arg;
558 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
560 public override object Evaluate (BaseIterator iter)
562 string str = arg0.EvaluateString (iter);
563 double ich = Math.Round (arg1.EvaluateNumber (iter)) - 1;
564 if (Double.IsNaN (ich) ||
565 Double.IsNegativeInfinity (ich) ||
566 ich >= (double) str.Length)
573 return str.Substring ((int) ich);
577 double cch = Math.Round (arg2.EvaluateNumber (iter));
578 if (Double.IsNaN (cch))
580 if (ich < 0.0 || cch < 0.0)
587 double cchMax = (double) str.Length - ich;
590 return str.Substring ((int) ich, (int) cch);
594 public override string ToString ()
596 return String.Concat (new string [] {
597 "substring(", arg0.ToString (), ",", arg1.ToString (), ",", arg2.ToString (), ")"});
602 internal class XPathFunctionStringLength : XPathFunction
606 public XPathFunctionStringLength (FunctionArguments args) : base (args)
610 if (args.Tail != null)
611 throw new XPathException ("string-length takes 1 or zero args");
615 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
617 public override object Evaluate (BaseIterator iter)
621 str = arg0.EvaluateString (iter);
623 str = iter.Current.Value;
624 return (double) str.Length;
627 public override string ToString ()
629 return String.Concat (new string [] {
630 "string-length(", arg0.ToString (), ")"});
635 internal class XPathFunctionNormalizeSpace : XPathFunction
639 public XPathFunctionNormalizeSpace (FunctionArguments args) : base (args)
643 if (args.Tail != null)
644 throw new XPathException ("normalize-space takes 1 or zero args");
648 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
650 public override object Evaluate (BaseIterator iter)
654 str = arg0.EvaluateString (iter);
656 str = iter.Current.Value;
657 System.Text.StringBuilder sb = new System.Text.StringBuilder ();
659 for (int i = 0; i < str.Length; i++) {
661 if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
676 return sb.ToString ();
679 public override string ToString ()
681 return String.Concat (new string [] {
683 arg0 != null ? arg0.ToString () : String.Empty,
689 internal class XPathFunctionTranslate : XPathFunction
691 Expression arg0, arg1, arg2;
693 public XPathFunctionTranslate (FunctionArguments args) : base (args)
695 if (args == null || args.Tail == null || args.Tail.Tail == null || args.Tail.Tail.Tail != null)
696 throw new XPathException ("translate takes 3 args");
699 arg1 = args.Tail.Arg;
700 arg2= args.Tail.Tail.Arg;
703 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
705 public override object Evaluate (BaseIterator iter)
707 string s0 = arg0.EvaluateString (iter);
708 string s1 = arg1.EvaluateString (iter);
709 string s2 = arg2.EvaluateString (iter);
711 StringBuilder ret = new StringBuilder (s0.Length);
713 int pos = 0, len = s0.Length, s2len = s2.Length;
716 int idx = s1.IndexOf (s0 [pos]);
720 ret.Append (s2 [idx]);
723 ret.Append (s0 [pos]);
728 return ret.ToString ();
731 public override string ToString ()
733 return String.Concat (new string [] {
735 arg0.ToString (), ",",
736 arg1.ToString (), ",",
737 arg2.ToString (), ")"});
742 internal class XPathFunctionBoolean : XPathFunction
746 public XPathFunctionBoolean (FunctionArguments args) : base (args)
750 if (args.Tail != null)
751 throw new XPathException ("boolean takes 1 or zero args");
755 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
757 public override object Evaluate (BaseIterator iter)
760 return XPathFunctions.ToBoolean (iter.Current.Value);
761 return arg0.EvaluateBoolean (iter);
764 public override string ToString ()
766 return String.Concat (new string [] {"boolean(", arg0.ToString (), ")"});
771 internal class XPathFunctionNot : XPathFunction
775 public XPathFunctionNot (FunctionArguments args) : base (args)
777 if (args == null || args.Tail != null)
778 throw new XPathException ("not takes one arg");
782 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
784 public override object Evaluate (BaseIterator iter)
786 return !arg0.EvaluateBoolean (iter);
789 public override string ToString ()
791 return String.Concat (new string [] {"not(", arg0.ToString (), ")"});
796 internal class XPathFunctionTrue : XPathFunction
798 public XPathFunctionTrue (FunctionArguments args) : base (args)
801 throw new XPathException ("true takes 0 args");
804 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
806 public override object Evaluate (BaseIterator iter)
811 public override string ToString ()
818 internal class XPathFunctionFalse : XPathFunction
820 public XPathFunctionFalse (FunctionArguments args) : base (args)
823 throw new XPathException ("false takes 0 args");
825 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
827 public override object Evaluate (BaseIterator iter)
832 public override string ToString ()
839 internal class XPathFunctionLang : XPathFunction
843 public XPathFunctionLang (FunctionArguments args) : base (args)
845 if (args == null || args.Tail != null)
846 throw new XPathException ("lang takes one arg");
850 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
852 public override object Evaluate (BaseIterator iter)
854 string lang = arg0.EvaluateString (iter).ToLower ();
855 string actualLang = iter.Current.XmlLang.ToLower ();
857 return lang == actualLang || lang == (actualLang.Split ('-')[0]);
860 public override string ToString ()
862 return String.Concat (new string [] {"lang(", arg0.ToString (), ")"});
867 internal class XPathFunctionNumber : XPathFunction
871 public XPathFunctionNumber (FunctionArguments args) : base (args)
875 if (args.Tail != null)
876 throw new XPathException ("number takes 1 or zero args");
880 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
882 public override object Evaluate (BaseIterator iter)
885 return XPathFunctions.ToNumber (iter.Current.Value);
886 return arg0.EvaluateNumber (iter);
889 public override string ToString ()
891 return String.Concat (new string [] {"number(", arg0.ToString (), ")"});
896 internal class XPathFunctionSum : XPathFunction
900 public XPathFunctionSum (FunctionArguments args) : base (args)
902 if (args == null || args.Tail != null)
903 throw new XPathException ("sum takes one arg");
907 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
909 public override object Evaluate (BaseIterator iter)
911 XPathNodeIterator itr = arg0.EvaluateNodeSet (iter);
914 while (itr.MoveNext ())
915 sum += XPathFunctions.ToNumber (itr.Current.Value);
920 public override string ToString ()
922 return String.Concat (new string [] {"sum(", arg0.ToString (), ")"});
927 internal class XPathFunctionFloor : XPathFunction
931 public XPathFunctionFloor (FunctionArguments args) : base (args)
933 if (args == null || args.Tail != null)
934 throw new XPathException ("floor takes one arg");
938 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
940 public override object Evaluate (BaseIterator iter)
942 return Math.Floor (arg0.EvaluateNumber (iter));
945 public override string ToString ()
947 return String.Concat (new string [] {"floor(", arg0.ToString (), ")"});
952 internal class XPathFunctionCeil : XPathFunction
956 public XPathFunctionCeil (FunctionArguments args) : base (args)
958 if (args == null || args.Tail != null)
959 throw new XPathException ("ceil takes one arg");
963 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
965 public override object Evaluate (BaseIterator iter)
967 return Math.Ceiling (arg0.EvaluateNumber (iter));
970 public override string ToString ()
972 return String.Concat (new string [] {"ceil(", arg0.ToString (), ")"});
977 internal class XPathFunctionRound : XPathFunction
981 public XPathFunctionRound (FunctionArguments args) : base (args)
983 if (args == null || args.Tail != null)
984 throw new XPathException ("round takes one arg");
988 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
990 public override object Evaluate (BaseIterator iter)
992 double arg = arg0.EvaluateNumber (iter);
993 if (arg < -0.5 || arg > 0)
994 return Math.Floor (arg + 0.5);
995 return Math.Round (arg);
998 public override string ToString ()
1000 return String.Concat (new string [] {"round(", arg0.ToString (), ")"});