XsltCompileContext.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / XmlUtils / System / Xml / Xsl / XsltOld / XsltCompileContext.cs / 1305376 / XsltCompileContext.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
//-----------------------------------------------------------------------------
 
namespace System.Xml.Xsl.XsltOld { 
    using Res = System.Xml.Utils.Res;
    using System.Diagnostics; 
    using System.IO;
    using System.Globalization;
    using System.Collections;
    using System.Xml.XPath; 
    using System.Xml.Xsl.Runtime;
    using MS.Internal.Xml.XPath; 
    using System.Reflection; 
    using System.Security;
    using System.Runtime.Versioning; 

    internal class XsltCompileContext : XsltContext {
        private InputScopeManager manager;
        private Processor         processor; 

        // storage for the functions 
        private static Hashtable            s_FunctionTable = CreateFunctionTable(); 
        private static IXsltContextFunction s_FuncNodeSet   = new FuncNodeSet();
        const          string               f_NodeSet       = "node-set"; 

        internal XsltCompileContext(InputScopeManager manager, Processor processor) : base(/*dummy*/false) {
            this.manager   = manager;
            this.processor = processor; 
        }
 
        internal XsltCompileContext() : base(/*dummy*/ false ) {} 

        internal void Recycle() { 
            manager = null;
            processor = null;
        }
 
        internal void Reinitialize(InputScopeManager manager, Processor processor) {
            this.manager = manager; 
            this.processor = processor; 
        }
 
        public override int CompareDocument(string baseUri, string nextbaseUri) {
            return String.Compare(baseUri, nextbaseUri, StringComparison.Ordinal);
        }
 
        // Namespace support
        public override string DefaultNamespace { 
            get { return string.Empty; } 
        }
 
        [System.Runtime.TargetedPatchingOptOutAttribute("Performance critical to inline across NGen image boundaries")]
        public override string LookupNamespace(string prefix) {
            return this.manager.ResolveXPathNamespace(prefix);
        } 

        // --------------------------- XsltContext ------------------- 
        //                Resolving variables and functions 

        public override IXsltContextVariable ResolveVariable(string prefix, string name) { 
            string namespaceURI = this.LookupNamespace(prefix);
            XmlQualifiedName qname = new XmlQualifiedName(name, namespaceURI);
            IXsltContextVariable variable = this.manager.VariableScope.ResolveVariable(qname);
            if (variable == null) { 
                throw XsltException.Create(Res.Xslt_InvalidVariable, qname.ToString());
            } 
            return variable; 
        }
 
        internal object EvaluateVariable(VariableAction variable) {
            Object result = this.processor.GetVariableValue(variable);
            if (result == null && ! variable.IsGlobal) {
                // This was uninitialized local variable. May be we have sutable global var too? 
                VariableAction global = this.manager.VariableScope.ResolveGlobalVariable(variable.Name);
                if (global != null) { 
                    result = this.processor.GetVariableValue(global); 
                }
            } 
            if (result == null) {
                throw XsltException.Create(Res.Xslt_InvalidVariable, variable.Name.ToString());
            }
            return result; 
        }
 
        // Whitespace stripping support 
        public override bool Whitespace {
            get { return this.processor.Stylesheet.Whitespace; } 
        }

        public override bool PreserveWhitespace(XPathNavigator node) {
            node = node.Clone(); 
            node.MoveToParent();
            return this.processor.Stylesheet.PreserveWhiteSpace(this.processor, node); 
        } 

        private MethodInfo FindBestMethod(MethodInfo[] methods, bool ignoreCase, bool publicOnly, string name, XPathResultType[] argTypes) { 
            int length = methods.Length;
            int free   = 0;
            // restrict search to methods with the same name and requiested protection attribute
            for(int i = 0; i < length; i ++) { 
                if(string.Compare(name, methods[i].Name, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) == 0) {
                    if(! publicOnly || methods[i].GetBaseDefinition().IsPublic) { 
                        methods[free ++] = methods[i]; 
                    }
                } 
            }
            length = free;
            if(length == 0) {
                // this is the only place we returning null in this function 
                return null;
            } 
            if(argTypes == null) { 
                // without arg types we can't do more detailed search
                return methods[0]; 
            }
            // restrict search by number of parameters
            free = 0;
            for(int i = 0; i < length; i ++) { 
                if(methods[i].GetParameters().Length == argTypes.Length) {
                    methods[free ++] = methods[i]; 
                } 
            }
            length = free; 
            if(length <= 1) {
                // 0 -- not method found. We have to return non-null and let it fail with corect exception on call.
                // 1 -- no reason to continue search anyway.
                return methods[0]; 
            }
            // restrict search by parameters type 
            free = 0; 
            for(int i = 0; i < length; i ++) {
                bool match = true; 
                ParameterInfo[] parameters = methods[i].GetParameters();
                for(int par = 0; par < parameters.Length; par ++) {
                    XPathResultType required = argTypes[par];
                    if(required == XPathResultType.Any) { 
                        continue;                        // Any means we don't know type and can't discriminate by it
                    } 
                    XPathResultType actual = GetXPathType(parameters[par].ParameterType); 
                    if(
                        actual != required && 
                        actual != XPathResultType.Any   // actual arg is object and we can pass everithing here.
                    ) {
                        match = false;
                        break; 
                    }
                } 
                if(match) { 
                    methods[free ++] = methods[i];
                } 
            }
            length = free;
            return methods[0];
        } 

        private const BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static; 
        private IXsltContextFunction GetExtentionMethod(string ns, string name, XPathResultType[] argTypes, out object extension) { 
            FuncExtension result = null;
            extension = this.processor.GetScriptObject(ns); 
            if (extension != null) {
                MethodInfo method = FindBestMethod(extension.GetType().GetMethods(bindingFlags), /*ignoreCase:*/true, /*publicOnly:*/false, name, argTypes);
                if (method != null) {
                    result = new FuncExtension(extension, method, /*permissions*/null); 
                }
                return result; 
            } 

            extension = this.processor.GetExtensionObject(ns); 
            if(extension != null) {
                MethodInfo method = FindBestMethod(extension.GetType().GetMethods(bindingFlags), /*ignoreCase:*/false, /*publicOnly:*/true, name, argTypes);
                if (method != null) {
                    result = new FuncExtension(extension, method, this.processor.permissions); 
                }
                return result; 
            } 

            return null; 
        }

        public override IXsltContextFunction ResolveFunction(string prefix, string name, XPathResultType[] argTypes) {
            IXsltContextFunction func = null; 
            if(prefix.Length == 0) {
                func = s_FunctionTable[name] as IXsltContextFunction; 
            } else { 
                string ns = this.LookupNamespace(prefix);
                if (ns == XmlReservedNs.NsMsxsl && name == f_NodeSet) { 
                    func = s_FuncNodeSet;
                } else {
                    object     extension;
                    func = GetExtentionMethod(ns, name, argTypes, out extension); 
                    if(extension == null) {
                        throw XsltException.Create(Res.Xslt_ScriptInvalidPrefix, prefix);  // BugBug: It's better to say that method 'name' not found 
                    } 
                }
            } 
            if (func == null) {
                throw XsltException.Create(Res.Xslt_UnknownXsltFunction, name);
            }
            if (argTypes.Length < func.Minargs || func.Maxargs < argTypes.Length) { 
               throw XsltException.Create(Res.Xslt_WrongNumberArgs, name, argTypes.Length.ToString(CultureInfo.InvariantCulture));
            } 
            return func; 
        }
 
        //
        // Xslt Function Extensions to XPath
        //
        [ResourceConsumption(ResourceScope.Machine)] 
        [ResourceExposure(ResourceScope.Machine)]
        private Uri ComposeUri(string thisUri, string baseUri) { 
            Debug.Assert(thisUri != null && baseUri != null); 
            XmlResolver resolver = processor.Resolver;
            Uri uriBase = null; 
            if (baseUri.Length != 0) {
                uriBase = resolver.ResolveUri(null, baseUri);
            }
            return resolver.ResolveUri(uriBase, thisUri); 
        }
 
        [ResourceConsumption(ResourceScope.Machine)] 
        [ResourceExposure(ResourceScope.Machine)]
        private XPathNodeIterator Document(object arg0, string baseUri) { 
            if (this.processor.permissions != null) {
                this.processor.permissions.PermitOnly();
            }
            XPathNodeIterator it = arg0 as XPathNodeIterator; 
            if (it != null) {
                ArrayList list = new ArrayList(); 
                Hashtable documents = new Hashtable(); 
                while (it.MoveNext()) {
                    Uri uri = ComposeUri(it.Current.Value, baseUri ?? it.Current.BaseURI); 
                    if (! documents.ContainsKey(uri)) {
                        documents.Add(uri, null);
                        list.Add(this.processor.GetNavigator(uri));
                    } 
                }
                return new XPathArrayIterator(list); 
            } else { 
                return new XPathSingletonIterator(
                    this.processor.GetNavigator( 
                        ComposeUri(XmlConvert.ToXPathString(arg0), baseUri ?? this.manager.Navigator.BaseURI)
                    )
                );
            } 
        }
 
        private Hashtable BuildKeyTable(Key key, XPathNavigator root) { 
            Hashtable keyTable = new Hashtable();
 
            string matchStr = this.processor.GetQueryExpression(key.MatchKey);
            Query matchExpr = this.processor.GetCompiledQuery(key.MatchKey);
            Query useExpr   = this.processor.GetCompiledQuery(key.UseKey);
 
            XPathNodeIterator  sel = root.SelectDescendants(XPathNodeType.All, /*matchSelf:*/ false);
 
            while(sel.MoveNext()) { 
                XPathNavigator node = sel.Current;
                EvaluateKey(node, matchExpr, matchStr, useExpr, keyTable); 
                if (node.MoveToFirstAttribute()) {
                    do {
                        EvaluateKey(node, matchExpr, matchStr, useExpr, keyTable);
                    } while (node.MoveToNextAttribute()); 
                    node.MoveToParent();
                } 
            } 
            return keyTable;
        } 

        private static void AddKeyValue(Hashtable keyTable, String key, XPathNavigator value, bool checkDuplicates) {
            ArrayList list = (ArrayList)keyTable[key];
            if (list == null) { 
                list = new ArrayList();
                keyTable.Add(key, list); 
            } else { 
                Debug.Assert(
                    value.ComparePosition((XPathNavigator) list[list.Count - 1]) != XmlNodeOrder.Before, 
                    "The way we traversing nodes should garantees node-order"
                );
                if (checkDuplicates) {
                    // it's posible that this value already was ----osiated with current node 
                    // but if this happened the node is last in the list of values.
                    if (value.ComparePosition((XPathNavigator) list[list.Count - 1]) == XmlNodeOrder.Same) { 
                        return; 
                    }
                } else { 
                    Debug.Assert(
                        value.ComparePosition((XPathNavigator) list[list.Count - 1]) != XmlNodeOrder.Same,
                        "checkDuplicates == false : We can't have duplicates"
                    ); 
                }
            } 
            list.Add(value.Clone()); 
        }
 
        private static void EvaluateKey(XPathNavigator node, Query matchExpr, string matchStr, Query useExpr, Hashtable keyTable) {
            try {
                if (matchExpr.MatchNode(node) == null) {
                    return; 
                }
            } catch(XPathException) { 
                throw XsltException.Create(Res.Xslt_InvalidPattern, matchStr); 
            }
            object result = useExpr.Evaluate(new XPathSingletonIterator(node, /*moved:*/true)); 
            XPathNodeIterator it = result as XPathNodeIterator;
            if (it != null) {
                bool checkDuplicates = false;
                while (it.MoveNext()) { 
                    AddKeyValue(keyTable, /*key:*/it.Current.Value, /*value:*/node, checkDuplicates);
                    checkDuplicates = true; 
                } 
            } else {
                String key = XmlConvert.ToXPathString(result); 
                AddKeyValue(keyTable, key, /*value:*/node, /*checkDuplicates:*/ false);
            }
        }
 
        private DecimalFormat ResolveFormatName(string formatName) {
            string ns = string.Empty, local = string.Empty; 
            if (formatName != null) { 
                string prefix;
                PrefixQName.ParseQualifiedName(formatName, out prefix, out local); 
                ns = LookupNamespace(prefix);
            }
            DecimalFormat formatInfo = this.processor.RootAction.GetDecimalFormat(new XmlQualifiedName(local, ns));
            if (formatInfo == null) { 
                if (formatName != null) {
                    throw XsltException.Create(Res.Xslt_NoDecimalFormat, formatName); 
                } 
                formatInfo = new DecimalFormat(new NumberFormatInfo(), '#', '0', ';');
            } 
            return formatInfo;
        }

        // see http://www.w3.org/TR/xslt#function-element-available 
        private bool ElementAvailable(string qname) {
            string name, prefix; 
            PrefixQName.ParseQualifiedName(qname, out prefix, out name); 
            string ns = this.manager.ResolveXmlNamespace(prefix);
            // msxsl:script - is not an "instruction" so we return false for it. 
            if (ns == XmlReservedNs.NsXslt) {
                return (
                    name == "apply-imports" ||
                    name == "apply-templates" || 
                    name == "attribute" ||
                    name == "call-template" || 
                    name == "choose" || 
                    name == "comment" ||
                    name == "copy" || 
                    name == "copy-of" ||
                    name == "element" ||
                    name == "fallback" ||
                    name == "for-each" || 
                    name == "if" ||
                    name == "message" || 
                    name == "number" || 
                    name == "processing-instruction" ||
                    name == "text" || 
                    name == "value-of" ||
                    name == "variable"
                );
            } 
            return false;
        } 
 
        // see: http://www.w3.org/TR/xslt#function-function-available
        private bool FunctionAvailable(string qname) { 
            string name, prefix;
            PrefixQName.ParseQualifiedName(qname, out prefix, out name);
            string ns = LookupNamespace(prefix);
 
            if (ns == XmlReservedNs.NsMsxsl) {
                return name == f_NodeSet; 
            } else if(ns.Length == 0) { 
                return (
                    // It'll be better to get this information from XPath 
                    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"             || 
                    // XSLT functions:
                    (s_FunctionTable[name] != null && name != "unparsed-entity-uri")
                );
            } else { 
                // Is this script or extention function?
                object extension; 
                return GetExtentionMethod(ns, name, /*argTypes*/null, out extension) != null; 
            }
        } 

        private XPathNodeIterator Current() {
            XPathNavigator nav = this.processor.Current;
            if (nav != null) { 
                return new XPathSingletonIterator(nav.Clone());
            } 
            return XPathEmptyIterator.Instance; 
        }
 
        private String SystemProperty(string qname) {
            String result = string.Empty;

            string prefix; 
            string local;
            PrefixQName.ParseQualifiedName(qname, out prefix, out local); 
 
            // verify the prefix corresponds to the Xslt namespace
            string urn = LookupNamespace(prefix); 

            if (urn == XmlReservedNs.NsXslt) {
                if(local == "version") {
                    result = "1"; 
                } else if(local == "vendor") {
                    result = "Microsoft"; 
                } else if(local == "vendor-url") { 
                    result = "http://www.microsoft.com";
                } 
            } else {
                if(urn == null && prefix != null) {
                // if prefix exist it has to be mapped to namespace.
                // Can it be "" here ? 
                    throw XsltException.Create(Res.Xslt_InvalidPrefix, prefix);
                } 
                return string.Empty; 
            }
 
            return result;
        }

        public static XPathResultType GetXPathType(Type type) { 
            switch(Type.GetTypeCode(type)) {
            case TypeCode.String : 
                return XPathResultType.String; 
            case TypeCode.Boolean :
                return XPathResultType.Boolean; 
            case TypeCode.Object :
                if (
                    typeof(XPathNavigator ).IsAssignableFrom(type) ||
                    typeof(IXPathNavigable).IsAssignableFrom(type) 
                ) {
                    return XPathResultType.Navigator; 
                } 
                if (typeof(XPathNodeIterator).IsAssignableFrom(type)) {
                    return XPathResultType.NodeSet; 
                }
                // [....]: It be better to check that type is realy object and otherwise return XPathResultType.Error
                return XPathResultType.Any;
            case TypeCode.DateTime : 
                return XPathResultType.Error;
            default: /* all numeric types */ 
                return XPathResultType.Number; 
            }
        } 

        // ---------------- Xslt Function Implementations -------------------
        //
        static Hashtable CreateFunctionTable() { 
            Hashtable ft = new Hashtable(10); {
                ft["current"            ] = new FuncCurrent          (); 
                ft["unparsed-entity-uri"] = new FuncUnEntityUri      (); 
                ft["generate-id"        ] = new FuncGenerateId       ();
                ft["system-property"    ] = new FuncSystemProp       (); 
                ft["element-available"  ] = new FuncElementAvailable ();
                ft["function-available" ] = new FuncFunctionAvailable();
                ft["document"           ] = new FuncDocument         ();
                ft["key"                ] = new FuncKey              (); 
                ft["format-number"      ] = new FuncFormatNumber     ();
            } 
            return ft; 
        }
 
        // + IXsltContextFunction
        //   + XsltFunctionImpl             func. name,       min/max args,      return type                args types
        //       FuncCurrent            "current"              0   0         XPathResultType.NodeSet   { }
        //       FuncUnEntityUri        "unparsed-entity-uri"  1   1         XPathResultType.String    { XPathResultType.String  } 
        //       FuncGenerateId         "generate-id"          0   1         XPathResultType.String    { XPathResultType.NodeSet }
        //       FuncSystemProp         "system-property"      1   1         XPathResultType.String    { XPathResultType.String  } 
        //       FuncElementAvailable   "element-available"    1   1         XPathResultType.Boolean   { XPathResultType.String  } 
        //       FuncFunctionAvailable  "function-available"   1   1         XPathResultType.Boolean   { XPathResultType.String  }
        //       FuncDocument           "document"             1   2         XPathResultType.NodeSet   { XPathResultType.Any    , XPathResultType.NodeSet } 
        //       FuncKey                "key"                  2   2         XPathResultType.NodeSet   { XPathResultType.String , XPathResultType.Any     }
        //       FuncFormatNumber       "format-number"        2   3         XPathResultType.String    { XPathResultType.Number , XPathResultType.String, XPathResultType.String }
        //       FuncNodeSet            "msxsl:node-set"       1   1         XPathResultType.NodeSet   { XPathResultType.Navigator }
        //       FuncExtension 
        //
        private abstract class XsltFunctionImpl : IXsltContextFunction { 
            private int               minargs; 
            private int               maxargs;
            private XPathResultType   returnType; 
            private XPathResultType[] argTypes;

            public XsltFunctionImpl() {}
            public XsltFunctionImpl(int minArgs, int maxArgs, XPathResultType returnType, XPathResultType[] argTypes) { 
                this.Init(minArgs, maxArgs, returnType, argTypes);
            } 
            protected void Init(int minArgs, int maxArgs, XPathResultType returnType, XPathResultType[] argTypes) { 
                this.minargs    = minArgs;
                this.maxargs    = maxArgs; 
                this.returnType = returnType;
                this.argTypes   = argTypes;
            }
 
            public int               Minargs       { get { return this.minargs;    } }
            public int               Maxargs       { get { return this.maxargs;    } } 
            public XPathResultType   ReturnType    { get { return this.returnType; } } 
            public XPathResultType[] ArgTypes      { get { return this.argTypes;   } }
            public abstract object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext); 

        // static helper methods:
            public static XPathNodeIterator ToIterator(object argument) {
                XPathNodeIterator it = argument as XPathNodeIterator; 
                if(it == null) {
                    throw XsltException.Create(Res.Xslt_NoNodeSetConversion); 
                } 
                return it;
            } 

            public static XPathNavigator ToNavigator(object argument) {
                XPathNavigator nav = argument as XPathNavigator;
                if(nav == null) { 
                    throw XsltException.Create(Res.Xslt_NoNavigatorConversion);
                } 
                return nav; 
            }
 
            private static string IteratorToString(XPathNodeIterator it) {
                Debug.Assert(it != null);
                if(it.MoveNext()) {
                    return it.Current.Value; 
                }
                return string.Empty; 
            } 

            public static string ToString(object argument) { 
                XPathNodeIterator it = argument as XPathNodeIterator;
                if(it != null) {
                    return IteratorToString(it);
                } else { 
                    return XmlConvert.ToXPathString(argument);
                } 
            } 

            public static bool ToBoolean(object argument) { 
                XPathNodeIterator it = argument as XPathNodeIterator;
                if(it != null) {
                    return Convert.ToBoolean(IteratorToString(it), CultureInfo.InvariantCulture);
                } 
                XPathNavigator nav = argument as XPathNavigator;
                if (nav != null) { 
                    return Convert.ToBoolean(nav.ToString(), CultureInfo.InvariantCulture); 
                }
                return Convert.ToBoolean(argument, CultureInfo.InvariantCulture); 
            }

            public static double ToNumber(object argument) {
                XPathNodeIterator it = argument as XPathNodeIterator; 
                if (it != null) {
                    return XmlConvert.ToXPathDouble(IteratorToString(it)); 
                } 
                XPathNavigator nav = argument as XPathNavigator;
                if (nav != null) { 
                    return XmlConvert.ToXPathDouble(nav.ToString());
                }
                return XmlConvert.ToXPathDouble(argument);
            } 

            private static object ToNumeric(object argument, TypeCode typeCode) { 
                return Convert.ChangeType(ToNumber(argument), typeCode, CultureInfo.InvariantCulture); 
            }
 
            public static object ConvertToXPathType(object val, XPathResultType xt, TypeCode typeCode) {
                switch(xt) {
                case XPathResultType.String    :
                    // Unfortunetely XPathResultType.String == XPathResultType.Navigator (This is wrong but cant be changed in Everett) 
                    // Fortunetely we have typeCode hare so let's discriminate by typeCode
                    if (typeCode == TypeCode.String) { 
                        return ToString(val); 
                    } else {
                        return ToNavigator(val); 
                    }
                case XPathResultType.Number    : return ToNumeric(val, typeCode);
                case XPathResultType.Boolean   : return ToBoolean(val);
                case XPathResultType.NodeSet   : return ToIterator(val); 
//                case XPathResultType.Navigator : return ToNavigator(val);
                case XPathResultType.Any       : 
                case XPathResultType.Error     : 
                    return val;
                default : 
                    Debug.Assert(false, "unexpected XPath type");
                    return val;
                }
            } 
        }
 
        private class FuncCurrent : XsltFunctionImpl { 
            public FuncCurrent() : base(0, 0, XPathResultType.NodeSet, new XPathResultType[] {}) {}
            public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) { 
                return ((XsltCompileContext)xsltContext).Current();
            }
        }
 
        private class FuncUnEntityUri : XsltFunctionImpl {
            public FuncUnEntityUri() : base(1, 1, XPathResultType.String , new XPathResultType[] { XPathResultType.String  }) {} 
            public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) { 
                throw XsltException.Create(Res.Xslt_UnsuppFunction, "unparsed-entity-uri");
            } 
        }

        private class FuncGenerateId : XsltFunctionImpl {
            public FuncGenerateId() : base(0, 1, XPathResultType.String , new XPathResultType[] { XPathResultType.NodeSet }) {} 
            public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
                if(args.Length > 0) { 
                    XPathNodeIterator it = ToIterator(args[0]); 
                    if(it.MoveNext()) {
                        return it.Current.UniqueId; 
                    } else {
                        // if empty nodeset, return empty string, otherwise return generated id
                        return string.Empty;
                    } 
                } else {
                    return docContext.UniqueId; 
                } 
            }
        } 

        private class FuncSystemProp : XsltFunctionImpl {
            public FuncSystemProp() : base(1, 1, XPathResultType.String , new XPathResultType[] { XPathResultType.String  }) {}
            public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) { 
                return ((XsltCompileContext)xsltContext).SystemProperty(ToString(args[0]));
            } 
        } 

        // see http://www.w3.org/TR/xslt#function-element-available 
        private class FuncElementAvailable : XsltFunctionImpl {
            public FuncElementAvailable() : base(1, 1, XPathResultType.Boolean, new XPathResultType[] { XPathResultType.String  }) {}
            public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
                return ((XsltCompileContext)xsltContext).ElementAvailable(ToString(args[0])); 
            }
        } 
 
        // see: http://www.w3.org/TR/xslt#function-function-available
        private class FuncFunctionAvailable : XsltFunctionImpl { 
            public FuncFunctionAvailable() : base(1, 1, XPathResultType.Boolean, new XPathResultType[] { XPathResultType.String  }) {}
            public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
                return ((XsltCompileContext)xsltContext).FunctionAvailable(ToString(args[0]));
            } 
        }
 
        private class FuncDocument : XsltFunctionImpl { 
            public FuncDocument() : base(1, 2, XPathResultType.NodeSet, new XPathResultType[] { XPathResultType.Any    , XPathResultType.NodeSet}) {}
 
            // SxS: This method uses resource names read from source document and does not expose any resources to the caller.
            // It's OK to suppress the SxS warning.
            [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
            [ResourceExposure(ResourceScope.None)] 
            public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
                string baseUri = null; 
                if (args.Length == 2) { 
                    XPathNodeIterator it = ToIterator(args[1]);
                    if (it.MoveNext()){ 
                        baseUri = it.Current.BaseURI;
                    } else {
                        // http://www.w3.org/1999/11/REC-xslt-19991116-errata (E14):
                        // 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; 
                        // if it does not signal an error, it must recover by returning an empty node-set.
                        baseUri = string.Empty; // call to Document will fail if args[0] is reletive. 
                    } 
                }
                try { 
                    return ((XsltCompileContext)xsltContext).Document(args[0], baseUri);
                }
                catch (Exception e) {
                    if (!XmlException.IsCatchableException(e)) { 
                        throw;
                    } 
                    return XPathEmptyIterator.Instance; 
                }
            } 
        }

        private class FuncKey : XsltFunctionImpl {
            public FuncKey() : base(2, 2, XPathResultType.NodeSet, new XPathResultType[] { XPathResultType.String , XPathResultType.Any     }) {} 
            public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
                XsltCompileContext xsltCompileContext = (XsltCompileContext) xsltContext; 
 
                string local, prefix;
                PrefixQName.ParseQualifiedName(ToString(args[0]), out prefix, out local); 
                string ns = xsltContext.LookupNamespace(prefix);
                XmlQualifiedName keyName = new XmlQualifiedName(local, ns);

                XPathNavigator root = docContext.Clone(); 
                root.MoveToRoot();
 
                ArrayList resultCollection = null; 

                foreach (Key key in xsltCompileContext.processor.KeyList) { 
                    if (key.Name == keyName) {
                        Hashtable keyTable = key.GetKeys(root);
                        if (keyTable == null) {
                            keyTable = xsltCompileContext.BuildKeyTable(key, root); 
                            key.AddKey(root, keyTable);
                        } 
 
                        XPathNodeIterator it = args[1] as XPathNodeIterator;
                        if (it != null) { 
                            it = it.Clone();
                            while (it.MoveNext()) {
                                resultCollection = AddToList(resultCollection, (ArrayList) keyTable[it.Current.Value]);
                            } 
                        } else {
                            resultCollection = AddToList(resultCollection, (ArrayList) keyTable[ToString(args[1])]); 
                        } 
                    }
                } 
                if (resultCollection == null) {
                    return XPathEmptyIterator.Instance;
                } else if (resultCollection[0] is XPathNavigator) {
                    return new XPathArrayIterator(resultCollection); 
                } else {
                    return new XPathMultyIterator(resultCollection); 
                } 
            }
 
            static ArrayList AddToList(ArrayList resultCollection, ArrayList newList) {
                if (newList == null) {
                    return resultCollection;
                } 
                if (resultCollection == null) {
                    return newList; 
                } 
                Debug.Assert(resultCollection.Count != 0);
                Debug.Assert(newList.Count != 0); 
                if (! (resultCollection[0] is ArrayList)) {
                    // Transform resultCollection from ArrayList(XPathNavigator) to ArrayList(ArrayList(XPathNavigator))
                    Debug.Assert(resultCollection[0] is XPathNavigator);
                    ArrayList firstList = resultCollection; 
                    resultCollection = new ArrayList();
                    resultCollection.Add(firstList); 
                } 
                resultCollection.Add(newList);
                return resultCollection; 
            }
        }

        private class FuncFormatNumber : XsltFunctionImpl { 
            public FuncFormatNumber() : base(2, 3, XPathResultType.String , new XPathResultType[] { XPathResultType.Number , XPathResultType.String, XPathResultType.String }) {}
            public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) { 
                DecimalFormat formatInfo = ((XsltCompileContext)xsltContext).ResolveFormatName(args.Length == 3 ? ToString(args[2]) : null); 
                return DecimalFormatter.Format(ToNumber(args[0]), ToString(args[1]), formatInfo);
            } 
        }

        private class FuncNodeSet : XsltFunctionImpl {
            public FuncNodeSet() : base(1, 1, XPathResultType.NodeSet, new XPathResultType[] { XPathResultType.Navigator }) {} 
            public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
                return new XPathSingletonIterator(ToNavigator(args[0])); 
            } 
        }
 
        private class FuncExtension : XsltFunctionImpl {
            private object            extension;
            private MethodInfo        method;
            private TypeCode[]        typeCodes; 
            private PermissionSet     permissions;
 
            public FuncExtension(object extension, MethodInfo method, PermissionSet permissions) { 
                Debug.Assert(extension != null);
                Debug.Assert(method    != null); 
                this.extension   = extension;
                this.method      = method;
                this.permissions = permissions;
                XPathResultType returnType = GetXPathType(method.ReturnType); 

                ParameterInfo[] parameters = method.GetParameters(); 
                int minArgs = parameters.Length; 
                int maxArgs = parameters.Length;
                this.typeCodes = new TypeCode[parameters.Length]; 
                XPathResultType[] argTypes = new XPathResultType[parameters.Length];
                bool optionalParams = true; // we allow only last params be optional. Set false on the first non optional.
                for (int i = parameters.Length - 1; 0 <= i; i --) {            // Revers order is essential: counting optional parameters
                    typeCodes[i] = Type.GetTypeCode(parameters[i].ParameterType); 
                    argTypes[i] = GetXPathType(parameters[i].ParameterType);
                    if(optionalParams) { 
                        if(parameters[i].IsOptional) { 
                            minArgs --;
                        } else { 
                            optionalParams = false;
                        }
                    }
                } 
                base.Init(minArgs, maxArgs, returnType, argTypes);
            } 
 
            public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
                Debug.Assert(args.Length <= this.Minargs, "We cheking this on resolve time"); 
                for(int i = args.Length -1; 0 <= i; i --) {
                    args[i] = ConvertToXPathType(args[i], this.ArgTypes[i], this.typeCodes[i]);
                }
                if (this.permissions != null) { 
                    this.permissions.PermitOnly();
                } 
                return method.Invoke(extension, args); 
            }
        } 
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
//-----------------------------------------------------------------------------
 
namespace System.Xml.Xsl.XsltOld { 
    using Res = System.Xml.Utils.Res;
    using System.Diagnostics; 
    using System.IO;
    using System.Globalization;
    using System.Collections;
    using System.Xml.XPath; 
    using System.Xml.Xsl.Runtime;
    using MS.Internal.Xml.XPath; 
    using System.Reflection; 
    using System.Security;
    using System.Runtime.Versioning; 

    internal class XsltCompileContext : XsltContext {
        private InputScopeManager manager;
        private Processor         processor; 

        // storage for the functions 
        private static Hashtable            s_FunctionTable = CreateFunctionTable(); 
        private static IXsltContextFunction s_FuncNodeSet   = new FuncNodeSet();
        const          string               f_NodeSet       = "node-set"; 

        internal XsltCompileContext(InputScopeManager manager, Processor processor) : base(/*dummy*/false) {
            this.manager   = manager;
            this.processor = processor; 
        }
 
        internal XsltCompileContext() : base(/*dummy*/ false ) {} 

        internal void Recycle() { 
            manager = null;
            processor = null;
        }
 
        internal void Reinitialize(InputScopeManager manager, Processor processor) {
            this.manager = manager; 
            this.processor = processor; 
        }
 
        public override int CompareDocument(string baseUri, string nextbaseUri) {
            return String.Compare(baseUri, nextbaseUri, StringComparison.Ordinal);
        }
 
        // Namespace support
        public override string DefaultNamespace { 
            get { return string.Empty; } 
        }
 
        [System.Runtime.TargetedPatchingOptOutAttribute("Performance critical to inline across NGen image boundaries")]
        public override string LookupNamespace(string prefix) {
            return this.manager.ResolveXPathNamespace(prefix);
        } 

        // --------------------------- XsltContext ------------------- 
        //                Resolving variables and functions 

        public override IXsltContextVariable ResolveVariable(string prefix, string name) { 
            string namespaceURI = this.LookupNamespace(prefix);
            XmlQualifiedName qname = new XmlQualifiedName(name, namespaceURI);
            IXsltContextVariable variable = this.manager.VariableScope.ResolveVariable(qname);
            if (variable == null) { 
                throw XsltException.Create(Res.Xslt_InvalidVariable, qname.ToString());
            } 
            return variable; 
        }
 
        internal object EvaluateVariable(VariableAction variable) {
            Object result = this.processor.GetVariableValue(variable);
            if (result == null && ! variable.IsGlobal) {
                // This was uninitialized local variable. May be we have sutable global var too? 
                VariableAction global = this.manager.VariableScope.ResolveGlobalVariable(variable.Name);
                if (global != null) { 
                    result = this.processor.GetVariableValue(global); 
                }
            } 
            if (result == null) {
                throw XsltException.Create(Res.Xslt_InvalidVariable, variable.Name.ToString());
            }
            return result; 
        }
 
        // Whitespace stripping support 
        public override bool Whitespace {
            get { return this.processor.Stylesheet.Whitespace; } 
        }

        public override bool PreserveWhitespace(XPathNavigator node) {
            node = node.Clone(); 
            node.MoveToParent();
            return this.processor.Stylesheet.PreserveWhiteSpace(this.processor, node); 
        } 

        private MethodInfo FindBestMethod(MethodInfo[] methods, bool ignoreCase, bool publicOnly, string name, XPathResultType[] argTypes) { 
            int length = methods.Length;
            int free   = 0;
            // restrict search to methods with the same name and requiested protection attribute
            for(int i = 0; i < length; i ++) { 
                if(string.Compare(name, methods[i].Name, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) == 0) {
                    if(! publicOnly || methods[i].GetBaseDefinition().IsPublic) { 
                        methods[free ++] = methods[i]; 
                    }
                } 
            }
            length = free;
            if(length == 0) {
                // this is the only place we returning null in this function 
                return null;
            } 
            if(argTypes == null) { 
                // without arg types we can't do more detailed search
                return methods[0]; 
            }
            // restrict search by number of parameters
            free = 0;
            for(int i = 0; i < length; i ++) { 
                if(methods[i].GetParameters().Length == argTypes.Length) {
                    methods[free ++] = methods[i]; 
                } 
            }
            length = free; 
            if(length <= 1) {
                // 0 -- not method found. We have to return non-null and let it fail with corect exception on call.
                // 1 -- no reason to continue search anyway.
                return methods[0]; 
            }
            // restrict search by parameters type 
            free = 0; 
            for(int i = 0; i < length; i ++) {
                bool match = true; 
                ParameterInfo[] parameters = methods[i].GetParameters();
                for(int par = 0; par < parameters.Length; par ++) {
                    XPathResultType required = argTypes[par];
                    if(required == XPathResultType.Any) { 
                        continue;                        // Any means we don't know type and can't discriminate by it
                    } 
                    XPathResultType actual = GetXPathType(parameters[par].ParameterType); 
                    if(
                        actual != required && 
                        actual != XPathResultType.Any   // actual arg is object and we can pass everithing here.
                    ) {
                        match = false;
                        break; 
                    }
                } 
                if(match) { 
                    methods[free ++] = methods[i];
                } 
            }
            length = free;
            return methods[0];
        } 

        private const BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static; 
        private IXsltContextFunction GetExtentionMethod(string ns, string name, XPathResultType[] argTypes, out object extension) { 
            FuncExtension result = null;
            extension = this.processor.GetScriptObject(ns); 
            if (extension != null) {
                MethodInfo method = FindBestMethod(extension.GetType().GetMethods(bindingFlags), /*ignoreCase:*/true, /*publicOnly:*/false, name, argTypes);
                if (method != null) {
                    result = new FuncExtension(extension, method, /*permissions*/null); 
                }
                return result; 
            } 

            extension = this.processor.GetExtensionObject(ns); 
            if(extension != null) {
                MethodInfo method = FindBestMethod(extension.GetType().GetMethods(bindingFlags), /*ignoreCase:*/false, /*publicOnly:*/true, name, argTypes);
                if (method != null) {
                    result = new FuncExtension(extension, method, this.processor.permissions); 
                }
                return result; 
            } 

            return null; 
        }

        public override IXsltContextFunction ResolveFunction(string prefix, string name, XPathResultType[] argTypes) {
            IXsltContextFunction func = null; 
            if(prefix.Length == 0) {
                func = s_FunctionTable[name] as IXsltContextFunction; 
            } else { 
                string ns = this.LookupNamespace(prefix);
                if (ns == XmlReservedNs.NsMsxsl && name == f_NodeSet) { 
                    func = s_FuncNodeSet;
                } else {
                    object     extension;
                    func = GetExtentionMethod(ns, name, argTypes, out extension); 
                    if(extension == null) {
                        throw XsltException.Create(Res.Xslt_ScriptInvalidPrefix, prefix);  // BugBug: It's better to say that method 'name' not found 
                    } 
                }
            } 
            if (func == null) {
                throw XsltException.Create(Res.Xslt_UnknownXsltFunction, name);
            }
            if (argTypes.Length < func.Minargs || func.Maxargs < argTypes.Length) { 
               throw XsltException.Create(Res.Xslt_WrongNumberArgs, name, argTypes.Length.ToString(CultureInfo.InvariantCulture));
            } 
            return func; 
        }
 
        //
        // Xslt Function Extensions to XPath
        //
        [ResourceConsumption(ResourceScope.Machine)] 
        [ResourceExposure(ResourceScope.Machine)]
        private Uri ComposeUri(string thisUri, string baseUri) { 
            Debug.Assert(thisUri != null && baseUri != null); 
            XmlResolver resolver = processor.Resolver;
            Uri uriBase = null; 
            if (baseUri.Length != 0) {
                uriBase = resolver.ResolveUri(null, baseUri);
            }
            return resolver.ResolveUri(uriBase, thisUri); 
        }
 
        [ResourceConsumption(ResourceScope.Machine)] 
        [ResourceExposure(ResourceScope.Machine)]
        private XPathNodeIterator Document(object arg0, string baseUri) { 
            if (this.processor.permissions != null) {
                this.processor.permissions.PermitOnly();
            }
            XPathNodeIterator it = arg0 as XPathNodeIterator; 
            if (it != null) {
                ArrayList list = new ArrayList(); 
                Hashtable documents = new Hashtable(); 
                while (it.MoveNext()) {
                    Uri uri = ComposeUri(it.Current.Value, baseUri ?? it.Current.BaseURI); 
                    if (! documents.ContainsKey(uri)) {
                        documents.Add(uri, null);
                        list.Add(this.processor.GetNavigator(uri));
                    } 
                }
                return new XPathArrayIterator(list); 
            } else { 
                return new XPathSingletonIterator(
                    this.processor.GetNavigator( 
                        ComposeUri(XmlConvert.ToXPathString(arg0), baseUri ?? this.manager.Navigator.BaseURI)
                    )
                );
            } 
        }
 
        private Hashtable BuildKeyTable(Key key, XPathNavigator root) { 
            Hashtable keyTable = new Hashtable();
 
            string matchStr = this.processor.GetQueryExpression(key.MatchKey);
            Query matchExpr = this.processor.GetCompiledQuery(key.MatchKey);
            Query useExpr   = this.processor.GetCompiledQuery(key.UseKey);
 
            XPathNodeIterator  sel = root.SelectDescendants(XPathNodeType.All, /*matchSelf:*/ false);
 
            while(sel.MoveNext()) { 
                XPathNavigator node = sel.Current;
                EvaluateKey(node, matchExpr, matchStr, useExpr, keyTable); 
                if (node.MoveToFirstAttribute()) {
                    do {
                        EvaluateKey(node, matchExpr, matchStr, useExpr, keyTable);
                    } while (node.MoveToNextAttribute()); 
                    node.MoveToParent();
                } 
            } 
            return keyTable;
        } 

        private static void AddKeyValue(Hashtable keyTable, String key, XPathNavigator value, bool checkDuplicates) {
            ArrayList list = (ArrayList)keyTable[key];
            if (list == null) { 
                list = new ArrayList();
                keyTable.Add(key, list); 
            } else { 
                Debug.Assert(
                    value.ComparePosition((XPathNavigator) list[list.Count - 1]) != XmlNodeOrder.Before, 
                    "The way we traversing nodes should garantees node-order"
                );
                if (checkDuplicates) {
                    // it's posible that this value already was ----osiated with current node 
                    // but if this happened the node is last in the list of values.
                    if (value.ComparePosition((XPathNavigator) list[list.Count - 1]) == XmlNodeOrder.Same) { 
                        return; 
                    }
                } else { 
                    Debug.Assert(
                        value.ComparePosition((XPathNavigator) list[list.Count - 1]) != XmlNodeOrder.Same,
                        "checkDuplicates == false : We can't have duplicates"
                    ); 
                }
            } 
            list.Add(value.Clone()); 
        }
 
        private static void EvaluateKey(XPathNavigator node, Query matchExpr, string matchStr, Query useExpr, Hashtable keyTable) {
            try {
                if (matchExpr.MatchNode(node) == null) {
                    return; 
                }
            } catch(XPathException) { 
                throw XsltException.Create(Res.Xslt_InvalidPattern, matchStr); 
            }
            object result = useExpr.Evaluate(new XPathSingletonIterator(node, /*moved:*/true)); 
            XPathNodeIterator it = result as XPathNodeIterator;
            if (it != null) {
                bool checkDuplicates = false;
                while (it.MoveNext()) { 
                    AddKeyValue(keyTable, /*key:*/it.Current.Value, /*value:*/node, checkDuplicates);
                    checkDuplicates = true; 
                } 
            } else {
                String key = XmlConvert.ToXPathString(result); 
                AddKeyValue(keyTable, key, /*value:*/node, /*checkDuplicates:*/ false);
            }
        }
 
        private DecimalFormat ResolveFormatName(string formatName) {
            string ns = string.Empty, local = string.Empty; 
            if (formatName != null) { 
                string prefix;
                PrefixQName.ParseQualifiedName(formatName, out prefix, out local); 
                ns = LookupNamespace(prefix);
            }
            DecimalFormat formatInfo = this.processor.RootAction.GetDecimalFormat(new XmlQualifiedName(local, ns));
            if (formatInfo == null) { 
                if (formatName != null) {
                    throw XsltException.Create(Res.Xslt_NoDecimalFormat, formatName); 
                } 
                formatInfo = new DecimalFormat(new NumberFormatInfo(), '#', '0', ';');
            } 
            return formatInfo;
        }

        // see http://www.w3.org/TR/xslt#function-element-available 
        private bool ElementAvailable(string qname) {
            string name, prefix; 
            PrefixQName.ParseQualifiedName(qname, out prefix, out name); 
            string ns = this.manager.ResolveXmlNamespace(prefix);
            // msxsl:script - is not an "instruction" so we return false for it. 
            if (ns == XmlReservedNs.NsXslt) {
                return (
                    name == "apply-imports" ||
                    name == "apply-templates" || 
                    name == "attribute" ||
                    name == "call-template" || 
                    name == "choose" || 
                    name == "comment" ||
                    name == "copy" || 
                    name == "copy-of" ||
                    name == "element" ||
                    name == "fallback" ||
                    name == "for-each" || 
                    name == "if" ||
                    name == "message" || 
                    name == "number" || 
                    name == "processing-instruction" ||
                    name == "text" || 
                    name == "value-of" ||
                    name == "variable"
                );
            } 
            return false;
        } 
 
        // see: http://www.w3.org/TR/xslt#function-function-available
        private bool FunctionAvailable(string qname) { 
            string name, prefix;
            PrefixQName.ParseQualifiedName(qname, out prefix, out name);
            string ns = LookupNamespace(prefix);
 
            if (ns == XmlReservedNs.NsMsxsl) {
                return name == f_NodeSet; 
            } else if(ns.Length == 0) { 
                return (
                    // It'll be better to get this information from XPath 
                    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"             || 
                    // XSLT functions:
                    (s_FunctionTable[name] != null && name != "unparsed-entity-uri")
                );
            } else { 
                // Is this script or extention function?
                object extension; 
                return GetExtentionMethod(ns, name, /*argTypes*/null, out extension) != null; 
            }
        } 

        private XPathNodeIterator Current() {
            XPathNavigator nav = this.processor.Current;
            if (nav != null) { 
                return new XPathSingletonIterator(nav.Clone());
            } 
            return XPathEmptyIterator.Instance; 
        }
 
        private String SystemProperty(string qname) {
            String result = string.Empty;

            string prefix; 
            string local;
            PrefixQName.ParseQualifiedName(qname, out prefix, out local); 
 
            // verify the prefix corresponds to the Xslt namespace
            string urn = LookupNamespace(prefix); 

            if (urn == XmlReservedNs.NsXslt) {
                if(local == "version") {
                    result = "1"; 
                } else if(local == "vendor") {
                    result = "Microsoft"; 
                } else if(local == "vendor-url") { 
                    result = "http://www.microsoft.com";
                } 
            } else {
                if(urn == null && prefix != null) {
                // if prefix exist it has to be mapped to namespace.
                // Can it be "" here ? 
                    throw XsltException.Create(Res.Xslt_InvalidPrefix, prefix);
                } 
                return string.Empty; 
            }
 
            return result;
        }

        public static XPathResultType GetXPathType(Type type) { 
            switch(Type.GetTypeCode(type)) {
            case TypeCode.String : 
                return XPathResultType.String; 
            case TypeCode.Boolean :
                return XPathResultType.Boolean; 
            case TypeCode.Object :
                if (
                    typeof(XPathNavigator ).IsAssignableFrom(type) ||
                    typeof(IXPathNavigable).IsAssignableFrom(type) 
                ) {
                    return XPathResultType.Navigator; 
                } 
                if (typeof(XPathNodeIterator).IsAssignableFrom(type)) {
                    return XPathResultType.NodeSet; 
                }
                // [....]: It be better to check that type is realy object and otherwise return XPathResultType.Error
                return XPathResultType.Any;
            case TypeCode.DateTime : 
                return XPathResultType.Error;
            default: /* all numeric types */ 
                return XPathResultType.Number; 
            }
        } 

        // ---------------- Xslt Function Implementations -------------------
        //
        static Hashtable CreateFunctionTable() { 
            Hashtable ft = new Hashtable(10); {
                ft["current"            ] = new FuncCurrent          (); 
                ft["unparsed-entity-uri"] = new FuncUnEntityUri      (); 
                ft["generate-id"        ] = new FuncGenerateId       ();
                ft["system-property"    ] = new FuncSystemProp       (); 
                ft["element-available"  ] = new FuncElementAvailable ();
                ft["function-available" ] = new FuncFunctionAvailable();
                ft["document"           ] = new FuncDocument         ();
                ft["key"                ] = new FuncKey              (); 
                ft["format-number"      ] = new FuncFormatNumber     ();
            } 
            return ft; 
        }
 
        // + IXsltContextFunction
        //   + XsltFunctionImpl             func. name,       min/max args,      return type                args types
        //       FuncCurrent            "current"              0   0         XPathResultType.NodeSet   { }
        //       FuncUnEntityUri        "unparsed-entity-uri"  1   1         XPathResultType.String    { XPathResultType.String  } 
        //       FuncGenerateId         "generate-id"          0   1         XPathResultType.String    { XPathResultType.NodeSet }
        //       FuncSystemProp         "system-property"      1   1         XPathResultType.String    { XPathResultType.String  } 
        //       FuncElementAvailable   "element-available"    1   1         XPathResultType.Boolean   { XPathResultType.String  } 
        //       FuncFunctionAvailable  "function-available"   1   1         XPathResultType.Boolean   { XPathResultType.String  }
        //       FuncDocument           "document"             1   2         XPathResultType.NodeSet   { XPathResultType.Any    , XPathResultType.NodeSet } 
        //       FuncKey                "key"                  2   2         XPathResultType.NodeSet   { XPathResultType.String , XPathResultType.Any     }
        //       FuncFormatNumber       "format-number"        2   3         XPathResultType.String    { XPathResultType.Number , XPathResultType.String, XPathResultType.String }
        //       FuncNodeSet            "msxsl:node-set"       1   1         XPathResultType.NodeSet   { XPathResultType.Navigator }
        //       FuncExtension 
        //
        private abstract class XsltFunctionImpl : IXsltContextFunction { 
            private int               minargs; 
            private int               maxargs;
            private XPathResultType   returnType; 
            private XPathResultType[] argTypes;

            public XsltFunctionImpl() {}
            public XsltFunctionImpl(int minArgs, int maxArgs, XPathResultType returnType, XPathResultType[] argTypes) { 
                this.Init(minArgs, maxArgs, returnType, argTypes);
            } 
            protected void Init(int minArgs, int maxArgs, XPathResultType returnType, XPathResultType[] argTypes) { 
                this.minargs    = minArgs;
                this.maxargs    = maxArgs; 
                this.returnType = returnType;
                this.argTypes   = argTypes;
            }
 
            public int               Minargs       { get { return this.minargs;    } }
            public int               Maxargs       { get { return this.maxargs;    } } 
            public XPathResultType   ReturnType    { get { return this.returnType; } } 
            public XPathResultType[] ArgTypes      { get { return this.argTypes;   } }
            public abstract object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext); 

        // static helper methods:
            public static XPathNodeIterator ToIterator(object argument) {
                XPathNodeIterator it = argument as XPathNodeIterator; 
                if(it == null) {
                    throw XsltException.Create(Res.Xslt_NoNodeSetConversion); 
                } 
                return it;
            } 

            public static XPathNavigator ToNavigator(object argument) {
                XPathNavigator nav = argument as XPathNavigator;
                if(nav == null) { 
                    throw XsltException.Create(Res.Xslt_NoNavigatorConversion);
                } 
                return nav; 
            }
 
            private static string IteratorToString(XPathNodeIterator it) {
                Debug.Assert(it != null);
                if(it.MoveNext()) {
                    return it.Current.Value; 
                }
                return string.Empty; 
            } 

            public static string ToString(object argument) { 
                XPathNodeIterator it = argument as XPathNodeIterator;
                if(it != null) {
                    return IteratorToString(it);
                } else { 
                    return XmlConvert.ToXPathString(argument);
                } 
            } 

            public static bool ToBoolean(object argument) { 
                XPathNodeIterator it = argument as XPathNodeIterator;
                if(it != null) {
                    return Convert.ToBoolean(IteratorToString(it), CultureInfo.InvariantCulture);
                } 
                XPathNavigator nav = argument as XPathNavigator;
                if (nav != null) { 
                    return Convert.ToBoolean(nav.ToString(), CultureInfo.InvariantCulture); 
                }
                return Convert.ToBoolean(argument, CultureInfo.InvariantCulture); 
            }

            public static double ToNumber(object argument) {
                XPathNodeIterator it = argument as XPathNodeIterator; 
                if (it != null) {
                    return XmlConvert.ToXPathDouble(IteratorToString(it)); 
                } 
                XPathNavigator nav = argument as XPathNavigator;
                if (nav != null) { 
                    return XmlConvert.ToXPathDouble(nav.ToString());
                }
                return XmlConvert.ToXPathDouble(argument);
            } 

            private static object ToNumeric(object argument, TypeCode typeCode) { 
                return Convert.ChangeType(ToNumber(argument), typeCode, CultureInfo.InvariantCulture); 
            }
 
            public static object ConvertToXPathType(object val, XPathResultType xt, TypeCode typeCode) {
                switch(xt) {
                case XPathResultType.String    :
                    // Unfortunetely XPathResultType.String == XPathResultType.Navigator (This is wrong but cant be changed in Everett) 
                    // Fortunetely we have typeCode hare so let's discriminate by typeCode
                    if (typeCode == TypeCode.String) { 
                        return ToString(val); 
                    } else {
                        return ToNavigator(val); 
                    }
                case XPathResultType.Number    : return ToNumeric(val, typeCode);
                case XPathResultType.Boolean   : return ToBoolean(val);
                case XPathResultType.NodeSet   : return ToIterator(val); 
//                case XPathResultType.Navigator : return ToNavigator(val);
                case XPathResultType.Any       : 
                case XPathResultType.Error     : 
                    return val;
                default : 
                    Debug.Assert(false, "unexpected XPath type");
                    return val;
                }
            } 
        }
 
        private class FuncCurrent : XsltFunctionImpl { 
            public FuncCurrent() : base(0, 0, XPathResultType.NodeSet, new XPathResultType[] {}) {}
            public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) { 
                return ((XsltCompileContext)xsltContext).Current();
            }
        }
 
        private class FuncUnEntityUri : XsltFunctionImpl {
            public FuncUnEntityUri() : base(1, 1, XPathResultType.String , new XPathResultType[] { XPathResultType.String  }) {} 
            public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) { 
                throw XsltException.Create(Res.Xslt_UnsuppFunction, "unparsed-entity-uri");
            } 
        }

        private class FuncGenerateId : XsltFunctionImpl {
            public FuncGenerateId() : base(0, 1, XPathResultType.String , new XPathResultType[] { XPathResultType.NodeSet }) {} 
            public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
                if(args.Length > 0) { 
                    XPathNodeIterator it = ToIterator(args[0]); 
                    if(it.MoveNext()) {
                        return it.Current.UniqueId; 
                    } else {
                        // if empty nodeset, return empty string, otherwise return generated id
                        return string.Empty;
                    } 
                } else {
                    return docContext.UniqueId; 
                } 
            }
        } 

        private class FuncSystemProp : XsltFunctionImpl {
            public FuncSystemProp() : base(1, 1, XPathResultType.String , new XPathResultType[] { XPathResultType.String  }) {}
            public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) { 
                return ((XsltCompileContext)xsltContext).SystemProperty(ToString(args[0]));
            } 
        } 

        // see http://www.w3.org/TR/xslt#function-element-available 
        private class FuncElementAvailable : XsltFunctionImpl {
            public FuncElementAvailable() : base(1, 1, XPathResultType.Boolean, new XPathResultType[] { XPathResultType.String  }) {}
            public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
                return ((XsltCompileContext)xsltContext).ElementAvailable(ToString(args[0])); 
            }
        } 
 
        // see: http://www.w3.org/TR/xslt#function-function-available
        private class FuncFunctionAvailable : XsltFunctionImpl { 
            public FuncFunctionAvailable() : base(1, 1, XPathResultType.Boolean, new XPathResultType[] { XPathResultType.String  }) {}
            public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
                return ((XsltCompileContext)xsltContext).FunctionAvailable(ToString(args[0]));
            } 
        }
 
        private class FuncDocument : XsltFunctionImpl { 
            public FuncDocument() : base(1, 2, XPathResultType.NodeSet, new XPathResultType[] { XPathResultType.Any    , XPathResultType.NodeSet}) {}
 
            // SxS: This method uses resource names read from source document and does not expose any resources to the caller.
            // It's OK to suppress the SxS warning.
            [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
            [ResourceExposure(ResourceScope.None)] 
            public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
                string baseUri = null; 
                if (args.Length == 2) { 
                    XPathNodeIterator it = ToIterator(args[1]);
                    if (it.MoveNext()){ 
                        baseUri = it.Current.BaseURI;
                    } else {
                        // http://www.w3.org/1999/11/REC-xslt-19991116-errata (E14):
                        // 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; 
                        // if it does not signal an error, it must recover by returning an empty node-set.
                        baseUri = string.Empty; // call to Document will fail if args[0] is reletive. 
                    } 
                }
                try { 
                    return ((XsltCompileContext)xsltContext).Document(args[0], baseUri);
                }
                catch (Exception e) {
                    if (!XmlException.IsCatchableException(e)) { 
                        throw;
                    } 
                    return XPathEmptyIterator.Instance; 
                }
            } 
        }

        private class FuncKey : XsltFunctionImpl {
            public FuncKey() : base(2, 2, XPathResultType.NodeSet, new XPathResultType[] { XPathResultType.String , XPathResultType.Any     }) {} 
            public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
                XsltCompileContext xsltCompileContext = (XsltCompileContext) xsltContext; 
 
                string local, prefix;
                PrefixQName.ParseQualifiedName(ToString(args[0]), out prefix, out local); 
                string ns = xsltContext.LookupNamespace(prefix);
                XmlQualifiedName keyName = new XmlQualifiedName(local, ns);

                XPathNavigator root = docContext.Clone(); 
                root.MoveToRoot();
 
                ArrayList resultCollection = null; 

                foreach (Key key in xsltCompileContext.processor.KeyList) { 
                    if (key.Name == keyName) {
                        Hashtable keyTable = key.GetKeys(root);
                        if (keyTable == null) {
                            keyTable = xsltCompileContext.BuildKeyTable(key, root); 
                            key.AddKey(root, keyTable);
                        } 
 
                        XPathNodeIterator it = args[1] as XPathNodeIterator;
                        if (it != null) { 
                            it = it.Clone();
                            while (it.MoveNext()) {
                                resultCollection = AddToList(resultCollection, (ArrayList) keyTable[it.Current.Value]);
                            } 
                        } else {
                            resultCollection = AddToList(resultCollection, (ArrayList) keyTable[ToString(args[1])]); 
                        } 
                    }
                } 
                if (resultCollection == null) {
                    return XPathEmptyIterator.Instance;
                } else if (resultCollection[0] is XPathNavigator) {
                    return new XPathArrayIterator(resultCollection); 
                } else {
                    return new XPathMultyIterator(resultCollection); 
                } 
            }
 
            static ArrayList AddToList(ArrayList resultCollection, ArrayList newList) {
                if (newList == null) {
                    return resultCollection;
                } 
                if (resultCollection == null) {
                    return newList; 
                } 
                Debug.Assert(resultCollection.Count != 0);
                Debug.Assert(newList.Count != 0); 
                if (! (resultCollection[0] is ArrayList)) {
                    // Transform resultCollection from ArrayList(XPathNavigator) to ArrayList(ArrayList(XPathNavigator))
                    Debug.Assert(resultCollection[0] is XPathNavigator);
                    ArrayList firstList = resultCollection; 
                    resultCollection = new ArrayList();
                    resultCollection.Add(firstList); 
                } 
                resultCollection.Add(newList);
                return resultCollection; 
            }
        }

        private class FuncFormatNumber : XsltFunctionImpl { 
            public FuncFormatNumber() : base(2, 3, XPathResultType.String , new XPathResultType[] { XPathResultType.Number , XPathResultType.String, XPathResultType.String }) {}
            public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) { 
                DecimalFormat formatInfo = ((XsltCompileContext)xsltContext).ResolveFormatName(args.Length == 3 ? ToString(args[2]) : null); 
                return DecimalFormatter.Format(ToNumber(args[0]), ToString(args[1]), formatInfo);
            } 
        }

        private class FuncNodeSet : XsltFunctionImpl {
            public FuncNodeSet() : base(1, 1, XPathResultType.NodeSet, new XPathResultType[] { XPathResultType.Navigator }) {} 
            public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
                return new XPathSingletonIterator(ToNavigator(args[0])); 
            } 
        }
 
        private class FuncExtension : XsltFunctionImpl {
            private object            extension;
            private MethodInfo        method;
            private TypeCode[]        typeCodes; 
            private PermissionSet     permissions;
 
            public FuncExtension(object extension, MethodInfo method, PermissionSet permissions) { 
                Debug.Assert(extension != null);
                Debug.Assert(method    != null); 
                this.extension   = extension;
                this.method      = method;
                this.permissions = permissions;
                XPathResultType returnType = GetXPathType(method.ReturnType); 

                ParameterInfo[] parameters = method.GetParameters(); 
                int minArgs = parameters.Length; 
                int maxArgs = parameters.Length;
                this.typeCodes = new TypeCode[parameters.Length]; 
                XPathResultType[] argTypes = new XPathResultType[parameters.Length];
                bool optionalParams = true; // we allow only last params be optional. Set false on the first non optional.
                for (int i = parameters.Length - 1; 0 <= i; i --) {            // Revers order is essential: counting optional parameters
                    typeCodes[i] = Type.GetTypeCode(parameters[i].ParameterType); 
                    argTypes[i] = GetXPathType(parameters[i].ParameterType);
                    if(optionalParams) { 
                        if(parameters[i].IsOptional) { 
                            minArgs --;
                        } else { 
                            optionalParams = false;
                        }
                    }
                } 
                base.Init(minArgs, maxArgs, returnType, argTypes);
            } 
 
            public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) {
                Debug.Assert(args.Length <= this.Minargs, "We cheking this on resolve time"); 
                for(int i = args.Length -1; 0 <= i; i --) {
                    args[i] = ConvertToXPathType(args[i], this.ArgTypes[i], this.typeCodes[i]);
                }
                if (this.permissions != null) { 
                    this.permissions.PermitOnly();
                } 
                return method.Invoke(extension, args); 
            }
        } 
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK