1 //------------------------------------------------------------------------------
2 // <copyright file="XsltCompileContext.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
8 namespace System.Xml.Xsl.XsltOld {
9 using Res = System.Xml.Utils.Res;
10 using System.Diagnostics;
12 using System.Globalization;
13 using System.Collections;
14 using System.Xml.XPath;
15 using System.Xml.Xsl.Runtime;
16 using MS.Internal.Xml.XPath;
17 using System.Reflection;
18 using System.Security;
19 using System.Runtime.Versioning;
21 internal class XsltCompileContext : XsltContext {
22 private InputScopeManager manager;
23 private Processor processor;
25 // storage for the functions
26 private static Hashtable s_FunctionTable = CreateFunctionTable();
27 private static IXsltContextFunction s_FuncNodeSet = new FuncNodeSet();
28 const string f_NodeSet = "node-set";
30 internal XsltCompileContext(InputScopeManager manager, Processor processor) : base(/*dummy*/false) {
31 this.manager = manager;
32 this.processor = processor;
35 internal XsltCompileContext() : base(/*dummy*/ false ) {}
37 internal void Recycle() {
42 internal void Reinitialize(InputScopeManager manager, Processor processor) {
43 this.manager = manager;
44 this.processor = processor;
47 public override int CompareDocument(string baseUri, string nextbaseUri) {
48 return String.Compare(baseUri, nextbaseUri, StringComparison.Ordinal);
52 public override string DefaultNamespace {
53 get { return string.Empty; }
56 public override string LookupNamespace(string prefix) {
57 return this.manager.ResolveXPathNamespace(prefix);
60 // --------------------------- XsltContext -------------------
61 // Resolving variables and functions
63 public override IXsltContextVariable ResolveVariable(string prefix, string name) {
64 string namespaceURI = this.LookupNamespace(prefix);
65 XmlQualifiedName qname = new XmlQualifiedName(name, namespaceURI);
66 IXsltContextVariable variable = this.manager.VariableScope.ResolveVariable(qname);
67 if (variable == null) {
68 throw XsltException.Create(Res.Xslt_InvalidVariable, qname.ToString());
73 internal object EvaluateVariable(VariableAction variable) {
74 Object result = this.processor.GetVariableValue(variable);
75 if (result == null && ! variable.IsGlobal) {
76 // This was uninitialized local variable. May be we have sutable global var too?
77 VariableAction global = this.manager.VariableScope.ResolveGlobalVariable(variable.Name);
79 result = this.processor.GetVariableValue(global);
83 throw XsltException.Create(Res.Xslt_InvalidVariable, variable.Name.ToString());
88 // Whitespace stripping support
89 public override bool Whitespace {
90 get { return this.processor.Stylesheet.Whitespace; }
93 public override bool PreserveWhitespace(XPathNavigator node) {
96 return this.processor.Stylesheet.PreserveWhiteSpace(this.processor, node);
99 private MethodInfo FindBestMethod(MethodInfo[] methods, bool ignoreCase, bool publicOnly, string name, XPathResultType[] argTypes) {
100 int length = methods.Length;
102 // restrict search to methods with the same name and requiested protection attribute
103 for(int i = 0; i < length; i ++) {
104 if(string.Compare(name, methods[i].Name, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) == 0) {
105 if(! publicOnly || methods[i].GetBaseDefinition().IsPublic) {
106 methods[free ++] = methods[i];
112 // this is the only place we returning null in this function
115 if(argTypes == null) {
116 // without arg types we can't do more detailed search
119 // restrict search by number of parameters
121 for(int i = 0; i < length; i ++) {
122 if(methods[i].GetParameters().Length == argTypes.Length) {
123 methods[free ++] = methods[i];
128 // 0 -- not method found. We have to return non-null and let it fail with corect exception on call.
129 // 1 -- no reason to continue search anyway.
132 // restrict search by parameters type
134 for(int i = 0; i < length; i ++) {
136 ParameterInfo[] parameters = methods[i].GetParameters();
137 for(int par = 0; par < parameters.Length; par ++) {
138 XPathResultType required = argTypes[par];
139 if(required == XPathResultType.Any) {
140 continue; // Any means we don't know type and can't discriminate by it
142 XPathResultType actual = GetXPathType(parameters[par].ParameterType);
144 actual != required &&
145 actual != XPathResultType.Any // actual arg is object and we can pass everithing here.
152 methods[free ++] = methods[i];
159 private const BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
160 private IXsltContextFunction GetExtentionMethod(string ns, string name, XPathResultType[] argTypes, out object extension) {
161 FuncExtension result = null;
162 extension = this.processor.GetScriptObject(ns);
163 if (extension != null) {
164 MethodInfo method = FindBestMethod(extension.GetType().GetMethods(bindingFlags), /*ignoreCase:*/true, /*publicOnly:*/false, name, argTypes);
165 if (method != null) {
166 result = new FuncExtension(extension, method, /*permissions*/null);
171 extension = this.processor.GetExtensionObject(ns);
172 if(extension != null) {
173 MethodInfo method = FindBestMethod(extension.GetType().GetMethods(bindingFlags), /*ignoreCase:*/false, /*publicOnly:*/true, name, argTypes);
174 if (method != null) {
175 result = new FuncExtension(extension, method, this.processor.permissions);
183 public override IXsltContextFunction ResolveFunction(string prefix, string name, XPathResultType[] argTypes) {
184 IXsltContextFunction func = null;
185 if(prefix.Length == 0) {
186 func = s_FunctionTable[name] as IXsltContextFunction;
188 string ns = this.LookupNamespace(prefix);
189 if (ns == XmlReservedNs.NsMsxsl && name == f_NodeSet) {
190 func = s_FuncNodeSet;
193 func = GetExtentionMethod(ns, name, argTypes, out extension);
194 if(extension == null) {
195 throw XsltException.Create(Res.Xslt_ScriptInvalidPrefix, prefix); // BugBug: It's better to say that method 'name' not found
200 throw XsltException.Create(Res.Xslt_UnknownXsltFunction, name);
202 if (argTypes.Length < func.Minargs || func.Maxargs < argTypes.Length) {
203 throw XsltException.Create(Res.Xslt_WrongNumberArgs, name, argTypes.Length.ToString(CultureInfo.InvariantCulture));
209 // Xslt Function Extensions to XPath
211 [ResourceConsumption(ResourceScope.Machine)]
212 [ResourceExposure(ResourceScope.Machine)]
213 private Uri ComposeUri(string thisUri, string baseUri) {
214 Debug.Assert(thisUri != null && baseUri != null);
215 XmlResolver resolver = processor.Resolver;
217 if (baseUri.Length != 0) {
218 uriBase = resolver.ResolveUri(null, baseUri);
220 return resolver.ResolveUri(uriBase, thisUri);
223 [ResourceConsumption(ResourceScope.Machine)]
224 [ResourceExposure(ResourceScope.Machine)]
225 private XPathNodeIterator Document(object arg0, string baseUri) {
226 if (this.processor.permissions != null) {
227 this.processor.permissions.PermitOnly();
229 XPathNodeIterator it = arg0 as XPathNodeIterator;
231 ArrayList list = new ArrayList();
232 Hashtable documents = new Hashtable();
233 while (it.MoveNext()) {
234 Uri uri = ComposeUri(it.Current.Value, baseUri ?? it.Current.BaseURI);
235 if (! documents.ContainsKey(uri)) {
236 documents.Add(uri, null);
237 list.Add(this.processor.GetNavigator(uri));
240 return new XPathArrayIterator(list);
242 return new XPathSingletonIterator(
243 this.processor.GetNavigator(
244 ComposeUri(XmlConvert.ToXPathString(arg0), baseUri ?? this.manager.Navigator.BaseURI)
250 private Hashtable BuildKeyTable(Key key, XPathNavigator root) {
251 Hashtable keyTable = new Hashtable();
253 string matchStr = this.processor.GetQueryExpression(key.MatchKey);
254 Query matchExpr = this.processor.GetCompiledQuery(key.MatchKey);
255 Query useExpr = this.processor.GetCompiledQuery(key.UseKey);
257 XPathNodeIterator sel = root.SelectDescendants(XPathNodeType.All, /*matchSelf:*/ false);
259 while(sel.MoveNext()) {
260 XPathNavigator node = sel.Current;
261 EvaluateKey(node, matchExpr, matchStr, useExpr, keyTable);
262 if (node.MoveToFirstAttribute()) {
264 EvaluateKey(node, matchExpr, matchStr, useExpr, keyTable);
265 } while (node.MoveToNextAttribute());
272 private static void AddKeyValue(Hashtable keyTable, String key, XPathNavigator value, bool checkDuplicates) {
273 ArrayList list = (ArrayList)keyTable[key];
275 list = new ArrayList();
276 keyTable.Add(key, list);
279 value.ComparePosition((XPathNavigator) list[list.Count - 1]) != XmlNodeOrder.Before,
280 "The way we traversing nodes should garantees node-order"
282 if (checkDuplicates) {
283 // it's posible that this value already was ----osiated with current node
284 // but if this happened the node is last in the list of values.
285 if (value.ComparePosition((XPathNavigator) list[list.Count - 1]) == XmlNodeOrder.Same) {
290 value.ComparePosition((XPathNavigator) list[list.Count - 1]) != XmlNodeOrder.Same,
291 "checkDuplicates == false : We can't have duplicates"
295 list.Add(value.Clone());
298 private static void EvaluateKey(XPathNavigator node, Query matchExpr, string matchStr, Query useExpr, Hashtable keyTable) {
300 if (matchExpr.MatchNode(node) == null) {
303 } catch(XPathException) {
304 throw XsltException.Create(Res.Xslt_InvalidPattern, matchStr);
306 object result = useExpr.Evaluate(new XPathSingletonIterator(node, /*moved:*/true));
307 XPathNodeIterator it = result as XPathNodeIterator;
309 bool checkDuplicates = false;
310 while (it.MoveNext()) {
311 AddKeyValue(keyTable, /*key:*/it.Current.Value, /*value:*/node, checkDuplicates);
312 checkDuplicates = true;
315 String key = XmlConvert.ToXPathString(result);
316 AddKeyValue(keyTable, key, /*value:*/node, /*checkDuplicates:*/ false);
320 private DecimalFormat ResolveFormatName(string formatName) {
321 string ns = string.Empty, local = string.Empty;
322 if (formatName != null) {
324 PrefixQName.ParseQualifiedName(formatName, out prefix, out local);
325 ns = LookupNamespace(prefix);
327 DecimalFormat formatInfo = this.processor.RootAction.GetDecimalFormat(new XmlQualifiedName(local, ns));
328 if (formatInfo == null) {
329 if (formatName != null) {
330 throw XsltException.Create(Res.Xslt_NoDecimalFormat, formatName);
332 formatInfo = new DecimalFormat(new NumberFormatInfo(), '#', '0', ';');
337 // see http://www.w3.org/TR/xslt#function-element-available
338 private bool ElementAvailable(string qname) {
340 PrefixQName.ParseQualifiedName(qname, out prefix, out name);
341 string ns = this.manager.ResolveXmlNamespace(prefix);
342 // msxsl:script - is not an "instruction" so we return false for it.
343 if (ns == XmlReservedNs.NsXslt) {
345 name == "apply-imports" ||
346 name == "apply-templates" ||
347 name == "attribute" ||
348 name == "call-template" ||
354 name == "fallback" ||
355 name == "for-each" ||
359 name == "processing-instruction" ||
361 name == "value-of" ||
368 // see: http://www.w3.org/TR/xslt#function-function-available
369 private bool FunctionAvailable(string qname) {
371 PrefixQName.ParseQualifiedName(qname, out prefix, out name);
372 string ns = LookupNamespace(prefix);
374 if (ns == XmlReservedNs.NsMsxsl) {
375 return name == f_NodeSet;
376 } else if(ns.Length == 0) {
378 // It'll be better to get this information from XPath
380 name == "position" ||
382 name == "namespace-uri" ||
383 name == "local-name" ||
388 name == "starts-with" ||
389 name == "contains" ||
390 name == "substring-before" ||
391 name == "substring-after" ||
392 name == "substring" ||
393 name == "string-length" ||
394 name == "normalize-space" ||
395 name == "translate" ||
407 (s_FunctionTable[name] != null && name != "unparsed-entity-uri")
410 // Is this script or extention function?
412 return GetExtentionMethod(ns, name, /*argTypes*/null, out extension) != null;
416 private XPathNodeIterator Current() {
417 XPathNavigator nav = this.processor.Current;
419 return new XPathSingletonIterator(nav.Clone());
421 return XPathEmptyIterator.Instance;
424 private String SystemProperty(string qname) {
425 String result = string.Empty;
429 PrefixQName.ParseQualifiedName(qname, out prefix, out local);
431 // verify the prefix corresponds to the Xslt namespace
432 string urn = LookupNamespace(prefix);
434 if (urn == XmlReservedNs.NsXslt) {
435 if(local == "version") {
437 } else if(local == "vendor") {
438 result = "Microsoft";
439 } else if(local == "vendor-url") {
440 result = "http://www.microsoft.com";
443 if(urn == null && prefix != null) {
444 // if prefix exist it has to be mapped to namespace.
445 // Can it be "" here ?
446 throw XsltException.Create(Res.Xslt_InvalidPrefix, prefix);
454 public static XPathResultType GetXPathType(Type type) {
455 switch(Type.GetTypeCode(type)) {
456 case TypeCode.String :
457 return XPathResultType.String;
458 case TypeCode.Boolean :
459 return XPathResultType.Boolean;
460 case TypeCode.Object :
462 typeof(XPathNavigator ).IsAssignableFrom(type) ||
463 typeof(IXPathNavigable).IsAssignableFrom(type)
465 return XPathResultType.Navigator;
467 if (typeof(XPathNodeIterator).IsAssignableFrom(type)) {
468 return XPathResultType.NodeSet;
470 // Microsoft: It be better to check that type is realy object and otherwise return XPathResultType.Error
471 return XPathResultType.Any;
472 case TypeCode.DateTime :
473 return XPathResultType.Error;
474 default: /* all numeric types */
475 return XPathResultType.Number;
479 // ---------------- Xslt Function Implementations -------------------
481 static Hashtable CreateFunctionTable() {
482 Hashtable ft = new Hashtable(10); {
483 ft["current" ] = new FuncCurrent ();
484 ft["unparsed-entity-uri"] = new FuncUnEntityUri ();
485 ft["generate-id" ] = new FuncGenerateId ();
486 ft["system-property" ] = new FuncSystemProp ();
487 ft["element-available" ] = new FuncElementAvailable ();
488 ft["function-available" ] = new FuncFunctionAvailable();
489 ft["document" ] = new FuncDocument ();
490 ft["key" ] = new FuncKey ();
491 ft["format-number" ] = new FuncFormatNumber ();
496 // + IXsltContextFunction
497 // + XsltFunctionImpl func. name, min/max args, return type args types
498 // FuncCurrent "current" 0 0 XPathResultType.NodeSet { }
499 // FuncUnEntityUri "unparsed-entity-uri" 1 1 XPathResultType.String { XPathResultType.String }
500 // FuncGenerateId "generate-id" 0 1 XPathResultType.String { XPathResultType.NodeSet }
501 // FuncSystemProp "system-property" 1 1 XPathResultType.String { XPathResultType.String }
502 // FuncElementAvailable "element-available" 1 1 XPathResultType.Boolean { XPathResultType.String }
503 // FuncFunctionAvailable "function-available" 1 1 XPathResultType.Boolean { XPathResultType.String }
504 // FuncDocument "document" 1 2 XPathResultType.NodeSet { XPathResultType.Any , XPathResultType.NodeSet }
505 // FuncKey "key" 2 2 XPathResultType.NodeSet { XPathResultType.String , XPathResultType.Any }
506 // FuncFormatNumber "format-number" 2 3 XPathResultType.String { XPathResultType.Number , XPathResultType.String, XPathResultType.String }
507 // FuncNodeSet "msxsl:node-set" 1 1 XPathResultType.NodeSet { XPathResultType.Navigator }
510 private abstract class XsltFunctionImpl : IXsltContextFunction {
513 private XPathResultType returnType;
514 private XPathResultType[] argTypes;
516 public XsltFunctionImpl() {}
517 public XsltFunctionImpl(int minArgs, int maxArgs, XPathResultType returnType, XPathResultType[] argTypes) {
518 this.Init(minArgs, maxArgs, returnType, argTypes);
520 protected void Init(int minArgs, int maxArgs, XPathResultType returnType, XPathResultType[] argTypes) {
521 this.minargs = minArgs;
522 this.maxargs = maxArgs;
523 this.returnType = returnType;
524 this.argTypes = argTypes;
527 public int Minargs { get { return this.minargs; } }
528 public int Maxargs { get { return this.maxargs; } }
529 public XPathResultType ReturnType { get { return this.returnType; } }
530 public XPathResultType[] ArgTypes { get { return this.argTypes; } }
531 public abstract object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext);
533 // static helper methods:
534 public static XPathNodeIterator ToIterator(object argument) {
535 XPathNodeIterator it = argument as XPathNodeIterator;
537 throw XsltException.Create(Res.Xslt_NoNodeSetConversion);
542 public static XPathNavigator ToNavigator(object argument) {
543 XPathNavigator nav = argument as XPathNavigator;
545 throw XsltException.Create(Res.Xslt_NoNavigatorConversion);
550 private static string IteratorToString(XPathNodeIterator it) {
551 Debug.Assert(it != null);
553 return it.Current.Value;
558 public static string ToString(object argument) {
559 XPathNodeIterator it = argument as XPathNodeIterator;
561 return IteratorToString(it);
563 return XmlConvert.ToXPathString(argument);
567 public static bool ToBoolean(object argument) {
568 XPathNodeIterator it = argument as XPathNodeIterator;
570 return Convert.ToBoolean(IteratorToString(it), CultureInfo.InvariantCulture);
572 XPathNavigator nav = argument as XPathNavigator;
574 return Convert.ToBoolean(nav.ToString(), CultureInfo.InvariantCulture);
576 return Convert.ToBoolean(argument, CultureInfo.InvariantCulture);
579 public static double ToNumber(object argument) {
580 XPathNodeIterator it = argument as XPathNodeIterator;
582 return XmlConvert.ToXPathDouble(IteratorToString(it));
584 XPathNavigator nav = argument as XPathNavigator;
586 return XmlConvert.ToXPathDouble(nav.ToString());
588 return XmlConvert.ToXPathDouble(argument);
591 private static object ToNumeric(object argument, TypeCode typeCode) {
592 return Convert.ChangeType(ToNumber(argument), typeCode, CultureInfo.InvariantCulture);
595 public static object ConvertToXPathType(object val, XPathResultType xt, TypeCode typeCode) {
597 case XPathResultType.String :
598 // Unfortunetely XPathResultType.String == XPathResultType.Navigator (This is wrong but cant be changed in Everett)
599 // Fortunetely we have typeCode hare so let's discriminate by typeCode
600 if (typeCode == TypeCode.String) {
601 return ToString(val);
603 return ToNavigator(val);
605 case XPathResultType.Number : return ToNumeric(val, typeCode);
606 case XPathResultType.Boolean : return ToBoolean(val);
607 case XPathResultType.NodeSet : return ToIterator(val);
608 // case XPathResultType.Navigator : return ToNavigator(val);
609 case XPathResultType.Any :
610 case XPathResultType.Error :
613 Debug.Assert(false, "unexpected XPath type");
619 private class FuncCurrent : XsltFunctionImpl {
620 public FuncCurrent() : base(0, 0, XPathResultType.NodeSet, new XPathResultType[] {}) {}
621 public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
622 return ((XsltCompileContext)xsltContext).Current();
626 private class FuncUnEntityUri : XsltFunctionImpl {
627 public FuncUnEntityUri() : base(1, 1, XPathResultType.String , new XPathResultType[] { XPathResultType.String }) {}
628 public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
629 throw XsltException.Create(Res.Xslt_UnsuppFunction, "unparsed-entity-uri");
633 private class FuncGenerateId : XsltFunctionImpl {
634 public FuncGenerateId() : base(0, 1, XPathResultType.String , new XPathResultType[] { XPathResultType.NodeSet }) {}
635 public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
636 if(args.Length > 0) {
637 XPathNodeIterator it = ToIterator(args[0]);
639 return it.Current.UniqueId;
641 // if empty nodeset, return empty string, otherwise return generated id
645 return docContext.UniqueId;
650 private class FuncSystemProp : XsltFunctionImpl {
651 public FuncSystemProp() : base(1, 1, XPathResultType.String , new XPathResultType[] { XPathResultType.String }) {}
652 public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
653 return ((XsltCompileContext)xsltContext).SystemProperty(ToString(args[0]));
657 // see http://www.w3.org/TR/xslt#function-element-available
658 private class FuncElementAvailable : XsltFunctionImpl {
659 public FuncElementAvailable() : base(1, 1, XPathResultType.Boolean, new XPathResultType[] { XPathResultType.String }) {}
660 public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
661 return ((XsltCompileContext)xsltContext).ElementAvailable(ToString(args[0]));
665 // see: http://www.w3.org/TR/xslt#function-function-available
666 private class FuncFunctionAvailable : XsltFunctionImpl {
667 public FuncFunctionAvailable() : base(1, 1, XPathResultType.Boolean, new XPathResultType[] { XPathResultType.String }) {}
668 public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
669 return ((XsltCompileContext)xsltContext).FunctionAvailable(ToString(args[0]));
673 private class FuncDocument : XsltFunctionImpl {
674 public FuncDocument() : base(1, 2, XPathResultType.NodeSet, new XPathResultType[] { XPathResultType.Any , XPathResultType.NodeSet}) {}
676 // SxS: This method uses resource names read from source document and does not expose any resources to the caller.
677 // It's OK to suppress the SxS warning.
678 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
679 [ResourceExposure(ResourceScope.None)]
680 public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
681 string baseUri = null;
682 if (args.Length == 2) {
683 XPathNodeIterator it = ToIterator(args[1]);
685 baseUri = it.Current.BaseURI;
687 // http://www.w3.org/1999/11/REC-xslt-19991116-errata (E14):
688 // It is an error if the second argument node-set is empty and the URI reference is relative; the XSLT processor may signal the error;
689 // if it does not signal an error, it must recover by returning an empty node-set.
690 baseUri = string.Empty; // call to Document will fail if args[0] is reletive.
694 return ((XsltCompileContext)xsltContext).Document(args[0], baseUri);
696 catch (Exception e) {
697 if (!XmlException.IsCatchableException(e)) {
700 return XPathEmptyIterator.Instance;
705 private class FuncKey : XsltFunctionImpl {
706 public FuncKey() : base(2, 2, XPathResultType.NodeSet, new XPathResultType[] { XPathResultType.String , XPathResultType.Any }) {}
707 public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
708 XsltCompileContext xsltCompileContext = (XsltCompileContext) xsltContext;
710 string local, prefix;
711 PrefixQName.ParseQualifiedName(ToString(args[0]), out prefix, out local);
712 string ns = xsltContext.LookupNamespace(prefix);
713 XmlQualifiedName keyName = new XmlQualifiedName(local, ns);
715 XPathNavigator root = docContext.Clone();
718 ArrayList resultCollection = null;
720 foreach (Key key in xsltCompileContext.processor.KeyList) {
721 if (key.Name == keyName) {
722 Hashtable keyTable = key.GetKeys(root);
723 if (keyTable == null) {
724 keyTable = xsltCompileContext.BuildKeyTable(key, root);
725 key.AddKey(root, keyTable);
728 XPathNodeIterator it = args[1] as XPathNodeIterator;
731 while (it.MoveNext()) {
732 resultCollection = AddToList(resultCollection, (ArrayList) keyTable[it.Current.Value]);
735 resultCollection = AddToList(resultCollection, (ArrayList) keyTable[ToString(args[1])]);
739 if (resultCollection == null) {
740 return XPathEmptyIterator.Instance;
741 } else if (resultCollection[0] is XPathNavigator) {
742 return new XPathArrayIterator(resultCollection);
744 return new XPathMultyIterator(resultCollection);
748 static ArrayList AddToList(ArrayList resultCollection, ArrayList newList) {
749 if (newList == null) {
750 return resultCollection;
752 if (resultCollection == null) {
755 Debug.Assert(resultCollection.Count != 0);
756 Debug.Assert(newList.Count != 0);
757 if (! (resultCollection[0] is ArrayList)) {
758 // Transform resultCollection from ArrayList(XPathNavigator) to ArrayList(ArrayList(XPathNavigator))
759 Debug.Assert(resultCollection[0] is XPathNavigator);
760 ArrayList firstList = resultCollection;
761 resultCollection = new ArrayList();
762 resultCollection.Add(firstList);
764 resultCollection.Add(newList);
765 return resultCollection;
769 private class FuncFormatNumber : XsltFunctionImpl {
770 public FuncFormatNumber() : base(2, 3, XPathResultType.String , new XPathResultType[] { XPathResultType.Number , XPathResultType.String, XPathResultType.String }) {}
771 public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
772 DecimalFormat formatInfo = ((XsltCompileContext)xsltContext).ResolveFormatName(args.Length == 3 ? ToString(args[2]) : null);
773 return DecimalFormatter.Format(ToNumber(args[0]), ToString(args[1]), formatInfo);
777 private class FuncNodeSet : XsltFunctionImpl {
778 public FuncNodeSet() : base(1, 1, XPathResultType.NodeSet, new XPathResultType[] { XPathResultType.Navigator }) {}
779 public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
780 return new XPathSingletonIterator(ToNavigator(args[0]));
784 private class FuncExtension : XsltFunctionImpl {
785 private object extension;
786 private MethodInfo method;
787 private TypeCode[] typeCodes;
788 private PermissionSet permissions;
790 public FuncExtension(object extension, MethodInfo method, PermissionSet permissions) {
791 Debug.Assert(extension != null);
792 Debug.Assert(method != null);
793 this.extension = extension;
794 this.method = method;
795 this.permissions = permissions;
796 XPathResultType returnType = GetXPathType(method.ReturnType);
798 ParameterInfo[] parameters = method.GetParameters();
799 int minArgs = parameters.Length;
800 int maxArgs = parameters.Length;
801 this.typeCodes = new TypeCode[parameters.Length];
802 XPathResultType[] argTypes = new XPathResultType[parameters.Length];
803 bool optionalParams = true; // we allow only last params be optional. Set false on the first non optional.
804 for (int i = parameters.Length - 1; 0 <= i; i --) { // Revers order is essential: counting optional parameters
805 typeCodes[i] = Type.GetTypeCode(parameters[i].ParameterType);
806 argTypes[i] = GetXPathType(parameters[i].ParameterType);
808 if(parameters[i].IsOptional) {
811 optionalParams = false;
815 base.Init(minArgs, maxArgs, returnType, argTypes);
818 public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
819 Debug.Assert(args.Length <= this.Minargs, "We cheking this on resolve time");
820 for(int i = args.Length -1; 0 <= i; i --) {
821 args[i] = ConvertToXPathType(args[i], this.ArgTypes[i], this.typeCodes[i]);
823 if (this.permissions != null) {
824 this.permissions.PermitOnly();
826 return method.Invoke(extension, args);