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;
15 namespace System.Xml.XPath
18 /// Summary description for DefaultContext.
20 internal class DefaultContext : XsltContext
22 protected static Hashtable _htFunctions = new Hashtable ();
24 static DefaultContext()
26 Add (new XPathFunctionLast ());
27 Add (new XPathFunctionPosition ());
28 Add (new XPathFunctionCount ());
29 Add (new XPathFunctionId ());
30 Add (new XPathFunctionLocalName ());
31 Add (new XPathFunctionNamespaceUri ());
32 Add (new XPathFunctionName ());
33 Add (new XPathFunctionString ());
34 Add (new XPathFunctionConcat ());
35 Add (new XPathFunctionStartsWith ());
36 Add (new XPathFunctionContains ());
37 Add (new XPathFunctionSubstringBefore ());
38 Add (new XPathFunctionSubstringAfter ());
39 Add (new XPathFunctionSubstring ());
40 Add (new XPathFunctionStringLength ());
41 Add (new XPathFunctionNormalizeSpace ());
42 Add (new XPathFunctionTranslate ());
43 Add (new XPathFunctionBoolean ());
44 Add (new XPathFunctionNot ());
45 Add (new XPathFunctionTrue ());
46 Add (new XPathFunctionFalse ());
47 Add (new XPathFunctionLang ());
48 Add (new XPathFunctionNumber ());
49 Add (new XPathFunctionSum ());
50 Add (new XPathFunctionFloor ());
51 Add (new XPathFunctionCeil ());
52 Add (new XPathFunctionRound ());
56 public override IXsltContextFunction ResolveFunction (string prefix, string name, XPathResultType[] ArgTypes)
59 if (prefix != null && prefix != "") // TODO: should we allow some namespaces here?
62 // match the function name
63 XPathFunction fn = (XPathFunction) _htFunctions [name];
67 // check the number of arguments
68 int cArgs = ArgTypes.Length;
69 if (cArgs < fn.Minargs || cArgs > fn.Maxargs)
72 // check the types of the arguments
73 XPathResultType [] rgTypes = fn.ArgTypes;
81 int cTypes = rgTypes.Length;
82 for (int iArg = 0; iArg < cArgs; iArg ++)
84 XPathResultType typeRequested = ArgTypes [iArg];
85 XPathResultType typeDefined = (iArg >= cTypes) ? rgTypes [cTypes - 1] : rgTypes [iArg];
86 if (typeDefined != XPathResultType.Any && typeDefined != typeRequested)
92 public override IXsltContextVariable ResolveVariable (string prefix, string name)
97 public override int CompareDocument (string baseUri, string nextBaseUri) { throw new NotImplementedException (); }
99 public override bool PreserveWhitespace (XPathNavigator nav) { throw new NotImplementedException (); }
101 public override bool Whitespace { get { throw new NotImplementedException (); }}
102 protected static void Add (XPathFunction fn)
104 _htFunctions.Add (fn.Name, fn);
108 internal class XPathFunctions
110 public static bool ToBoolean (object arg)
113 throw new ArgumentNullException ();
118 double dArg = (double) arg;
119 return (dArg != 0.0 && !double.IsNaN (dArg));
122 return ((string) arg).Length != 0;
123 if (arg is BaseIterator)
125 BaseIterator iter = (BaseIterator) arg;
126 return iter.MoveNext ();
128 throw new ArgumentException ();
131 public static string ToString (object arg)
134 throw new ArgumentNullException ();
138 return ((bool) arg) ? "true" : "false";
140 return (string) XmlConvert.ToString ((double) arg); // TODO: spec? convert number to string
141 if (arg is BaseIterator)
143 BaseIterator iter = (BaseIterator) arg;
144 if (!iter.MoveNext ())
146 return iter.Current.Value;
148 throw new ArgumentException ();
151 public static double ToNumber (object arg)
154 throw new ArgumentNullException ();
155 if (arg is BaseIterator)
156 arg = ToString (arg); // follow on
158 return XmlConvert.ToDouble ((string) arg); // TODO: spec? convert string to number
162 return Convert.ToDouble ((bool) arg);
163 throw new ArgumentException ();
167 internal abstract class XPathFunction : IXsltContextFunction
169 public abstract XPathResultType ReturnType { get; }
170 public abstract int Minargs { get; }
171 public abstract int Maxargs { get; }
172 public abstract XPathResultType [] ArgTypes { get; }
173 public object Invoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
175 return TypesafeInvoke (xsltContext, args, docContext);
178 public abstract string Name { get; }
179 public abstract object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext);
182 internal class XPathFunctionLast : XPathFunction
184 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
185 public override int Minargs { get { return 0; }}
186 public override int Maxargs { get { return 0; }}
187 public override XPathResultType [] ArgTypes { get { return null; }}
188 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
190 throw new NotImplementedException (); // special-cased
192 public override string Name { get { return "last"; }}
194 internal class XPathFunctionPosition : XPathFunction
196 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
197 public override int Minargs { get { return 0; }}
198 public override int Maxargs { get { return 0; }}
199 public override XPathResultType [] ArgTypes { get { return null; }}
200 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
202 throw new NotImplementedException (); // special-cased
204 public override string Name { get { return "position"; }}
206 internal class XPathFunctionCount : XPathFunction
208 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
209 public override int Minargs { get { return 1; }}
210 public override int Maxargs { get { return 1; }}
211 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.NodeSet }; }}
212 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
214 return ((BaseIterator) args [0]).Count;
216 public override string Name { get { return "count"; }}
218 internal class XPathFunctionId : XPathFunction
220 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
221 public override int Minargs { get { return 1; }}
222 public override int Maxargs { get { return 1; }}
223 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Any }; }}
225 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
227 throw new NotImplementedException ();
229 public override string Name { get { return "id"; }}
231 internal class XPathFunctionLocalName : XPathFunction
233 public override XPathResultType ReturnType { get { return XPathResultType.NodeSet; }}
234 public override int Minargs { get { return 0; }}
235 public override int Maxargs { get { return 1; }}
236 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.NodeSet }; }}
237 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
239 BaseIterator iter = (args.Length == 1) ? ((BaseIterator) args [0]) : new SelfIterator (docContext, xsltContext);
240 if (iter == null || !iter.MoveNext ())
242 return iter.Current.LocalName;
244 public override string Name { get { return "local-name"; }}
246 internal class XPathFunctionNamespaceUri : XPathFunction
248 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
249 public override int Minargs { get { return 0; }}
250 public override int Maxargs { get { return 1; }}
251 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.NodeSet }; }}
253 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
255 BaseIterator iter = (args.Length == 1) ? ((BaseIterator) args [0]) : new SelfIterator (docContext, xsltContext);
256 if (iter == null || !iter.MoveNext ())
258 return iter.Current.NamespaceURI; // TODO: should the namespace be expanded wrt. the given context?
260 public override string Name { get { return "namespace-uri"; }}
262 internal class XPathFunctionName : XPathFunction
264 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
265 public override int Minargs { get { return 0; }}
266 public override int Maxargs { get { return 1; }}
267 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.NodeSet }; }}
269 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
271 throw new NotImplementedException ();
273 public override string Name { get { return "name"; }}
275 internal class XPathFunctionString : XPathFunction
277 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
278 public override int Minargs { get { return 0; }}
279 public override int Maxargs { get { return 1; }}
280 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Any }; }}
281 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
283 return XPathFunctions.ToString (args [0]);
285 public override string Name { get { return "string"; }}
287 internal class XPathFunctionConcat : XPathFunction
289 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
290 public override int Minargs { get { return 2; }}
291 public override int Maxargs { get { return int.MaxValue; }}
292 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String, XPathResultType.String, XPathResultType.String }; }}
293 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
296 foreach (string strArg in args)
300 public override string Name { get { return "concat"; }}
302 internal class XPathFunctionStartsWith : XPathFunction
304 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
305 public override int Minargs { get { return 2; }}
306 public override int Maxargs { get { return 2; }}
307 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String, XPathResultType.String }; }}
308 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
310 string str1 = (string) args [0];
311 string str2 = (string) args [1];
312 return str1.StartsWith (str2);
314 public override string Name { get { return "starts-with"; }}
316 internal class XPathFunctionContains : XPathFunction
318 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
319 public override int Minargs { get { return 2; }}
320 public override int Maxargs { get { return 2; }}
321 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String, XPathResultType.String }; }}
322 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
324 string str1 = (string) args [0];
325 string str2 = (string) args [1];
326 return str1.IndexOf (str2) != -1;
328 public override string Name { get { return "contains"; }}
330 internal class XPathFunctionSubstringBefore : XPathFunction
332 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
333 public override int Minargs { get { return 2; }}
334 public override int Maxargs { get { return 2; }}
335 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String, XPathResultType.String }; }}
336 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
338 string str1 = (string) args [0];
339 string str2 = (string) args [1];
340 int ich = str1.IndexOf (str2);
343 return str1.Substring (0, ich);
345 public override string Name { get { return "substring-before"; }}
347 internal class XPathFunctionSubstringAfter : XPathFunction
349 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
350 public override int Minargs { get { return 2; }}
351 public override int Maxargs { get { return 2; }}
352 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String, XPathResultType.String }; }}
353 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
355 string str1 = (string) args [0];
356 string str2 = (string) args [1];
357 int ich = str1.IndexOf (str2);
360 return str1.Substring (ich + str2.Length);
362 public override string Name { get { return "substring-after"; }}
364 internal class XPathFunctionSubstring : XPathFunction
366 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
367 public override int Minargs { get { return 2; }}
368 public override int Maxargs { get { return int.MaxValue; }}
369 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String, XPathResultType.String, XPathResultType.String }; }}
371 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
373 // TODO: check this, what the hell were they smoking?
374 string str = (string) args [0];
375 double ich = Math.Round ((double) args [1]) - 1;
376 if (Double.IsNaN (ich) || ich >= (double) str.Length)
379 if (args.Length == 2)
383 return str.Substring ((int) ich);
387 double cch = Math.Round ((double) args [2]);
388 if (Double.IsNaN (cch))
390 if (ich < 0.0 || cch < 0.0)
397 double cchMax = (double) str.Length - ich;
400 return str.Substring ((int) ich, (int) cch);
403 public override string Name { get { return "substring"; }}
405 internal class XPathFunctionStringLength : XPathFunction
407 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
408 public override int Minargs { get { return 0; }}
409 public override int Maxargs { get { return 1; }}
410 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String }; }}
411 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
413 return (double) ((string) args [0]).Length;
415 public override string Name { get { return "string-length"; }}
417 internal class XPathFunctionNormalizeSpace : XPathFunction
419 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
420 public override int Minargs { get { return 0; }}
421 public override int Maxargs { get { return 1; }}
422 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String }; }}
424 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
426 throw new NotImplementedException ();
428 public override string Name { get { return "normalize-sace"; }}
430 internal class XPathFunctionTranslate : XPathFunction
432 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
433 public override int Minargs { get { return 3; }}
434 public override int Maxargs { get { return 3; }}
435 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String, XPathResultType.String, XPathResultType.String }; }}
437 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
439 throw new NotImplementedException ();
441 public override string Name { get { return "translate"; }}
443 internal class XPathFunctionBoolean : XPathFunction
445 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
446 public override int Minargs { get { return 1; }}
447 public override int Maxargs { get { return 1; }}
448 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Any }; }}
449 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
451 return XPathFunctions.ToBoolean (args [0]);
453 public override string Name { get { return "boolean"; }}
455 internal class XPathFunctionNot : XPathFunction
457 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
458 public override int Minargs { get { return 1; }}
459 public override int Maxargs { get { return 1; }}
460 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Boolean }; }}
461 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
463 return !((bool) args [0]);
465 public override string Name { get { return "not"; }}
467 internal class XPathFunctionTrue : XPathFunction
469 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
470 public override int Minargs { get { return 0; }}
471 public override int Maxargs { get { return 0; }}
472 public override XPathResultType [] ArgTypes { get { return null; }}
473 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
477 public override string Name { get { return "true"; }}
479 internal class XPathFunctionFalse : XPathFunction
481 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
482 public override int Minargs { get { return 0; }}
483 public override int Maxargs { get { return 0; }}
484 public override XPathResultType [] ArgTypes { get { return null; }}
485 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
489 public override string Name { get { return "false"; }}
491 internal class XPathFunctionLang : XPathFunction
493 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
494 public override int Minargs { get { return 1; }}
495 public override int Maxargs { get { return 1; }}
496 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String }; }}
498 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
500 throw new NotImplementedException ();
502 public override string Name { get { return "lang"; }}
504 internal class XPathFunctionNumber : XPathFunction
506 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
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.Any }; }}
510 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
512 return XPathFunctions.ToNumber (args [0]);
514 public override string Name { get { return "number"; }}
516 internal class XPathFunctionSum : XPathFunction
518 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
519 public override int Minargs { get { return 1; }}
520 public override int Maxargs { get { return 1; }}
521 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.NodeSet }; }}
523 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
525 throw new NotImplementedException ();
527 public override string Name { get { return "sum"; }}
529 internal class XPathFunctionFloor : XPathFunction
531 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
532 public override int Minargs { get { return 1; }}
533 public override int Maxargs { get { return 1; }}
534 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Number }; }}
535 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
537 return Math.Floor ((double) args [0]);
539 public override string Name { get { return "floor"; }}
541 internal class XPathFunctionCeil : XPathFunction
543 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
544 public override int Minargs { get { return 1; }}
545 public override int Maxargs { get { return 1; }}
546 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Number }; }}
547 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
549 return Math.Ceiling ((double) args [0]);
551 public override string Name { get { return "ceil"; }}
553 internal class XPathFunctionRound : XPathFunction
555 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
556 public override int Minargs { get { return 1; }}
557 public override int Maxargs { get { return 1; }}
558 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Number }; }}
559 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
561 double arg = (double) args [0];
562 if (arg < -0.5 || arg > 0)
563 return Math.Floor (arg + 0.5);
564 return Math.Round (arg);
566 public override string Name { get { return "round"; }}