The Labs \ Source Viewer \ SSCLI \ System.Xml.Xsl.XsltOld \ FuncSystemProp

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XsltCompileContext.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. namespace System.Xml.Xsl.XsltOld
  16. {
  17.     using Res = System.Xml.Utils.Res;
  18.     using System.Diagnostics;
  19.     using System.IO;
  20.     using System.Globalization;
  21.     using System.Collections;
  22.     using System.Xml.XPath;
  23.     using System.Xml.Xsl.Runtime;
  24.     using MS.Internal.Xml.XPath;
  25.     using System.Reflection;
  26.     using System.Security;
  27.    
  28.     internal class XsltCompileContext : XsltContext
  29.     {
  30.         private InputScopeManager manager;
  31.         private Processor processor;
  32.        
  33.         // storage for the functions
  34.         private static Hashtable s_FunctionTable = CreateFunctionTable();
  35.         private static IXsltContextFunction s_FuncNodeSet = new FuncNodeSet();
  36.         const string f_NodeSet = "node-set";
  37.        
  38.         internal XsltCompileContext(InputScopeManager manager, Processor processor) : base(false)/*dummy*/
  39.         {
  40.             this.manager = manager;
  41.             this.processor = processor;
  42.         }
  43.        
  44.         internal XsltCompileContext() : base(false)/*dummy*/
  45.         {
  46.         }
  47.        
  48.         internal void Recycle()
  49.         {
  50.             manager = null;
  51.             processor = null;
  52.         }
  53.        
  54.         internal void Reinitialize(InputScopeManager manager, Processor processor)
  55.         {
  56.             this.manager = manager;
  57.             this.processor = processor;
  58.         }
  59.        
  60.         public override int CompareDocument(string baseUri, string nextbaseUri)
  61.         {
  62.             return String.Compare(baseUri, nextbaseUri, StringComparison.Ordinal);
  63.         }
  64.        
  65.         // Namespace support
  66.         public override string DefaultNamespace {
  67.             get { return string.Empty; }
  68.         }
  69.        
  70.         public override string LookupNamespace(string prefix)
  71.         {
  72.             return this.manager.ResolveXPathNamespace(prefix);
  73.         }
  74.        
  75.         // --------------------------- XsltContext -------------------
  76.         // Resolving variables and functions
  77.        
  78.         public override IXsltContextVariable ResolveVariable(string prefix, string name)
  79.         {
  80.             string namespaceURI = this.LookupNamespace(prefix);
  81.             XmlQualifiedName qname = new XmlQualifiedName(name, namespaceURI);
  82.             IXsltContextVariable variable = this.manager.VariableScope.ResolveVariable(qname);
  83.             if (variable == null) {
  84.                 throw XsltException.Create(Res.Xslt_InvalidVariable, qname.ToString());
  85.             }
  86.             return variable;
  87.         }
  88.        
  89.         internal object EvaluateVariable(VariableAction variable)
  90.         {
  91.             object result = this.processor.GetVariableValue(variable);
  92.             if (result == null && !variable.IsGlobal) {
  93.                 // This was uninitialized local variable. May be we have sutable global var too?
  94.                 VariableAction global = this.manager.VariableScope.ResolveGlobalVariable(variable.Name);
  95.                 if (global != null) {
  96.                     result = this.processor.GetVariableValue(global);
  97.                 }
  98.             }
  99.             if (result == null) {
  100.                 throw XsltException.Create(Res.Xslt_InvalidVariable, variable.Name.ToString());
  101.             }
  102.             return result;
  103.         }
  104.        
  105.         // Whitespace stripping support
  106.         public override bool Whitespace {
  107.             get { return this.processor.Stylesheet.Whitespace; }
  108.         }
  109.        
  110.         public override bool PreserveWhitespace(XPathNavigator node)
  111.         {
  112.             node = node.Clone();
  113.             node.MoveToParent();
  114.             return this.processor.Stylesheet.PreserveWhiteSpace(this.processor, node);
  115.         }
  116.        
  117.         private MethodInfo FindBestMethod(MethodInfo[] methods, bool ignoreCase, bool publicOnly, string name, XPathResultType[] argTypes)
  118.         {
  119.             int length = methods.Length;
  120.             int free = 0;
  121.             // restrict search to methods with the same name and requiested protection attribute
  122.             for (int i = 0; i < length; i++) {
  123.                 if (string.Compare(name, methods[i].Name, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) == 0) {
  124.                     if (!publicOnly || methods[i].GetBaseDefinition().IsPublic) {
  125.                         methods[free++] = methods[i];
  126.                     }
  127.                 }
  128.             }
  129.             length = free;
  130.             if (length == 0) {
  131.                 // this is the only place we returning null in this function
  132.                 return null;
  133.             }
  134.             if (argTypes == null) {
  135.                 // without arg types we can't do more detailed search
  136.                 return methods[0];
  137.             }
  138.             // restrict search by number of parameters
  139.             free = 0;
  140.             for (int i = 0; i < length; i++) {
  141.                 if (methods[i].GetParameters().Length == argTypes.Length) {
  142.                     methods[free++] = methods[i];
  143.                 }
  144.             }
  145.             length = free;
  146.             if (length <= 1) {
  147.                 // 0 -- not method found. We have to return non-null and let it fail with corect exception on call.
  148.                 // 1 -- no reason to continue search anyway.
  149.                 return methods[0];
  150.             }
  151.             // restrict search by parameters type
  152.             free = 0;
  153.             for (int i = 0; i < length; i++) {
  154.                 bool match = true;
  155.                 ParameterInfo[] parameters = methods[i].GetParameters();
  156.                 for (int par = 0; par < parameters.Length; par++) {
  157.                     XPathResultType required = argTypes[par];
  158.                     if (required == XPathResultType.Any) {
  159.                         continue;
  160.                         // Any means we don't know type and can't discriminate by it
  161.                     }
  162.                     XPathResultType actual = GetXPathType(parameters[par].ParameterType);
  163.                     // actual arg is object and we can pass everithing here.
  164.                     if (actual != required && actual != XPathResultType.Any) {
  165.                         match = false;
  166.                         break;
  167.                     }
  168.                 }
  169.                 if (match) {
  170.                     methods[free++] = methods[i];
  171.                 }
  172.             }
  173.             length = free;
  174.             return methods[0];
  175.         }
  176.        
  177.         private const BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
  178.         private IXsltContextFunction GetExtentionMethod(string ns, string name, XPathResultType[] argTypes, out object extension)
  179.         {
  180.             FuncExtension result = null;
  181.             extension = this.processor.GetScriptObject(ns);
  182.             if (extension != null) {
  183.                     /*ignoreCase:*/                    /*publicOnly:*/                MethodInfo method = FindBestMethod(extension.GetType().GetMethods(bindingFlags), true, false, name, argTypes);
  184.                 if (method != null) {
  185.                         /*permissions*/                    result = new FuncExtension(extension, method, null);
  186.                 }
  187.                 return result;
  188.             }
  189.            
  190.             extension = this.processor.GetExtensionObject(ns);
  191.             if (extension != null) {
  192.                     /*ignoreCase:*/                    /*publicOnly:*/                MethodInfo method = FindBestMethod(extension.GetType().GetMethods(bindingFlags), false, true, name, argTypes);
  193.                 if (method != null) {
  194.                     result = new FuncExtension(extension, method, this.processor.permissions);
  195.                 }
  196.                 return result;
  197.             }
  198.            
  199.             return null;
  200.         }
  201.        
  202.         public override IXsltContextFunction ResolveFunction(string prefix, string name, XPathResultType[] argTypes)
  203.         {
  204.             IXsltContextFunction func = null;
  205.             if (prefix.Length == 0) {
  206.                 func = s_FunctionTable[name] as IXsltContextFunction;
  207.             }
  208.             else {
  209.                 string ns = this.LookupNamespace(prefix);
  210.                 if (ns == Keywords.s_MsXsltNamespace && name == f_NodeSet) {
  211.                     func = s_FuncNodeSet;
  212.                 }
  213.                 else {
  214.                     object extension;
  215.                     func = GetExtentionMethod(ns, name, argTypes, out extension);
  216.                     if (extension == null) {
  217.                         throw XsltException.Create(Res.Xslt_ScriptInvalidPrefix, prefix);
  218.                     }
  219.                 }
  220.             }
  221.             if (func == null) {
  222.                 throw XsltException.Create(Res.Xslt_UnknownXsltFunction, name);
  223.             }
  224.             if (argTypes.Length < func.Minargs || func.Maxargs < argTypes.Length) {
  225.                 throw XsltException.Create(Res.Xslt_WrongNumberArgs, name, argTypes.Length.ToString(CultureInfo.InvariantCulture));
  226.             }
  227.             return func;
  228.         }
  229.        
  230.         //
  231.         // Xslt Function Extensions to XPath
  232.         //
  233.         private Uri ComposeUri(string thisUri, string baseUri)
  234.         {
  235.             Debug.Assert(thisUri != null && baseUri != null);
  236.             XmlResolver resolver = processor.Resolver;
  237.             Uri uriBase = null;
  238.             if (baseUri.Length != 0) {
  239.                 uriBase = resolver.ResolveUri(null, baseUri);
  240.             }
  241.             return resolver.ResolveUri(uriBase, thisUri);
  242.         }
  243.        
  244.         private XPathNodeIterator Document(object arg0, string baseUri)
  245.         {
  246.             if (this.processor.permissions != null) {
  247.                 this.processor.permissions.PermitOnly();
  248.             }
  249.             XPathNodeIterator it = arg0 as XPathNodeIterator;
  250.             if (it != null) {
  251.                 ArrayList list = new ArrayList();
  252.                 Hashtable documents = new Hashtable();
  253.                 while (it.MoveNext()) {
  254.                     Uri uri = ComposeUri(it.Current.Value, baseUri ?? it.Current.BaseURI);
  255.                     if (!documents.ContainsKey(uri)) {
  256.                         documents.Add(uri, null);
  257.                         list.Add(this.processor.GetNavigator(uri));
  258.                     }
  259.                 }
  260.                 return new XPathArrayIterator(list);
  261.             }
  262.             else {
  263.                 return new XPathSingletonIterator(this.processor.GetNavigator(ComposeUri(XmlConvert.ToXPathString(arg0), baseUri ?? this.manager.Navigator.BaseURI)));
  264.             }
  265.         }
  266.        
  267.         private Hashtable BuildKeyTable(Key key, XPathNavigator root)
  268.         {
  269.             Hashtable keyTable = new Hashtable();
  270.            
  271.             string matchStr = this.processor.GetQueryExpression(key.MatchKey);
  272.             Query matchExpr = this.processor.GetCompiledQuery(key.MatchKey);
  273.             Query useExpr = this.processor.GetCompiledQuery(key.UseKey);
  274.            
  275.                 /*matchSelf:*/            XPathNodeIterator sel = root.SelectDescendants(XPathNodeType.All, false);
  276.            
  277.             while (sel.MoveNext()) {
  278.                 XPathNavigator node = sel.Current;
  279.                 EvaluateKey(node, matchExpr, matchStr, useExpr, keyTable);
  280.                 if (node.MoveToFirstAttribute()) {
  281.                     do {
  282.                         EvaluateKey(node, matchExpr, matchStr, useExpr, keyTable);
  283.                     }
  284.                     while (node.MoveToNextAttribute());
  285.                     node.MoveToParent();
  286.                 }
  287.             }
  288.             return keyTable;
  289.         }
  290.        
  291.         private static void AddKeyValue(Hashtable keyTable, string key, XPathNavigator value, bool checkDuplicates)
  292.         {
  293.             ArrayList list = (ArrayList)keyTable[key];
  294.             if (list == null) {
  295.                 list = new ArrayList();
  296.                 keyTable.Add(key, list);
  297.             }
  298.             else {
  299.                 Debug.Assert(value.ComparePosition((XPathNavigator)list[list.Count - 1]) != XmlNodeOrder.Before, "The way we traversing nodes should garantees node-order");
  300.                 if (checkDuplicates) {
  301.                     // it's posible that this value already was assosiated with current node
  302.                     // but if this happened the node is last in the list of values.
  303.                     if (value.ComparePosition((XPathNavigator)list[list.Count - 1]) == XmlNodeOrder.Same) {
  304.                         return;
  305.                     }
  306.                 }
  307.                 else {
  308.                     Debug.Assert(value.ComparePosition((XPathNavigator)list[list.Count - 1]) != XmlNodeOrder.Same, "checkDuplicates == false : We can't have duplicates");
  309.                 }
  310.             }
  311.             list.Add(value.Clone());
  312.         }
  313.        
  314.         private static void EvaluateKey(XPathNavigator node, Query matchExpr, string matchStr, Query useExpr, Hashtable keyTable)
  315.         {
  316.             try {
  317.                 if (matchExpr.MatchNode(node) == null) {
  318.                     return;
  319.                 }
  320.             }
  321.             catch (XPathException) {
  322.                 throw XsltException.Create(Res.Xslt_InvalidPattern, matchStr);
  323.             }
  324.                 /*moved:*/            object result = useExpr.Evaluate(new XPathSingletonIterator(node, true));
  325.             XPathNodeIterator it = result as XPathNodeIterator;
  326.             if (it != null) {
  327.                 bool checkDuplicates = false;
  328.                 while (it.MoveNext()) {
  329.                         /*key:*/                        /*value:*/                    AddKeyValue(keyTable, it.Current.Value, node, checkDuplicates);
  330.                     checkDuplicates = true;
  331.                 }
  332.             }
  333.             else {
  334.                 string key = XmlConvert.ToXPathString(result);
  335.                     /*value:*/                    /*checkDuplicates:*/                AddKeyValue(keyTable, key, node, false);
  336.             }
  337.         }
  338.        
  339.         private DecimalFormat ResolveFormatName(string formatName)
  340.         {
  341.             string ns = string.Empty;
  342.             string local = string.Empty;
  343.             if (formatName != null) {
  344.                 string prefix;
  345.                 PrefixQName.ParseQualifiedName(formatName, out prefix, out local);
  346.                 ns = LookupNamespace(prefix);
  347.             }
  348.             DecimalFormat formatInfo = this.processor.RootAction.GetDecimalFormat(new XmlQualifiedName(local, ns));
  349.             if (formatInfo == null) {
  350.                 if (formatName != null) {
  351.                     throw XsltException.Create(Res.Xslt_NoDecimalFormat, formatName);
  352.                 }
  353.                 formatInfo = new DecimalFormat(new NumberFormatInfo(), '#', '0', ';');
  354.             }
  355.             return formatInfo;
  356.         }
  357.        
  358.         // see http://www.w3.org/TR/xslt#function-element-available
  359.         private bool ElementAvailable(string qname)
  360.         {
  361.             string name;
  362.             string prefix;
  363.             PrefixQName.ParseQualifiedName(qname, out prefix, out name);
  364.             string ns = this.manager.ResolveXmlNamespace(prefix);
  365.             // msxsl:script - is not an "instruction" so we return false for it.
  366.             if (ns == Keywords.s_XsltNamespace) {
  367.                 return (name == Keywords.s_ApplyImports || name == Keywords.s_ApplyTemplates || name == Keywords.s_Attribute || name == Keywords.s_CallTemplate || name == Keywords.s_Choose || name == Keywords.s_Comment || name == Keywords.s_Copy || name == Keywords.s_CopyOf || name == Keywords.s_Element || name == Keywords.s_Fallback || name == Keywords.s_ForEach || name == Keywords.s_If || name == Keywords.s_Message || name == Keywords.s_Number || name == Keywords.s_ProcessingInstruction || name == Keywords.s_Text || name == Keywords.s_ValueOf || name == Keywords.s_Variable);
  368.             }
  369.             return false;
  370.         }
  371.        
  372.         // see: http://www.w3.org/TR/xslt#function-function-available
  373.         private bool FunctionAvailable(string qname)
  374.         {
  375.             string name;
  376.             string prefix;
  377.             PrefixQName.ParseQualifiedName(qname, out prefix, out name);
  378.             string ns = LookupNamespace(prefix);
  379.            
  380.             if (ns == Keywords.s_MsXsltNamespace) {
  381.                 return name == f_NodeSet;
  382.             }
  383.             else if (ns.Length == 0) {
  384.                     // It'll be better to get this information from XPath
  385.                     // XSLT functions:
  386.                 return (name == "last" || name == "position" || name == "name" || name == "namespace-uri" || name == "local-name" || name == "count" || name == "id" || name == "string" || name == "concat" || name == "starts-with" || name == "contains" || name == "substring-before" || name == "substring-after" || name == "substring" || name == "string-length" || name == "normalize-space" || name == "translate" || name == "boolean" || name == "not" || name == "true" || name == "false" || name == "lang" || name == "number" || name == "sum" || name == "floor" || name == "ceiling" || name == "round" || (s_FunctionTable[name] != null && name != "unparsed-entity-uri"));
  387.             }
  388.             else {
  389.                 // Is this script or extention function?
  390.                 object extension;
  391.                     /*argTypes*/                return GetExtentionMethod(ns, name, null, out extension) != null;
  392.             }
  393.         }
  394.        
  395.         private XPathNodeIterator Current()
  396.         {
  397.             XPathNavigator nav = this.processor.Current;
  398.             if (nav != null) {
  399.                 return new XPathSingletonIterator(nav.Clone());
  400.             }
  401.             return XPathEmptyIterator.Instance;
  402.         }
  403.        
  404.         private string SystemProperty(string qname)
  405.         {
  406.             string result = string.Empty;
  407.            
  408.             string prefix;
  409.             string local;
  410.             PrefixQName.ParseQualifiedName(qname, out prefix, out local);
  411.            
  412.             // verify the prefix corresponds to the Xslt namespace
  413.             string urn = LookupNamespace(prefix);
  414.            
  415.             if (urn == Keywords.s_XsltNamespace) {
  416.                 if (local == "version") {
  417.                     result = "1";
  418.                 }
  419.                 else if (local == "vendor") {
  420.                     result = "Microsoft";
  421.                 }
  422.                 else if (local == "vendor-url") {
  423.                     result = "http://www.microsoft.com";
  424.                 }
  425.             }
  426.             else {
  427.                 if (urn == null && prefix != null) {
  428.                     // if prefix exist it has to be mapped to namespace.
  429.                     // Can it be "" here ?
  430.                     throw XsltException.Create(Res.Xslt_InvalidPrefix, prefix);
  431.                 }
  432.                 return string.Empty;
  433.             }
  434.            
  435.             return result;
  436.         }
  437.        
  438.         public static XPathResultType GetXPathType(Type type)
  439.         {
  440.             switch (Type.GetTypeCode(type)) {
  441.                 case TypeCode.String:
  442.                     return XPathResultType.String;
  443.                 case TypeCode.Boolean:
  444.                     return XPathResultType.Boolean;
  445.                 case TypeCode.Object:
  446.                     if (typeof(XPathNavigator).IsAssignableFrom(type) || typeof(IXPathNavigable).IsAssignableFrom(type)) {
  447.                         return XPathResultType.Navigator;
  448.                     }
  449.                     if (typeof(XPathNodeIterator).IsAssignableFrom(type)) {
  450.                         return XPathResultType.NodeSet;
  451.                     }
  452.                     return XPathResultType.Any;
  453.                 case TypeCode.DateTime:
  454.                     return XPathResultType.Error;
  455.                 default:
  456.                     /* all numeric types */return XPathResultType.Number;
  457.             }
  458.         }
  459.        
  460.         // ---------------- Xslt Function Implementations -------------------
  461.         //
  462.         static Hashtable CreateFunctionTable()
  463.         {
  464.             Hashtable ft = new Hashtable(10);
  465.             {
  466.                 ft["current"] = new FuncCurrent();
  467.                 ft["unparsed-entity-uri"] = new FuncUnEntityUri();
  468.                 ft["generate-id"] = new FuncGenerateId();
  469.                 ft["system-property"] = new FuncSystemProp();
  470.                 ft["element-available"] = new FuncElementAvailable();
  471.                 ft["function-available"] = new FuncFunctionAvailable();
  472.                 ft["document"] = new FuncDocument();
  473.                 ft["key"] = new FuncKey();
  474.                 ft["format-number"] = new FuncFormatNumber();
  475.             }
  476.             return ft;
  477.         }
  478.        
  479.         // + IXsltContextFunction
  480.         // + XsltFunctionImpl func. name, min/max args, return type args types
  481.         // FuncCurrent "current" 0 0 XPathResultType.NodeSet { }
  482.         // FuncUnEntityUri "unparsed-entity-uri" 1 1 XPathResultType.String { XPathResultType.String }
  483.         // FuncGenerateId "generate-id" 0 1 XPathResultType.String { XPathResultType.NodeSet }
  484.         // FuncSystemProp "system-property" 1 1 XPathResultType.String { XPathResultType.String }
  485.         // FuncElementAvailable "element-available" 1 1 XPathResultType.Boolean { XPathResultType.String }
  486.         // FuncFunctionAvailable "function-available" 1 1 XPathResultType.Boolean { XPathResultType.String }
  487.         // FuncDocument "document" 1 2 XPathResultType.NodeSet { XPathResultType.Any , XPathResultType.NodeSet }
  488.         // FuncKey "key" 2 2 XPathResultType.NodeSet { XPathResultType.String , XPathResultType.Any }
  489.         // FuncFormatNumber "format-number" 2 3 XPathResultType.String { XPathResultType.Number , XPathResultType.String, XPathResultType.String }
  490.         // FuncNodeSet "msxsl:node-set" 1 1 XPathResultType.NodeSet { XPathResultType.Navigator }
  491.         // FuncExtension
  492.         //
  493.         private abstract class XsltFunctionImpl : IXsltContextFunction
  494.         {
  495.             private int minargs;
  496.             private int maxargs;
  497.             private XPathResultType returnType;
  498.             private XPathResultType[] argTypes;
  499.            
  500.             public XsltFunctionImpl()
  501.             {
  502.             }
  503.             public XsltFunctionImpl(int minArgs, int maxArgs, XPathResultType returnType, XPathResultType[] argTypes)
  504.             {
  505.                 this.Init(minArgs, maxArgs, returnType, argTypes);
  506.             }
  507.             protected void Init(int minArgs, int maxArgs, XPathResultType returnType, XPathResultType[] argTypes)
  508.             {
  509.                 this.minargs = minArgs;
  510.                 this.maxargs = maxArgs;
  511.                 this.returnType = returnType;
  512.                 this.argTypes = argTypes;
  513.             }
  514.            
  515.             public int Minargs {
  516.                 get { return this.minargs; }
  517.             }
  518.             public int Maxargs {
  519.                 get { return this.maxargs; }
  520.             }
  521.             public XPathResultType ReturnType {
  522.                 get { return this.returnType; }
  523.             }
  524.             public XPathResultType[] ArgTypes {
  525.                 get { return this.argTypes; }
  526.             }
  527.             public abstract object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext);
  528.            
  529.             // static helper methods:
  530.             public static XPathNodeIterator ToIterator(object argument)
  531.             {
  532.                 XPathNodeIterator it = argument as XPathNodeIterator;
  533.                 if (it == null) {
  534.                     throw XsltException.Create(Res.Xslt_NoNodeSetConversion);
  535.                 }
  536.                 return it;
  537.             }
  538.            
  539.             public static XPathNavigator ToNavigator(object argument)
  540.             {
  541.                 XPathNavigator nav = argument as XPathNavigator;
  542.                 if (nav == null) {
  543.                     throw XsltException.Create(Res.Xslt_NoNavigatorConversion);
  544.                 }
  545.                 return nav;
  546.             }
  547.            
  548.             private static string IteratorToString(XPathNodeIterator it)
  549.             {
  550.                 Debug.Assert(it != null);
  551.                 if (it.MoveNext()) {
  552.                     return it.Current.Value;
  553.                 }
  554.                 return string.Empty;
  555.             }
  556.            
  557.             public static string ToString(object argument)
  558.             {
  559.                 XPathNodeIterator it = argument as XPathNodeIterator;
  560.                 if (it != null) {
  561.                     return IteratorToString(it);
  562.                 }
  563.                 else {
  564.                     return XmlConvert.ToXPathString(argument);
  565.                 }
  566.             }
  567.            
  568.             public static bool ToBoolean(object argument)
  569.             {
  570.                 XPathNodeIterator it = argument as XPathNodeIterator;
  571.                 if (it != null) {
  572.                     return Convert.ToBoolean(IteratorToString(it), CultureInfo.InvariantCulture);
  573.                 }
  574.                 XPathNavigator nav = argument as XPathNavigator;
  575.                 if (nav != null) {
  576.                     return Convert.ToBoolean(nav.ToString(), CultureInfo.InvariantCulture);
  577.                 }
  578.                 return Convert.ToBoolean(argument, CultureInfo.InvariantCulture);
  579.             }
  580.            
  581.             public static double ToNumber(object argument)
  582.             {
  583.                 XPathNodeIterator it = argument as XPathNodeIterator;
  584.                 if (it != null) {
  585.                     return XmlConvert.ToXPathDouble(IteratorToString(it));
  586.                 }
  587.                 XPathNavigator nav = argument as XPathNavigator;
  588.                 if (nav != null) {
  589.                     return XmlConvert.ToXPathDouble(nav.ToString());
  590.                 }
  591.                 return XmlConvert.ToXPathDouble(argument);
  592.             }
  593.            
  594.             private static object ToNumeric(object argument, TypeCode typeCode)
  595.             {
  596.                 return Convert.ChangeType(ToNumber(argument), typeCode, CultureInfo.InvariantCulture);
  597.             }
  598.            
  599.             public static object ConvertToXPathType(object val, XPathResultType xt, TypeCode typeCode)
  600.             {
  601.                 switch (xt) {
  602.                     case XPathResultType.String:
  603.                         if (typeCode == TypeCode.String) {
  604.                             return ToString(val);
  605.                         }
  606.                         else {
  607.                             return ToNavigator(val);
  608.                         }
  609.                         break;
  610.                     case XPathResultType.Number:
  611.                         return ToNumeric(val, typeCode);
  612.                     case XPathResultType.Boolean:
  613.                         return ToBoolean(val);
  614.                     case XPathResultType.NodeSet:
  615.                         return ToIterator(val);
  616.                     case XPathResultType.Any:
  617.                     case XPathResultType.Error:
  618.                         // case XPathResultType.Navigator : return ToNavigator(val);
  619.                         return val;
  620.                     default:
  621.                         Debug.Assert(false, "unexpected XPath type");
  622.                         return val;
  623.                 }
  624.             }
  625.         }
  626.        
  627.         private class FuncCurrent : XsltFunctionImpl
  628.         {
  629.             public FuncCurrent() : base(0, 0, XPathResultType.NodeSet, new XPathResultType[] {})
  630.             {
  631.             }
  632.             public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext)
  633.             {
  634.                 return ((XsltCompileContext)xsltContext).Current();
  635.             }
  636.         }
  637.        
  638.         private class FuncUnEntityUri : XsltFunctionImpl
  639.         {
  640.             public FuncUnEntityUri() : base(1, 1, XPathResultType.String, new XPathResultType[] {XPathResultType.String})
  641.             {
  642.             }
  643.             public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext)
  644.             {
  645.                 throw XsltException.Create(Res.Xslt_UnsuppFunction, "unparsed-entity-uri");
  646.             }
  647.         }
  648.        
  649.         private class FuncGenerateId : XsltFunctionImpl
  650.         {
  651.             public FuncGenerateId() : base(0, 1, XPathResultType.String, new XPathResultType[] {XPathResultType.NodeSet})
  652.             {
  653.             }
  654.             public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext)
  655.             {
  656.                 if (args.Length > 0) {
  657.                     XPathNodeIterator it = ToIterator(args[0]);
  658.                     if (it.MoveNext()) {
  659.                         return it.Current.UniqueId;
  660.                     }
  661.                     else {
  662.                         // if empty nodeset, return empty string, otherwise return generated id
  663.                         return string.Empty;
  664.                     }
  665.                 }
  666.                 else {
  667.                     return docContext.UniqueId;
  668.                 }
  669.             }
  670.         }
  671.        
  672.         private class FuncSystemProp : XsltFunctionImpl
  673.         {
  674.             public FuncSystemProp() : base(1, 1, XPathResultType.String, new XPathResultType[] {XPathResultType.String})
  675.             {
  676.             }
  677.             public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext)
  678.             {
  679.                 return ((XsltCompileContext)xsltContext).SystemProperty(ToString(args[0]));
  680.             }
  681.         }
  682.        
  683.         // see http://www.w3.org/TR/xslt#function-element-available
  684.         private class FuncElementAvailable : XsltFunctionImpl
  685.         {
  686.             public FuncElementAvailable() : base(1, 1, XPathResultType.Boolean, new XPathResultType[] {XPathResultType.String})
  687.             {
  688.             }
  689.             public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext)
  690.             {
  691.                 return ((XsltCompileContext)xsltContext).ElementAvailable(ToString(args[0]));
  692.             }
  693.         }
  694.        
  695.         // see: http://www.w3.org/TR/xslt#function-function-available
  696.         private class FuncFunctionAvailable : XsltFunctionImpl
  697.         {
  698.             public FuncFunctionAvailable() : base(1, 1, XPathResultType.Boolean, new XPathResultType[] {XPathResultType.String})
  699.             {
  700.             }
  701.             public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext)
  702.             {
  703.                 return ((XsltCompileContext)xsltContext).FunctionAvailable(ToString(args[0]));
  704.             }
  705.         }
  706.        
  707.         private class FuncDocument : XsltFunctionImpl
  708.         {
  709.             public FuncDocument() : base(1, 2, XPathResultType.NodeSet, new XPathResultType[] {XPathResultType.Any, XPathResultType.NodeSet})
  710.             {
  711.             }
  712.             public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext)
  713.             {
  714.                 string baseUri = null;
  715.                 if (args.Length == 2) {
  716.                     XPathNodeIterator it = ToIterator(args[1]);
  717.                     if (it.MoveNext()) {
  718.                         baseUri = it.Current.BaseURI;
  719.                     }
  720.                     else {
  721.                         // http://www.w3.org/1999/11/REC-xslt-19991116-errata (E14):
  722.                         // 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;
  723.                         // if it does not signal an error, it must recover by returning an empty node-set.
  724.                         baseUri = string.Empty;
  725.                         // call to Document will fail if args[0] is reletive.
  726.                     }
  727.                 }
  728.                 try {
  729.                     return ((XsltCompileContext)xsltContext).Document(args[0], baseUri);
  730.                 }
  731.                 catch (Exception e) {
  732.                     if (!XmlException.IsCatchableException(e)) {
  733.                         throw;
  734.                     }
  735.                     return XPathEmptyIterator.Instance;
  736.                 }
  737.             }
  738.         }
  739.        
  740.         private class FuncKey : XsltFunctionImpl
  741.         {
  742.             public FuncKey() : base(2, 2, XPathResultType.NodeSet, new XPathResultType[] {XPathResultType.String, XPathResultType.Any})
  743.             {
  744.             }
  745.             public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext)
  746.             {
  747.                 XsltCompileContext xsltCompileContext = (XsltCompileContext)xsltContext;
  748.                
  749.                 string local;
  750.                 string prefix;
  751.                 PrefixQName.ParseQualifiedName(ToString(args[0]), out prefix, out local);
  752.                 string ns = xsltContext.LookupNamespace(prefix);
  753.                 XmlQualifiedName keyName = new XmlQualifiedName(local, ns);
  754.                
  755.                 XPathNavigator root = docContext.Clone();
  756.                 root.MoveToRoot();
  757.                
  758.                 ArrayList resultCollection = null;
  759.                
  760.                 foreach (Key key in xsltCompileContext.processor.KeyList) {
  761.                     if (key.Name == keyName) {
  762.                         Hashtable keyTable = key.GetKeys(root);
  763.                         if (keyTable == null) {
  764.                             keyTable = xsltCompileContext.BuildKeyTable(key, root);
  765.                             key.AddKey(root, keyTable);
  766.                         }
  767.                        
  768.                         XPathNodeIterator it = args[1] as XPathNodeIterator;
  769.                         if (it != null) {
  770.                             it = it.Clone();
  771.                             while (it.MoveNext()) {
  772.                                 resultCollection = AddToList(resultCollection, (ArrayList)keyTable[it.Current.Value]);
  773.                             }
  774.                         }
  775.                         else {
  776.                             resultCollection = AddToList(resultCollection, (ArrayList)keyTable[ToString(args[1])]);
  777.                         }
  778.                     }
  779.                 }
  780.                 if (resultCollection == null) {
  781.                     return XPathEmptyIterator.Instance;
  782.                 }
  783.                 else if (resultCollection[0] is XPathNavigator) {
  784.                     return new XPathArrayIterator(resultCollection);
  785.                 }
  786.                 else {
  787.                     return new XPathMultyIterator(resultCollection);
  788.                 }
  789.             }
  790.            
  791.             static ArrayList AddToList(ArrayList resultCollection, ArrayList newList)
  792.             {
  793.                 if (newList == null) {
  794.                     return resultCollection;
  795.                 }
  796.                 if (resultCollection == null) {
  797.                     return newList;
  798.                 }
  799.                 Debug.Assert(resultCollection.Count != 0);
  800.                 Debug.Assert(newList.Count != 0);
  801.                 if (!(resultCollection[0] is ArrayList)) {
  802.                     // Transform resultCollection from ArrayList(XPathNavigator) to ArrayList(ArrayList(XPathNavigator))
  803.                     Debug.Assert(resultCollection[0] is XPathNavigator);
  804.                     ArrayList firstList = resultCollection;
  805.                     resultCollection = new ArrayList();
  806.                     resultCollection.Add(firstList);
  807.                 }
  808.                 resultCollection.Add(newList);
  809.                 return resultCollection;
  810.             }
  811.         }
  812.        
  813.         private class FuncFormatNumber : XsltFunctionImpl
  814.         {
  815.             public FuncFormatNumber() : base(2, 3, XPathResultType.String, new XPathResultType[] {XPathResultType.Number, XPathResultType.String, XPathResultType.String})
  816.             {
  817.             }
  818.             public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext)
  819.             {
  820.                 DecimalFormat formatInfo = ((XsltCompileContext)xsltContext).ResolveFormatName(args.Length == 3 ? ToString(args[2]) : null);
  821.                 return DecimalFormatter.Format(ToNumber(args[0]), ToString(args[1]), formatInfo);
  822.             }
  823.         }
  824.        
  825.         private class FuncNodeSet : XsltFunctionImpl
  826.         {
  827.             public FuncNodeSet() : base(1, 1, XPathResultType.NodeSet, new XPathResultType[] {XPathResultType.Navigator})
  828.             {
  829.             }
  830.             public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext)
  831.             {
  832.                 return new XPathSingletonIterator(ToNavigator(args[0]));
  833.             }
  834.         }
  835.        
  836.         private class FuncExtension : XsltFunctionImpl
  837.         {
  838.             private object extension;
  839.             private MethodInfo method;
  840.             private TypeCode[] typeCodes;
  841.             private PermissionSet permissions;
  842.            
  843.             public FuncExtension(object extension, MethodInfo method, PermissionSet permissions)
  844.             {
  845.                 Debug.Assert(extension != null);
  846.                 Debug.Assert(method != null);
  847.                 this.extension = extension;
  848.                 this.method = method;
  849.                 this.permissions = permissions;
  850.                 XPathResultType returnType = GetXPathType(method.ReturnType);
  851.                
  852.                 ParameterInfo[] parameters = method.GetParameters();
  853.                 int minArgs = parameters.Length;
  854.                 int maxArgs = parameters.Length;
  855.                 this.typeCodes = new TypeCode[parameters.Length];
  856.                 XPathResultType[] argTypes = new XPathResultType[parameters.Length];
  857.                 bool optionalParams = true;
  858.                 // we allow only last params be optional. Set false on the first non optional.
  859.                 for (int i = parameters.Length - 1; 0 <= i; i--) {
  860.                     // Revers order is essential: counting optional parameters
  861.                     typeCodes[i] = Type.GetTypeCode(parameters[i].ParameterType);
  862.                     argTypes[i] = GetXPathType(parameters[i].ParameterType);
  863.                     if (optionalParams) {
  864.                         if (parameters[i].IsOptional) {
  865.                             minArgs--;
  866.                         }
  867.                         else {
  868.                             optionalParams = false;
  869.                         }
  870.                     }
  871.                 }
  872.                 base.Init(minArgs, maxArgs, returnType, argTypes);
  873.             }
  874.            
  875.             public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext)
  876.             {
  877.                 Debug.Assert(args.Length <= this.Minargs, "We cheking this on resolve time");
  878.                 for (int i = args.Length - 1; 0 <= i; i--) {
  879.                     args[i] = ConvertToXPathType(args[i], this.ArgTypes[i], this.typeCodes[i]);
  880.                 }
  881.                 if (this.permissions != null) {
  882.                     this.permissions.PermitOnly();
  883.                 }
  884.                 return method.Invoke(extension, args);
  885.             }
  886.         }
  887.     }
  888. }

Developer Fusion