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 ();
69 return XmlConvert.ToDouble ((string) arg); // TODO: spec? convert string to number
71 catch (System.FormatException)
76 if (arg is BaseIterator)
77 arg = ToString (arg); // follow on
81 return Convert.ToDouble ((bool) arg);
82 throw new ArgumentException ();
85 public static double ToNumber (string arg)
88 return XmlConvert.ToDouble ((string) arg.Trim (XmlChar.WhitespaceChars));
89 } catch (System.OverflowException) {
91 } catch (System.FormatException) {
97 internal abstract class XPathFunction : Expression
99 public XPathFunction (FunctionArguments args) {}
103 internal class XPathFunctionLast : XPathFunction
105 public XPathFunctionLast (FunctionArguments args) : base (args)
108 throw new XPathException ("last takes 0 args");
111 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
113 public override object Evaluate (BaseIterator iter)
115 return (double) iter.Count;
118 public override string ToString ()
125 internal class XPathFunctionPosition : XPathFunction
127 public XPathFunctionPosition (FunctionArguments args) : base (args)
130 throw new XPathException ("position takes 0 args");
133 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
135 public override object Evaluate (BaseIterator iter)
137 return (double) iter.CurrentPosition;
140 public override string ToString ()
147 internal class XPathFunctionCount : XPathFunction
151 public XPathFunctionCount (FunctionArguments args) : base (args)
153 if (args == null || args.Tail != null)
154 throw new XPathException ("count takes 1 arg");
159 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
161 public override object Evaluate (BaseIterator iter)
163 return (double) arg0.EvaluateNodeSet (iter).Count;
166 public override bool EvaluateBoolean (BaseIterator iter)
168 if (arg0.GetReturnType (iter) == XPathResultType.NodeSet)
169 return arg0.EvaluateBoolean (iter);
171 return arg0.EvaluateNodeSet (iter).MoveNext ();
174 public override string ToString ()
176 return "count(" + arg0.ToString () + ")";
181 internal class XPathFunctionId : XPathFunction
185 public XPathFunctionId (FunctionArguments args) : base (args)
187 if (args == null || args.Tail != null)
188 throw new XPathException ("id takes 1 arg");
193 public Expression Id { get { return arg0; } }
195 private static char [] rgchWhitespace = {' ', '\t', '\r', '\n'};
196 public override XPathResultType ReturnType { get { return XPathResultType.NodeSet; }}
198 public override object Evaluate (BaseIterator iter)
201 object val = arg0.Evaluate (iter);
203 BaseIterator valItr = val as BaseIterator;
207 while (valItr.MoveNext ())
208 strArgs += valItr.Current.Value + " ";
211 strArgs = XPathFunctions.ToString (val);
213 XPathNavigator n = iter.Current.Clone ();
214 ArrayList rgNodes = new ArrayList ();
215 foreach (string strArg in strArgs.Split (rgchWhitespace))
217 if (n.MoveToId (strArg)) {
218 rgNodes.Add (n.Clone ());
221 rgNodes.Sort (XPathNavigatorComparer.Instance);
222 return new EnumeratorIterator (iter, rgNodes.GetEnumerator ());
225 public override string ToString ()
227 return "id(" + arg0.ToString () + ")";
231 internal class XPathFunctionLocalName : XPathFunction
235 public XPathFunctionLocalName (FunctionArguments args) : base (args)
239 if (args.Tail != null)
240 throw new XPathException ("local-name takes 1 or zero args");
244 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
246 public override object Evaluate (BaseIterator iter)
249 return iter.Current.LocalName;
251 BaseIterator argNs = arg0.EvaluateNodeSet (iter);
252 if (argNs == null || !argNs.MoveNext ())
254 return argNs.Current.LocalName;
257 public override string ToString ()
259 return "local-name(" + arg0.ToString () + ")";
264 internal class XPathFunctionNamespaceUri : XPathFunction
268 public XPathFunctionNamespaceUri (FunctionArguments args) : base (args)
272 if (args.Tail != null)
273 throw new XPathException ("namespace-uri takes 1 or zero args");
277 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
279 public override object Evaluate (BaseIterator iter)
282 return iter.Current.NamespaceURI;
284 BaseIterator argNs = arg0.EvaluateNodeSet (iter);
285 if (argNs == null || !argNs.MoveNext ())
287 return argNs.Current.NamespaceURI;
290 public override string ToString ()
292 return "namespace-uri(" + arg0.ToString () + ")";
297 internal class XPathFunctionName : XPathFunction
301 public XPathFunctionName (FunctionArguments args) : base (args)
305 if (args.Tail != null)
306 throw new XPathException ("name takes 1 or zero args");
310 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
312 public override object Evaluate (BaseIterator iter)
315 return iter.Current.Name;
317 BaseIterator argNs = arg0.EvaluateNodeSet (iter);
318 if (argNs == null || !argNs.MoveNext ())
320 return argNs.Current.Name;
323 public override string ToString ()
325 return "name(" + arg0.ToString () + ")";
330 internal class XPathFunctionString : XPathFunction
334 public XPathFunctionString (FunctionArguments args) : base (args)
338 if (args.Tail != null)
339 throw new XPathException ("boolean takes 1 or zero args");
343 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
345 public override object Evaluate (BaseIterator iter)
348 return iter.Current.Value;
349 return arg0.EvaluateString (iter);
352 public override string ToString ()
354 return "string(" + arg0.ToString () + ")";
359 internal class XPathFunctionConcat : XPathFunction
363 public XPathFunctionConcat (FunctionArguments args) : base (args)
365 if (args == null || args.Tail == null)
366 throw new XPathException ("concat takes 2 or more args");
368 args.ToArrayList (rgs = new ArrayList ());
371 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
373 public override object Evaluate (BaseIterator iter)
375 StringBuilder sb = new StringBuilder ();
378 for (int i = 0; i < len; i++)
379 sb.Append (((Expression)rgs[i]).EvaluateString (iter));
381 return sb.ToString ();
384 public override string ToString ()
386 StringBuilder sb = new StringBuilder ();
387 sb.Append ("concat(");
388 for (int i = 0; i < rgs.Count - 1; i++) {
389 sb.Append (rgs [i].ToString ());
392 sb.Append (rgs [rgs.Count - 1].ToString ());
394 return sb.ToString ();
399 internal class XPathFunctionStartsWith : XPathFunction
401 Expression arg0, arg1;
403 public XPathFunctionStartsWith (FunctionArguments args) : base (args)
405 if (args == null || args.Tail == null || args.Tail.Tail != null)
406 throw new XPathException ("starts-with takes 2 args");
409 arg1 = args.Tail.Arg;
412 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
414 public override object Evaluate (BaseIterator iter)
416 return arg0.EvaluateString (iter).StartsWith (arg1.EvaluateString (iter));
419 public override string ToString ()
421 return String.Concat ("starts-with(", arg0.ToString (), ",", arg1.ToString (), ")");
426 internal class XPathFunctionContains : XPathFunction
428 Expression arg0, arg1;
430 public XPathFunctionContains (FunctionArguments args) : base (args)
432 if (args == null || args.Tail == null || args.Tail.Tail != null)
433 throw new XPathException ("contains takes 2 args");
436 arg1 = args.Tail.Arg;
439 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
441 public override object Evaluate (BaseIterator iter)
443 return arg0.EvaluateString (iter).IndexOf (arg1.EvaluateString (iter)) != -1;
446 public override string ToString ()
448 return String.Concat ("contains(", arg0.ToString (), ",", arg1.ToString (), ")");
453 internal class XPathFunctionSubstringBefore : XPathFunction
455 Expression arg0, arg1;
457 public XPathFunctionSubstringBefore (FunctionArguments args) : base (args)
459 if (args == null || args.Tail == null || args.Tail.Tail != null)
460 throw new XPathException ("substring-before takes 2 args");
463 arg1 = args.Tail.Arg;
466 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
468 public override object Evaluate (BaseIterator iter)
470 string str1 = arg0.EvaluateString (iter);
471 string str2 = arg1.EvaluateString (iter);
472 int ich = str1.IndexOf (str2);
475 return str1.Substring (0, ich);
478 public override string ToString ()
480 return String.Concat ("substring-before(", arg0.ToString (), ",", arg1.ToString (), ")");
485 internal class XPathFunctionSubstringAfter : XPathFunction
487 Expression arg0, arg1;
489 public XPathFunctionSubstringAfter (FunctionArguments args) : base (args)
491 if (args == null || args.Tail == null || args.Tail.Tail != null)
492 throw new XPathException ("substring-after takes 2 args");
495 arg1 = args.Tail.Arg;
498 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
500 public override object Evaluate (BaseIterator iter)
502 string str1 = arg0.EvaluateString (iter);
503 string str2 = arg1.EvaluateString (iter);
504 int ich = str1.IndexOf (str2);
507 return str1.Substring (ich + str2.Length);
510 public override string ToString ()
512 return String.Concat ("substring-after(", arg0.ToString (), ",", arg1.ToString (), ")");
517 internal class XPathFunctionSubstring : XPathFunction
519 Expression arg0, arg1, arg2;
521 public XPathFunctionSubstring (FunctionArguments args) : base (args)
523 if (args == null || args.Tail == null || (args.Tail.Tail != null && args.Tail.Tail.Tail != null))
524 throw new XPathException ("substring takes 2 or 3 args");
527 arg1 = args.Tail.Arg;
528 if (args.Tail.Tail != null)
529 arg2= args.Tail.Tail.Arg;
532 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
535 public override object Evaluate (BaseIterator iter)
537 // TODO: check this, what the hell were they smoking?
538 string str = arg0.EvaluateString (iter);
539 double ich = Math.Round (arg1.EvaluateNumber (iter)) - 1;
540 if (Double.IsNaN (ich) || ich >= (double) str.Length)
547 return str.Substring ((int) ich);
551 double cch = Math.Round (arg2.EvaluateNumber (iter));
552 if (Double.IsNaN (cch))
554 if (ich < 0.0 || cch < 0.0)
561 double cchMax = (double) str.Length - ich;
564 return str.Substring ((int) ich, (int) cch);
568 public override string ToString ()
570 return String.Concat (new string [] {
571 "substring(", arg0.ToString (), ",", arg1.ToString (), ",", arg2.ToString (), ")"});
576 internal class XPathFunctionStringLength : XPathFunction
580 public XPathFunctionStringLength (FunctionArguments args) : base (args)
584 if (args.Tail != null)
585 throw new XPathException ("string-length takes 1 or zero args");
589 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
591 public override object Evaluate (BaseIterator iter)
595 str = arg0.EvaluateString (iter);
597 str = iter.Current.Value;
598 return (double) str.Length;
601 public override string ToString ()
603 return String.Concat (new string [] {
604 "string-length(", arg0.ToString (), ")"});
609 internal class XPathFunctionNormalizeSpace : XPathFunction
613 public XPathFunctionNormalizeSpace (FunctionArguments args) : base (args)
617 if (args.Tail != null)
618 throw new XPathException ("normalize-space takes 1 or zero args");
622 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
624 public override object Evaluate (BaseIterator iter)
628 str = arg0.EvaluateString (iter);
630 str = iter.Current.Value;
631 System.Text.StringBuilder sb = new System.Text.StringBuilder ();
633 foreach (char ch in str)
635 if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
650 return sb.ToString ();
653 public override string ToString ()
655 return String.Concat (new string [] {
657 arg0 != null ? arg0.ToString () : String.Empty,
663 internal class XPathFunctionTranslate : XPathFunction
665 Expression arg0, arg1, arg2;
667 public XPathFunctionTranslate (FunctionArguments args) : base (args)
669 if (args == null || args.Tail == null || args.Tail.Tail == null || args.Tail.Tail.Tail != null)
670 throw new XPathException ("translate takes 3 args");
673 arg1 = args.Tail.Arg;
674 arg2= args.Tail.Tail.Arg;
677 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
679 public override object Evaluate (BaseIterator iter)
681 string s0 = arg0.EvaluateString (iter);
682 string s1 = arg1.EvaluateString (iter);
683 string s2 = arg2.EvaluateString (iter);
685 StringBuilder ret = new StringBuilder (s0.Length);
687 int pos = 0, len = s0.Length, s2len = s2.Length;
690 int idx = s1.IndexOf (s0 [pos]);
694 ret.Append (s2 [idx]);
697 ret.Append (s0 [pos]);
702 return ret.ToString ();
705 public override string ToString ()
707 return String.Concat (new string [] {
709 arg0.ToString (), ",",
710 arg1.ToString (), ",",
711 arg2.ToString (), ")"});
716 internal class XPathFunctionBoolean : XPathFunction
720 public XPathFunctionBoolean (FunctionArguments args) : base (args)
724 if (args.Tail != null)
725 throw new XPathException ("boolean takes 1 or zero args");
729 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
731 public override object Evaluate (BaseIterator iter)
734 return XPathFunctions.ToBoolean (iter.Current.Value);
735 return arg0.EvaluateBoolean (iter);
738 public override string ToString ()
740 return String.Concat (new string [] {"boolean(", arg0.ToString (), ")"});
745 internal class XPathFunctionNot : XPathFunction
749 public XPathFunctionNot (FunctionArguments args) : base (args)
751 if (args == null || args.Tail != null)
752 throw new XPathException ("not takes one arg");
756 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
758 public override object Evaluate (BaseIterator iter)
760 return !arg0.EvaluateBoolean (iter);
763 public override string ToString ()
765 return String.Concat (new string [] {"not(", arg0.ToString (), ")"});
770 internal class XPathFunctionTrue : XPathFunction
772 public XPathFunctionTrue (FunctionArguments args) : base (args)
775 throw new XPathException ("true takes 0 args");
778 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
780 public override object Evaluate (BaseIterator iter)
785 public override string ToString ()
792 internal class XPathFunctionFalse : XPathFunction
794 public XPathFunctionFalse (FunctionArguments args) : base (args)
797 throw new XPathException ("false takes 0 args");
799 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
801 public override object Evaluate (BaseIterator iter)
806 public override string ToString ()
813 internal class XPathFunctionLang : XPathFunction
817 public XPathFunctionLang (FunctionArguments args) : base (args)
819 if (args == null || args.Tail != null)
820 throw new XPathException ("lang takes one arg");
824 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
826 public override object Evaluate (BaseIterator iter)
828 string lang = arg0.EvaluateString (iter).ToLower ();
829 string actualLang = iter.Current.XmlLang.ToLower ();
831 return lang == actualLang || lang == (actualLang.Split ('-')[0]);
834 public override string ToString ()
836 return String.Concat (new string [] {"lang(", arg0.ToString (), ")"});
841 internal class XPathFunctionNumber : XPathFunction
845 public XPathFunctionNumber (FunctionArguments args) : base (args)
849 if (args.Tail != null)
850 throw new XPathException ("number takes 1 or zero args");
854 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
856 public override object Evaluate (BaseIterator iter)
859 return XPathFunctions.ToNumber (iter.Current.Value);
860 return arg0.EvaluateNumber (iter);
863 public override string ToString ()
865 return String.Concat (new string [] {"number(", arg0.ToString (), ")"});
870 internal class XPathFunctionSum : XPathFunction
874 public XPathFunctionSum (FunctionArguments args) : base (args)
876 if (args == null || args.Tail != null)
877 throw new XPathException ("sum takes one arg");
881 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
883 public override object Evaluate (BaseIterator iter)
885 XPathNodeIterator itr = arg0.EvaluateNodeSet (iter);
888 while (itr.MoveNext ())
889 sum += XPathFunctions.ToNumber (itr.Current.Value);
894 public override string ToString ()
896 return String.Concat (new string [] {"sum(", arg0.ToString (), ")"});
901 internal class XPathFunctionFloor : XPathFunction
905 public XPathFunctionFloor (FunctionArguments args) : base (args)
907 if (args == null || args.Tail != null)
908 throw new XPathException ("floor takes one arg");
912 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
914 public override object Evaluate (BaseIterator iter)
916 return Math.Floor (arg0.EvaluateNumber (iter));
919 public override string ToString ()
921 return String.Concat (new string [] {"floor(", arg0.ToString (), ")"});
926 internal class XPathFunctionCeil : XPathFunction
930 public XPathFunctionCeil (FunctionArguments args) : base (args)
932 if (args == null || args.Tail != null)
933 throw new XPathException ("ceil takes one arg");
937 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
939 public override object Evaluate (BaseIterator iter)
941 return Math.Ceiling (arg0.EvaluateNumber (iter));
944 public override string ToString ()
946 return String.Concat (new string [] {"ceil(", arg0.ToString (), ")"});
951 internal class XPathFunctionRound : XPathFunction
955 public XPathFunctionRound (FunctionArguments args) : base (args)
957 if (args == null || args.Tail != null)
958 throw new XPathException ("round takes one arg");
962 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
964 public override object Evaluate (BaseIterator iter)
966 double arg = arg0.EvaluateNumber (iter);
967 if (arg < -0.5 || arg > 0)
968 return Math.Floor (arg + 0.5);
969 return Math.Round (arg);
972 public override string ToString ()
974 return String.Concat (new string [] {"round(", arg0.ToString (), ")"});