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
19 /// Summary description for DefaultContext.
21 internal class DefaultContext : XsltContext
23 protected static Hashtable _htFunctions = new Hashtable ();
25 static DefaultContext()
27 Add (new XPathFunctionLast ());
28 Add (new XPathFunctionPosition ());
29 Add (new XPathFunctionCount ());
30 Add (new XPathFunctionId ());
31 Add (new XPathFunctionLocalName ());
32 Add (new XPathFunctionNamespaceUri ());
33 Add (new XPathFunctionName ());
34 Add (new XPathFunctionString ());
35 Add (new XPathFunctionConcat ());
36 Add (new XPathFunctionStartsWith ());
37 Add (new XPathFunctionContains ());
38 Add (new XPathFunctionSubstringBefore ());
39 Add (new XPathFunctionSubstringAfter ());
40 Add (new XPathFunctionSubstring ());
41 Add (new XPathFunctionStringLength ());
42 Add (new XPathFunctionNormalizeSpace ());
43 Add (new XPathFunctionTranslate ());
44 Add (new XPathFunctionBoolean ());
45 Add (new XPathFunctionNot ());
46 Add (new XPathFunctionTrue ());
47 Add (new XPathFunctionFalse ());
48 Add (new XPathFunctionLang ());
49 Add (new XPathFunctionNumber ());
50 Add (new XPathFunctionSum ());
51 Add (new XPathFunctionFloor ());
52 Add (new XPathFunctionCeil ());
53 Add (new XPathFunctionRound ());
57 public override IXsltContextFunction ResolveFunction (string prefix, string name, XPathResultType[] ArgTypes)
60 if (prefix != null && prefix != "") // TODO: should we allow some namespaces here?
63 // match the function name
64 XPathFunction fn = (XPathFunction) _htFunctions [name];
68 // check the number of arguments
69 int cArgs = ArgTypes.Length;
70 if (cArgs < fn.Minargs || cArgs > fn.Maxargs)
73 // check the types of the arguments
74 XPathResultType [] rgTypes = fn.ArgTypes;
82 int cTypes = rgTypes.Length;
83 XPathResultType [] rgTypesRequested = ArgTypes;
84 for (int iArg = 0; iArg < cArgs; iArg ++)
86 XPathResultType typeRequested = rgTypesRequested [iArg];
87 XPathResultType typeDefined = (iArg >= cTypes) ? rgTypes [cTypes - 1] : rgTypes [iArg];
89 // if the arguments don't match...
90 if (typeDefined != XPathResultType.Any &&
91 typeDefined != typeRequested)
93 // if the function requires a nodeset
94 // then the arg should be .Any
95 // other conversions are illegal
96 if (typeDefined == XPathResultType.NodeSet &&
97 typeRequested != XPathResultType.Any)
106 public override IXsltContextVariable ResolveVariable (string prefix, string name)
111 public override int CompareDocument (string baseUri, string nextBaseUri) { throw new NotImplementedException (); }
113 public override bool PreserveWhitespace (XPathNavigator nav) { throw new NotImplementedException (); }
115 public override bool Whitespace { get { throw new NotImplementedException (); }}
116 protected static void Add (XPathFunction fn)
118 _htFunctions.Add (fn.Name, fn);
123 internal class XPathFunctions
125 public static bool ToBoolean (object arg)
128 throw new ArgumentNullException ();
133 double dArg = (double) arg;
134 return (dArg != 0.0 && !double.IsNaN (dArg));
137 return ((string) arg).Length != 0;
138 if (arg is BaseIterator)
140 BaseIterator iter = (BaseIterator) arg;
141 return iter.MoveNext ();
143 throw new ArgumentException ();
146 public static string ToString (object arg)
149 throw new ArgumentNullException ();
153 return ((bool) arg) ? "true" : "false";
155 return ((double) arg).ToString ("R", System.Globalization.NumberFormatInfo.InvariantInfo);
156 if (arg is BaseIterator)
158 BaseIterator iter = (BaseIterator) arg;
159 if (!iter.MoveNext ())
161 return iter.Current.Value;
163 throw new ArgumentException ();
166 public static double ToNumber (object arg)
169 throw new ArgumentNullException ();
174 return XmlConvert.ToDouble ((string) arg); // TODO: spec? convert string to number
176 catch (System.FormatException)
181 if (arg is BaseIterator)
182 arg = ToString (arg); // follow on
186 return Convert.ToDouble ((bool) arg);
187 throw new ArgumentException ();
191 internal abstract class XPathFunction : IXsltContextFunction
193 public abstract XPathResultType ReturnType { get; }
194 public abstract int Minargs { get; }
195 public abstract int Maxargs { get; }
196 public abstract XPathResultType [] ArgTypes { get; }
197 public object Invoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
199 return TypesafeInvoke (xsltContext, args, docContext);
202 public abstract string Name { get; }
203 public abstract object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext);
207 internal class XPathFunctionLast : XPathFunction
209 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
210 public override int Minargs { get { return 0; }}
211 public override int Maxargs { get { return 0; }}
212 public override XPathResultType [] ArgTypes { get { return null; }}
213 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
215 throw new NotImplementedException (); // special-cased
217 public override string Name { get { return "last"; }}
221 internal class XPathFunctionPosition : XPathFunction
223 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
224 public override int Minargs { get { return 0; }}
225 public override int Maxargs { get { return 0; }}
226 public override XPathResultType [] ArgTypes { get { return null; }}
227 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
229 throw new NotImplementedException (); // special-cased
231 public override string Name { get { return "position"; }}
235 internal class XPathFunctionCount : XPathFunction
237 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
238 public override int Minargs { get { return 1; }}
239 public override int Maxargs { get { return 1; }}
240 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.NodeSet }; }}
241 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
243 return (double) ((BaseIterator) args [0]).Count;
245 public override string Name { get { return "count"; }}
249 internal class XPathFunctionId : XPathFunction
251 private static char [] rgchWhitespace = {' ', '\t', '\r', '\n'};
252 public override XPathResultType ReturnType { get { return XPathResultType.NodeSet; }}
253 public override int Minargs { get { return 1; }}
254 public override int Maxargs { get { return 1; }}
255 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Any }; }}
257 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
260 BaseIterator iter = args [0] as BaseIterator;
264 while (!iter.MoveNext ())
265 strArgs += iter.Current.Value + " ";
268 strArgs = XPathFunctions.ToString (args [0]);
269 string [] rgstrArgs = strArgs.Split (rgchWhitespace);
270 ArrayList rgNodes = new ArrayList ();
271 foreach (string strArg in rgstrArgs)
273 if (docContext.MoveToId (strArg))
274 rgNodes.Add (docContext.Clone ());
276 return new EnumeratorIterator (iter, rgNodes.GetEnumerator ());
278 public override string Name { get { return "id"; }}
282 internal class XPathFunctionLocalName : XPathFunction
284 public override XPathResultType ReturnType { get { return XPathResultType.NodeSet; }}
285 public override int Minargs { get { return 0; }}
286 public override int Maxargs { get { return 1; }}
287 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.NodeSet }; }}
288 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
290 if (args.Length == 0)
291 return docContext.LocalName;
292 BaseIterator iter = (BaseIterator) args [0];
293 if (iter == null || !iter.MoveNext ())
295 return iter.Current.LocalName;
297 public override string Name { get { return "local-name"; }}
301 internal class XPathFunctionNamespaceUri : XPathFunction
303 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
304 public override int Minargs { get { return 0; }}
305 public override int Maxargs { get { return 1; }}
306 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.NodeSet }; }}
308 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
310 if (args.Length == 0)
311 return docContext.NamespaceURI;
312 BaseIterator iter = (BaseIterator) args [0];
313 if (iter == null || !iter.MoveNext ())
315 return iter.Current.NamespaceURI; // TODO: should the namespace be expanded wrt. the given context?
317 public override string Name { get { return "namespace-uri"; }}
321 internal class XPathFunctionName : XPathFunction
323 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
324 public override int Minargs { get { return 0; }}
325 public override int Maxargs { get { return 1; }}
326 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.NodeSet }; }}
328 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
330 if (args.Length == 0)
331 return docContext.Name;
332 BaseIterator iter = (BaseIterator) args [0];
333 if (iter == null || !iter.MoveNext ())
335 return iter.Current.Name;
337 public override string Name { get { return "name"; }}
341 internal class XPathFunctionString : XPathFunction
343 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
344 public override int Minargs { get { return 0; }}
345 public override int Maxargs { get { return 1; }}
346 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Any }; }}
347 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
349 return XPathFunctions.ToString (args [0]);
351 public override string Name { get { return "string"; }}
355 internal class XPathFunctionConcat : XPathFunction
357 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
358 public override int Minargs { get { return 2; }}
359 public override int Maxargs { get { return int.MaxValue; }}
360 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Any, XPathResultType.Any, XPathResultType.Any }; }}
361 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
363 StringBuilder sb = new StringBuilder ();
364 foreach (object arg in args)
365 sb.Append (XPathFunctions.ToString (arg));
366 return sb.ToString ();
368 public override string Name { get { return "concat"; }}
372 internal class XPathFunctionStartsWith : XPathFunction
374 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
375 public override int Minargs { get { return 2; }}
376 public override int Maxargs { get { return 2; }}
377 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String, XPathResultType.String }; }}
378 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
380 string str1 = (string) args [0];
381 string str2 = (string) args [1];
382 return str1.StartsWith (str2);
384 public override string Name { get { return "starts-with"; }}
388 internal class XPathFunctionContains : XPathFunction
390 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
391 public override int Minargs { get { return 2; }}
392 public override int Maxargs { get { return 2; }}
393 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String, XPathResultType.String }; }}
394 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
396 string str1 = (string) args [0];
397 string str2 = (string) args [1];
398 return str1.IndexOf (str2) != -1;
400 public override string Name { get { return "contains"; }}
404 internal class XPathFunctionSubstringBefore : XPathFunction
406 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
407 public override int Minargs { get { return 2; }}
408 public override int Maxargs { get { return 2; }}
409 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String, XPathResultType.String }; }}
410 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
412 string str1 = (string) args [0];
413 string str2 = (string) args [1];
414 int ich = str1.IndexOf (str2);
417 return str1.Substring (0, ich);
419 public override string Name { get { return "substring-before"; }}
423 internal class XPathFunctionSubstringAfter : XPathFunction
425 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
426 public override int Minargs { get { return 2; }}
427 public override int Maxargs { get { return 2; }}
428 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String, XPathResultType.String }; }}
429 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
431 string str1 = (string) args [0];
432 string str2 = (string) args [1];
433 int ich = str1.IndexOf (str2);
436 return str1.Substring (ich + str2.Length);
438 public override string Name { get { return "substring-after"; }}
442 internal class XPathFunctionSubstring : XPathFunction
444 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
445 public override int Minargs { get { return 2; }}
446 public override int Maxargs { get { return 3; }}
447 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String, XPathResultType.Number, XPathResultType.Number }; }}
449 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
451 // TODO: check this, what the hell were they smoking?
452 string str = (string) args [0];
453 double ich = Math.Round ((double) args [1]) - 1;
454 if (Double.IsNaN (ich) || ich >= (double) str.Length)
457 if (args.Length == 2)
461 return str.Substring ((int) ich);
465 double cch = Math.Round ((double) args [2]);
466 if (Double.IsNaN (cch))
468 if (ich < 0.0 || cch < 0.0)
475 double cchMax = (double) str.Length - ich;
478 return str.Substring ((int) ich, (int) cch);
481 public override string Name { get { return "substring"; }}
485 internal class XPathFunctionStringLength : XPathFunction
487 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
488 public override int Minargs { get { return 0; }}
489 public override int Maxargs { get { return 1; }}
490 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String }; }}
491 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
494 if (args.Length == 1)
495 str = (string) args [0];
497 str = docContext.Value;
498 return (double) str.Length;
500 public override string Name { get { return "string-length"; }}
504 internal class XPathFunctionNormalizeSpace : XPathFunction
506 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
507 public override int Minargs { get { return 0; }}
508 public override int Maxargs { get { return 1; }}
509 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String }; }}
511 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
514 if (args.Length == 1)
515 str = (string) args [0];
517 str = docContext.Value;
518 System.Text.StringBuilder sb = new System.Text.StringBuilder ();
520 foreach (char ch in str)
522 if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
537 return sb.ToString ();
539 public override string Name { get { return "normalize-space"; }}
543 internal class XPathFunctionTranslate : XPathFunction
545 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
546 public override int Minargs { get { return 3; }}
547 public override int Maxargs { get { return 3; }}
548 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String, XPathResultType.String, XPathResultType.String }; }}
550 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
552 throw new NotImplementedException ();
554 public override string Name { get { return "translate"; }}
558 internal class XPathFunctionBoolean : XPathFunction
560 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
561 public override int Minargs { get { return 1; }}
562 public override int Maxargs { get { return 1; }}
563 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Any }; }}
564 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
566 return XPathFunctions.ToBoolean (args [0]);
568 public override string Name { get { return "boolean"; }}
572 internal class XPathFunctionNot : XPathFunction
574 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
575 public override int Minargs { get { return 1; }}
576 public override int Maxargs { get { return 1; }}
577 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Any }; }}
578 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
580 return !(XPathFunctions.ToBoolean (args [0]));
582 public override string Name { get { return "not"; }}
586 internal class XPathFunctionTrue : XPathFunction
588 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
589 public override int Minargs { get { return 0; }}
590 public override int Maxargs { get { return 0; }}
591 public override XPathResultType [] ArgTypes { get { return null; }}
592 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
596 public override string Name { get { return "true"; }}
600 internal class XPathFunctionFalse : XPathFunction
602 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
603 public override int Minargs { get { return 0; }}
604 public override int Maxargs { get { return 0; }}
605 public override XPathResultType [] ArgTypes { get { return null; }}
606 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
610 public override string Name { get { return "false"; }}
614 internal class XPathFunctionLang : XPathFunction
616 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
617 public override int Minargs { get { return 1; }}
618 public override int Maxargs { get { return 1; }}
619 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String }; }}
620 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
622 string lang = ((string)args[0]).ToLower ();
623 string actualLang = docContext.XmlLang.ToLower ();
625 return lang == actualLang || lang == (actualLang.Split ('-')[0]);
627 public override string Name { get { return "lang"; }}
631 internal class XPathFunctionNumber : XPathFunction
633 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
634 public override int Minargs { get { return 0; }}
635 public override int Maxargs { get { return 1; }}
636 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Any }; }}
637 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
639 return XPathFunctions.ToNumber (args [0]);
641 public override string Name { get { return "number"; }}
645 internal class XPathFunctionSum : XPathFunction
647 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
648 public override int Minargs { get { return 1; }}
649 public override int Maxargs { get { return 1; }}
650 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.NodeSet }; }}
652 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
654 throw new NotImplementedException ();
656 public override string Name { get { return "sum"; }}
660 internal class XPathFunctionFloor : XPathFunction
662 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
663 public override int Minargs { get { return 1; }}
664 public override int Maxargs { get { return 1; }}
665 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Number }; }}
666 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
668 return Math.Floor ((double) args [0]);
670 public override string Name { get { return "floor"; }}
674 internal class XPathFunctionCeil : XPathFunction
676 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
677 public override int Minargs { get { return 1; }}
678 public override int Maxargs { get { return 1; }}
679 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Number }; }}
680 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
682 return Math.Ceiling ((double) args [0]);
684 public override string Name { get { return "ceil"; }}
688 internal class XPathFunctionRound : XPathFunction
690 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
691 public override int Minargs { get { return 1; }}
692 public override int Maxargs { get { return 1; }}
693 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Number }; }}
694 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
696 double arg = (double) args [0];
697 if (arg < -0.5 || arg > 0)
698 return Math.Floor (arg + 0.5);
699 return Math.Round (arg);
701 public override string Name { get { return "round"; }}