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 private static char [] rgchWhitespace = {' ', '\t', '\r', '\n'};
221 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
222 public override int Minargs { get { return 1; }}
223 public override int Maxargs { get { return 1; }}
224 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Any }; }}
226 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
229 BaseIterator iter = args [0] as BaseIterator;
233 while (!iter.MoveNext ())
234 strArgs += iter.Current.Value + " ";
237 strArgs = XPathFunctions.ToString (args [0]);
238 string [] rgstrArgs = strArgs.Split (rgchWhitespace);
239 ArrayList rgNodes = new ArrayList ();
240 foreach (string strArg in rgstrArgs)
242 if (docContext.MoveToId (strArg))
243 rgNodes.Add (docContext.Clone ());
245 return new ArrayListIterator (iter, rgNodes);
247 public override string Name { get { return "id"; }}
249 internal class XPathFunctionLocalName : XPathFunction
251 public override XPathResultType ReturnType { get { return XPathResultType.NodeSet; }}
252 public override int Minargs { get { return 0; }}
253 public override int Maxargs { get { return 1; }}
254 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.NodeSet }; }}
255 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
257 BaseIterator iter = (args.Length == 1) ? ((BaseIterator) args [0]) : new SelfIterator (docContext, xsltContext);
258 if (iter == null || !iter.MoveNext ())
260 return iter.Current.LocalName;
262 public override string Name { get { return "local-name"; }}
264 internal class XPathFunctionNamespaceUri : XPathFunction
266 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
267 public override int Minargs { get { return 0; }}
268 public override int Maxargs { get { return 1; }}
269 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.NodeSet }; }}
271 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
273 BaseIterator iter = (args.Length == 1) ? ((BaseIterator) args [0]) : new SelfIterator (docContext, xsltContext);
274 if (iter == null || !iter.MoveNext ())
276 return iter.Current.NamespaceURI; // TODO: should the namespace be expanded wrt. the given context?
278 public override string Name { get { return "namespace-uri"; }}
280 internal class XPathFunctionName : XPathFunction
282 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
283 public override int Minargs { get { return 0; }}
284 public override int Maxargs { get { return 1; }}
285 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.NodeSet }; }}
287 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
289 BaseIterator iter = (args.Length == 1) ? ((BaseIterator) args [0]) : new SelfIterator (docContext, xsltContext);
290 if (iter == null || !iter.MoveNext ())
292 return iter.Current.Name;
294 public override string Name { get { return "name"; }}
296 internal class XPathFunctionString : XPathFunction
298 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
299 public override int Minargs { get { return 0; }}
300 public override int Maxargs { get { return 1; }}
301 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Any }; }}
302 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
304 return XPathFunctions.ToString (args [0]);
306 public override string Name { get { return "string"; }}
308 internal class XPathFunctionConcat : XPathFunction
310 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
311 public override int Minargs { get { return 2; }}
312 public override int Maxargs { get { return int.MaxValue; }}
313 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String, XPathResultType.String, XPathResultType.String }; }}
314 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
317 foreach (string strArg in args)
321 public override string Name { get { return "concat"; }}
323 internal class XPathFunctionStartsWith : XPathFunction
325 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
326 public override int Minargs { get { return 2; }}
327 public override int Maxargs { get { return 2; }}
328 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String, XPathResultType.String }; }}
329 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
331 string str1 = (string) args [0];
332 string str2 = (string) args [1];
333 return str1.StartsWith (str2);
335 public override string Name { get { return "starts-with"; }}
337 internal class XPathFunctionContains : XPathFunction
339 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
340 public override int Minargs { get { return 2; }}
341 public override int Maxargs { get { return 2; }}
342 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String, XPathResultType.String }; }}
343 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
345 string str1 = (string) args [0];
346 string str2 = (string) args [1];
347 return str1.IndexOf (str2) != -1;
349 public override string Name { get { return "contains"; }}
351 internal class XPathFunctionSubstringBefore : XPathFunction
353 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
354 public override int Minargs { get { return 2; }}
355 public override int Maxargs { get { return 2; }}
356 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String, XPathResultType.String }; }}
357 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
359 string str1 = (string) args [0];
360 string str2 = (string) args [1];
361 int ich = str1.IndexOf (str2);
364 return str1.Substring (0, ich);
366 public override string Name { get { return "substring-before"; }}
368 internal class XPathFunctionSubstringAfter : XPathFunction
370 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
371 public override int Minargs { get { return 2; }}
372 public override int Maxargs { get { return 2; }}
373 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String, XPathResultType.String }; }}
374 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
376 string str1 = (string) args [0];
377 string str2 = (string) args [1];
378 int ich = str1.IndexOf (str2);
381 return str1.Substring (ich + str2.Length);
383 public override string Name { get { return "substring-after"; }}
385 internal class XPathFunctionSubstring : XPathFunction
387 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
388 public override int Minargs { get { return 2; }}
389 public override int Maxargs { get { return int.MaxValue; }}
390 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String, XPathResultType.String, XPathResultType.String }; }}
392 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
394 // TODO: check this, what the hell were they smoking?
395 string str = (string) args [0];
396 double ich = Math.Round ((double) args [1]) - 1;
397 if (Double.IsNaN (ich) || ich >= (double) str.Length)
400 if (args.Length == 2)
404 return str.Substring ((int) ich);
408 double cch = Math.Round ((double) args [2]);
409 if (Double.IsNaN (cch))
411 if (ich < 0.0 || cch < 0.0)
418 double cchMax = (double) str.Length - ich;
421 return str.Substring ((int) ich, (int) cch);
424 public override string Name { get { return "substring"; }}
426 internal class XPathFunctionStringLength : XPathFunction
428 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
429 public override int Minargs { get { return 0; }}
430 public override int Maxargs { get { return 1; }}
431 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String }; }}
432 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
435 if (args.Length == 1)
436 str = (string) args [0];
438 str = docContext.Value;
439 return (double) str.Length;
441 public override string Name { get { return "string-length"; }}
443 internal class XPathFunctionNormalizeSpace : XPathFunction
445 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
446 public override int Minargs { get { return 0; }}
447 public override int Maxargs { get { return 1; }}
448 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String }; }}
450 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
453 if (args.Length == 1)
454 str = (string) args [0];
456 str = docContext.Value;
457 System.Text.StringBuilder sb = new System.Text.StringBuilder ();
459 foreach (char ch in str)
461 if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
476 return sb.ToString ();
478 public override string Name { get { return "normalize-space"; }}
480 internal class XPathFunctionTranslate : XPathFunction
482 public override XPathResultType ReturnType { get { return XPathResultType.String; }}
483 public override int Minargs { get { return 3; }}
484 public override int Maxargs { get { return 3; }}
485 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.String, XPathResultType.String, XPathResultType.String }; }}
487 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
489 throw new NotImplementedException ();
491 public override string Name { get { return "translate"; }}
493 internal class XPathFunctionBoolean : XPathFunction
495 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
496 public override int Minargs { get { return 1; }}
497 public override int Maxargs { get { return 1; }}
498 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Any }; }}
499 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
501 return XPathFunctions.ToBoolean (args [0]);
503 public override string Name { get { return "boolean"; }}
505 internal class XPathFunctionNot : XPathFunction
507 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
508 public override int Minargs { get { return 1; }}
509 public override int Maxargs { get { return 1; }}
510 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Boolean }; }}
511 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
513 return !((bool) args [0]);
515 public override string Name { get { return "not"; }}
517 internal class XPathFunctionTrue : XPathFunction
519 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
520 public override int Minargs { get { return 0; }}
521 public override int Maxargs { get { return 0; }}
522 public override XPathResultType [] ArgTypes { get { return null; }}
523 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
527 public override string Name { get { return "true"; }}
529 internal class XPathFunctionFalse : XPathFunction
531 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
532 public override int Minargs { get { return 0; }}
533 public override int Maxargs { get { return 0; }}
534 public override XPathResultType [] ArgTypes { get { return null; }}
535 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
539 public override string Name { get { return "false"; }}
541 internal class XPathFunctionLang : XPathFunction
543 public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
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.String }; }}
548 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
550 throw new NotImplementedException ();
552 public override string Name { get { return "lang"; }}
554 internal class XPathFunctionNumber : XPathFunction
556 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
557 public override int Minargs { get { return 0; }}
558 public override int Maxargs { get { return 1; }}
559 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Any }; }}
560 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
562 return XPathFunctions.ToNumber (args [0]);
564 public override string Name { get { return "number"; }}
566 internal class XPathFunctionSum : XPathFunction
568 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
569 public override int Minargs { get { return 1; }}
570 public override int Maxargs { get { return 1; }}
571 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.NodeSet }; }}
573 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
575 throw new NotImplementedException ();
577 public override string Name { get { return "sum"; }}
579 internal class XPathFunctionFloor : XPathFunction
581 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
582 public override int Minargs { get { return 1; }}
583 public override int Maxargs { get { return 1; }}
584 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Number }; }}
585 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
587 return Math.Floor ((double) args [0]);
589 public override string Name { get { return "floor"; }}
591 internal class XPathFunctionCeil : XPathFunction
593 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
594 public override int Minargs { get { return 1; }}
595 public override int Maxargs { get { return 1; }}
596 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Number }; }}
597 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
599 return Math.Ceiling ((double) args [0]);
601 public override string Name { get { return "ceil"; }}
603 internal class XPathFunctionRound : XPathFunction
605 public override XPathResultType ReturnType { get { return XPathResultType.Number; }}
606 public override int Minargs { get { return 1; }}
607 public override int Maxargs { get { return 1; }}
608 public override XPathResultType [] ArgTypes { get { return new XPathResultType [] { XPathResultType.Number }; }}
609 public override object TypesafeInvoke (XsltContext xsltContext, object[] args, XPathNavigator docContext)
611 double arg = (double) args [0];
612 if (arg < -0.5 || arg > 0)
613 return Math.Floor (arg + 0.5);
614 return Math.Round (arg);
616 public override string Name { get { return "round"; }}