The Labs \ Source Viewer \ SSCLI \ System.Xml.Xsl.Xslt \ ThrowErrorHelper

  1. //------------------------------------------------------------------------------
  2. // <copyright file="QilGeneratorEnv.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. using System.Collections.Generic;
  16. using System.Diagnostics;
  17. using System.Xml.XPath;
  18. using System.Xml.Schema;
  19. using System.Xml.Xsl.Qil;
  20. using System.Xml.Xsl.Runtime;
  21. using System.Xml.Xsl.XPath;
  22. namespace System.Xml.Xsl.Xslt
  23. {
  24.     using FunctionInfo = XPathBuilder.FunctionInfo<QilGenerator.FuncId>;
  25.     using Res = System.Xml.Utils.Res;
  26.     using T = XmlQueryTypeFactory;
  27.    
  28.     internal partial class QilGenerator : IXPathEnvironment
  29.     {
  30.        
  31.         // Everywhere in this code in case of error in the stylesheet we should throw XslLoadException.
  32.         // This helper IErrorHelper implementation is used to wrap XmlException's into XslLoadException's.
  33.        
  34.         private struct ThrowErrorHelper : IErrorHelper
  35.         {
  36.             public void ReportError(string res, params string[] args)
  37.             {
  38.                 Debug.Assert(args == null || args.Length == 0, "Error message must already be composed in res");
  39.                 throw new XslLoadException(Res.Xml_UserException, res);
  40.             }
  41.            
  42.             public void ReportWarning(string res, params string[] args)
  43.             {
  44.                 Debug.Fail("Should never get here");
  45.             }
  46.         }
  47.        
  48.         // -------------------------------- IXPathEnvironment --------------------------------
  49.         // IXPathEnvironment represents static context (namespaces, focus) and most naturaly implemented by QilGenerator itself
  50.         // $var current() key()
  51.         // */@select or */@test + + +
  52.         // template/@match - - +
  53.         // key/@match - - -
  54.         // key/@use - + -
  55.         // number/@count + - +
  56.         // number/@from + - +
  57.        
  58.         private bool allowVariables = true;
  59.         private bool allowCurrent = true;
  60.         private bool allowKey = true;
  61.        
  62.         private void SetEnvironmentFlags(bool allowVariables, bool allowCurrent, bool allowKey)
  63.         {
  64.             this.allowVariables = allowVariables;
  65.             this.allowCurrent = allowCurrent;
  66.             this.allowKey = allowKey;
  67.         }
  68.        
  69.         XPathQilFactory IXPathEnvironment.Factory {
  70.             get { return f; }
  71.         }
  72.        
  73.         // IXPathEnvironment interface
  74.         QilNode IFocus.GetCurrent()
  75.         {
  76.             return this.GetCurrentNode();
  77.         }
  78.         QilNode IFocus.GetPosition()
  79.         {
  80.             return this.GetCurrentPosition();
  81.         }
  82.         QilNode IFocus.GetLast()
  83.         {
  84.             return this.GetLastPosition();
  85.         }
  86.        
  87.         string IXPathEnvironment.ResolvePrefix(string prefix)
  88.         {
  89.             return ResolvePrefixThrow(true, prefix);
  90.         }
  91.        
  92.         QilNode IXPathEnvironment.ResolveVariable(string prefix, string name)
  93.         {
  94.             if (!allowVariables) {
  95.                 throw new XslLoadException(Res.Xslt_VariablesNotAllowed);
  96.             }
  97.                 /*ignoreDefaultNs:*/            string ns = ResolvePrefixThrow(true, prefix);
  98.             Debug.Assert(ns != null);
  99.            
  100.             // Look up in params and variables of the current scope and all outer ones
  101.             QilNode var = this.scope.LookupVariable(name, ns);
  102.            
  103.             if (var == null) {
  104.                 throw new XslLoadException(Res.Xslt_InvalidVariable, Compiler.ConstructQName(prefix, name));
  105.             }
  106.            
  107.             // All Node* parameters are guaranteed to be in document order with no duplicates, so TypeAssert
  108.             // this so that optimizer can use this information to avoid redundant sorts and duplicate removal.
  109.             if (var.NodeType == QilNodeType.Parameter && var.XmlType.IsNode && var.XmlType.MaybeMany && !var.XmlType.IsDod) {
  110.                 var = f.TypeAssert(var, XmlQueryTypeFactory.NodeDodS);
  111.             }
  112.            
  113.             return var;
  114.         }
  115.        
  116.         // NOTE: DO NOT call QilNode.Clone() while executing this method since fixup nodes cannot be cloned
  117.         QilNode IXPathEnvironment.ResolveFunction(string prefix, string name, IList<QilNode> args, IFocus env)
  118.         {
  119.             Debug.Assert(!args.IsReadOnly, "Writable collection expected");
  120.             if (prefix.Length == 0) {
  121.                 FunctionInfo func;
  122.                 if (FunctionTable.TryGetValue(name, out func)) {
  123.                     func.CastArguments(args, name, f);
  124.                    
  125.                     switch (func.id) {
  126.                         case FuncId.Current:
  127.                             if (!allowCurrent) {
  128.                                 throw new XslLoadException(Res.Xslt_CurrentNotAllowed);
  129.                             }
  130.                             // NOTE: This is the only place where the current node (and not the context node) must be used
  131.                             return ((IXPathEnvironment)this).GetCurrent();
  132.                         case FuncId.Key:
  133.                             if (!allowKey) {
  134.                                 throw new XslLoadException(Res.Xslt_KeyNotAllowed);
  135.                             }
  136.                             return CompileFnKey(args[0], args[1], env);
  137.                         case FuncId.Document:
  138.                             return CompileFnDocument(args[0], args.Count > 1 ? args[1] : null);
  139.                         case FuncId.FormatNumber:
  140.                             return CompileFormatNumber(args[0], args[1], args.Count > 2 ? args[2] : null);
  141.                         case FuncId.UnparsedEntityUri:
  142.                             return CompileUnparsedEntityUri(args[0]);
  143.                         case FuncId.GenerateId:
  144.                             return CompileGenerateId(args.Count > 0 ? args[0] : env.GetCurrent());
  145.                         case FuncId.SystemProperty:
  146.                             return CompileSystemProperty(args[0]);
  147.                         case FuncId.ElementAvailable:
  148.                             return CompileElementAvailable(args[0]);
  149.                         case FuncId.FunctionAvailable:
  150.                             return CompileFunctionAvailable(args[0]);
  151.                         default:
  152.                             Debug.Fail(func.id + " is present in the function table, but absent from the switch");
  153.                             return null;
  154.                     }
  155.                 }
  156.                 else {
  157.                     throw new XslLoadException(Res.Xslt_UnknownXsltFunction, Compiler.ConstructQName(prefix, name));
  158.                 }
  159.             }
  160.             else {
  161.                     /*ignoreDefaultNs:*/                string ns = ResolvePrefixThrow(true, prefix);
  162.                 Debug.Assert(ns != null);
  163.                 if (ns == XmlReservedNs.NsMsxsl) {
  164.                     if (name == "node-set") {
  165.                             /*minArg:*/                            /*maxArg:*/                        FunctionInfo.CheckArity(1, 1, name, args.Count);
  166.                         return CompileMsNodeSet(args[0]);
  167.                     }
  168.                     else if (name == "string-compare") {
  169.                             /*minArg:*/                            /*maxArg:*/                        FunctionInfo.CheckArity(2, 4, name, args.Count);
  170.                             /*x:      */                            /*y:      */                            /*lang:  */                            /*options:*/                        return f.InvokeMsStringCompare(f.ConvertToString(args[0]), f.ConvertToString(args[1]), 2 < args.Count ? f.ConvertToString(args[2]) : f.String(string.Empty), 3 < args.Count ? f.ConvertToString(args[3]) : f.String(string.Empty));
  171.                     }
  172.                     else if (name == "utc") {
  173.                             /*minArg:*/                            /*maxArg:*/                        FunctionInfo.CheckArity(1, 1, name, args.Count);
  174.                             /*datetime:*/                        return f.InvokeMsUtc(f.ConvertToString(args[0]));
  175.                     }
  176.                     else if (name == "format-date" || name == "format-time") {
  177.                             /*minArg:*/                            /*maxArg:*/                        FunctionInfo.CheckArity(1, 3, name, args.Count);
  178.                         bool fwdCompat = (xslVersion == XslVersion.ForwardsCompatible);
  179.                             /*datetime:*/                            /*format:  */                            /*lang:    */                            /*isDate:  */                        return f.InvokeMsFormatDateTime(f.ConvertToString(args[0]), 1 < args.Count ? f.ConvertToString(args[1]) : f.String(string.Empty), 2 < args.Count ? f.ConvertToString(args[2]) : f.String(string.Empty), f.Boolean(name == "format-date"));
  180.                     }
  181.                     else if (name == "local-name") {
  182.                             /*minArg:*/                            /*maxArg:*/                        FunctionInfo.CheckArity(1, 1, name, args.Count);
  183.                         return f.InvokeMsLocalName(f.ConvertToString(args[0]));
  184.                     }
  185.                     else if (name == "namespace-uri") {
  186.                             /*minArg:*/                            /*maxArg:*/                        FunctionInfo.CheckArity(1, 1, name, args.Count);
  187.                         return f.InvokeMsNamespaceUri(f.ConvertToString(args[0]), env.GetCurrent());
  188.                     }
  189.                     else if (name == "number") {
  190.                             /*minArg:*/                            /*maxArg:*/                        FunctionInfo.CheckArity(1, 1, name, args.Count);
  191.                         return f.InvokeMsNumber(args[0]);
  192.                     }
  193.                 }
  194.                
  195.                 if (ns == XmlReservedNs.NsExsltCommon) {
  196.                     if (name == "node-set") {
  197.                             /*minArg:*/                            /*maxArg:*/                        FunctionInfo.CheckArity(1, 1, name, args.Count);
  198.                         return CompileMsNodeSet(args[0]);
  199.                     }
  200.                     else if (name == "object-type") {
  201.                             /*minArg:*/                            /*maxArg:*/                        FunctionInfo.CheckArity(1, 1, name, args.Count);
  202.                         return EXslObjectType(args[0]);
  203.                     }
  204.                 }
  205.                
  206.                 // NOTE: If you add any function here, add it to IsFunctionAvailable as well
  207.                
  208.                 // Ensure that all node-set parameters are DocOrderDistinct
  209.                 for (int i = 0; i < args.Count; i++)
  210.                     args[i] = f.SafeDocOrderDistinct(args[i]);
  211.                
  212.                 if (compiler.Settings.EnableScript) {
  213.                     XmlExtensionFunction scrFunc = compiler.Scripts.ResolveFunction(name, ns, args.Count, (IErrorHelper)this);
  214.                     if (scrFunc != null) {
  215.                         return GenerateScriptCall(f.QName(name, ns, prefix), scrFunc, args);
  216.                     }
  217.                 }
  218.                 else {
  219.                     if (compiler.Scripts.ScriptClasses.ContainsKey(ns)) {
  220.                         ReportWarning(Res.Xslt_ScriptsProhibited);
  221.                         return f.Error(lastScope.SourceLine, Res.Xslt_ScriptsProhibited);
  222.                     }
  223.                 }
  224.                
  225.                 return f.XsltInvokeLateBound(f.QName(name, ns, prefix), args);
  226.             }
  227.         }
  228.        
  229.         private QilNode GenerateScriptCall(QilName name, XmlExtensionFunction scrFunc, IList<QilNode> args)
  230.         {
  231.             XmlQueryType xmlTypeFormalArg;
  232.            
  233.             for (int i = 0; i < args.Count; i++) {
  234.                 xmlTypeFormalArg = scrFunc.GetXmlArgumentType(i);
  235.                 switch (xmlTypeFormalArg.TypeCode) {
  236.                     case XmlTypeCode.Boolean:
  237.                         args[i] = f.ConvertToBoolean(args[i]);
  238.                         break;
  239.                     case XmlTypeCode.Double:
  240.                         args[i] = f.ConvertToNumber(args[i]);
  241.                         break;
  242.                     case XmlTypeCode.String:
  243.                         args[i] = f.ConvertToString(args[i]);
  244.                         break;
  245.                     case XmlTypeCode.Node:
  246.                         args[i] = xmlTypeFormalArg.IsSingleton ? f.ConvertToNode(args[i]) : f.ConvertToNodeSet(args[i]);
  247.                         break;
  248.                     case XmlTypeCode.Item:
  249.                         break;
  250.                     default:
  251.                         Debug.Fail("This XmlTypeCode should never be inferred from a Clr type: " + xmlTypeFormalArg.TypeCode);
  252.                         break;
  253.                 }
  254.             }
  255.            
  256.             return f.XsltInvokeEarlyBound(name, scrFunc.Method, scrFunc.XmlReturnType, args);
  257.         }
  258.        
  259.         private string ResolvePrefixThrow(bool ignoreDefaultNs, string prefix)
  260.         {
  261.             if (ignoreDefaultNs && prefix.Length == 0) {
  262.                 return string.Empty;
  263.             }
  264.             else {
  265.                 string ns = scope.LookupNamespace(prefix);
  266.                 if (ns == null) {
  267.                     if (prefix.Length != 0) {
  268.                         throw new XslLoadException(Res.Xslt_InvalidPrefix, prefix);
  269.                     }
  270.                     ns = string.Empty;
  271.                 }
  272.                 return ns;
  273.             }
  274.         }
  275.        
  276.         //------------------------------------------------
  277.         // XSLT Functions
  278.         //------------------------------------------------
  279.        
  280.         public enum FuncId
  281.         {
  282.             Current,
  283.             Document,
  284.             Key,
  285.             FormatNumber,
  286.             UnparsedEntityUri,
  287.             GenerateId,
  288.             SystemProperty,
  289.             ElementAvailable,
  290.             FunctionAvailable
  291.         }
  292.        
  293.         private static readonly XmlTypeCode[] argFnDocument = {XmlTypeCode.Item, XmlTypeCode.Node};
  294.         private static readonly XmlTypeCode[] argFnKey = {XmlTypeCode.String, XmlTypeCode.Item};
  295.         private static readonly XmlTypeCode[] argFnFormatNumber = {XmlTypeCode.Double, XmlTypeCode.String, XmlTypeCode.String};
  296.        
  297.         public static Dictionary<string, FunctionInfo> FunctionTable = CreateFunctionTable();
  298.         private static Dictionary<string, FunctionInfo> CreateFunctionTable()
  299.         {
  300.             Dictionary<string, FunctionInfo> table = new Dictionary<string, FunctionInfo>(16);
  301.             table.Add("current", new FunctionInfo(FuncId.Current, 0, 0, null));
  302.             table.Add("document", new FunctionInfo(FuncId.Document, 1, 2, argFnDocument));
  303.             table.Add("key", new FunctionInfo(FuncId.Key, 2, 2, argFnKey));
  304.             table.Add("format-number", new FunctionInfo(FuncId.FormatNumber, 2, 3, argFnFormatNumber));
  305.             table.Add("unparsed-entity-uri", new FunctionInfo(FuncId.UnparsedEntityUri, 1, 1, XPathBuilder.argString));
  306.             table.Add("generate-id", new FunctionInfo(FuncId.GenerateId, 0, 1, XPathBuilder.argNodeSet));
  307.             table.Add("system-property", new FunctionInfo(FuncId.SystemProperty, 1, 1, XPathBuilder.argString));
  308.             table.Add("element-available", new FunctionInfo(FuncId.ElementAvailable, 1, 1, XPathBuilder.argString));
  309.             table.Add("function-available", new FunctionInfo(FuncId.FunctionAvailable, 1, 1, XPathBuilder.argString));
  310.             return table;
  311.         }
  312.        
  313.         public static bool IsFunctionAvailable(string localName, string nsUri)
  314.         {
  315.             if (XPathBuilder.IsFunctionAvailable(localName, nsUri)) {
  316.                 return true;
  317.             }
  318.             if (nsUri.Length == 0) {
  319.                 return FunctionTable.ContainsKey(localName) && localName != "unparsed-entity-uri";
  320.             }
  321.             if (nsUri == XmlReservedNs.NsMsxsl) {
  322.                 return (localName == "node-set" || localName == "format-date" || localName == "format-time" || localName == "local-name" || localName == "namespace-uri" || localName == "number" || localName == "string-compare" || localName == "utc");
  323.             }
  324.             if (nsUri == XmlReservedNs.NsExsltCommon) {
  325.                 return localName == "node-set" || localName == "object-type";
  326.             }
  327.             return false;
  328.         }
  329.        
  330.         public static bool IsElementAvailable(XmlQualifiedName name)
  331.         {
  332.             if (name.Namespace == XmlReservedNs.NsXslt) {
  333.                 string localName = name.Name;
  334.                 return (localName == "apply-imports" || localName == "apply-templates" || localName == "attribute" || localName == "call-template" || localName == "choose" || localName == "comment" || localName == "copy" || localName == "copy-of" || localName == "element" || localName == "fallback" || localName == "for-each" || localName == "if" || localName == "message" || localName == "number" || localName == "processing-instruction" || localName == "text" || localName == "value-of" || localName == "variable");
  335.             }
  336.             // NOTE: msxsl:script is not an "instruction", so we return false for it
  337.             return false;
  338.         }
  339.        
  340.         private QilNode CompileFnKey(QilNode name, QilNode keys, IFocus env)
  341.         {
  342.             QilNode result;
  343.             QilIterator i;
  344.             QilIterator n;
  345.             QilIterator k;
  346.             if (keys.XmlType.IsNode) {
  347.                 if (keys.XmlType.IsSingleton) {
  348.                     result = CompileSingleKey(name, f.ConvertToString(keys), env);
  349.                 }
  350.                 else {
  351.                     result = f.Loop(i = f.For(keys), CompileSingleKey(name, f.ConvertToString(i), env));
  352.                 }
  353.             }
  354.             else if (keys.XmlType.IsAtomicValue) {
  355.                 result = CompileSingleKey(name, f.ConvertToString(keys), env);
  356.             }
  357.             else {
  358.                 result = f.Loop(n = f.Let(name), f.Loop(k = f.Let(keys), f.Conditional(f.Not(f.IsType(k, T.AnyAtomicType)), f.Loop(i = f.For(f.TypeAssert(k, T.NodeS)), CompileSingleKey(n, f.ConvertToString(i), env)), CompileSingleKey(n, f.XsltConvert(k, T.StringX), env))));
  359.             }
  360.             return f.DocOrderDistinct(result);
  361.         }
  362.        
  363.         private QilNode CompileSingleKey(QilNode name, QilNode key, IFocus env)
  364.         {
  365.             Debug.Assert(name.XmlType == T.StringX && key.XmlType == T.StringX);
  366.             QilNode result;
  367.            
  368.             if (name.NodeType == QilNodeType.LiteralString) {
  369.                 string keyName = (string)(QilLiteral)name;
  370.                 string prefix;
  371.                 string local;
  372.                 string nsUri;
  373.                
  374.                 compiler.ParseQName(keyName, out prefix, out local, new ThrowErrorHelper());
  375.                     /*ignoreDefaultNs:*/                nsUri = ResolvePrefixThrow(true, prefix);
  376.                 QilName qname = f.QName(local, nsUri, prefix);
  377.                
  378.                 if (!compiler.Keys.Contains(qname)) {
  379.                     throw new XslLoadException(Res.Xslt_UndefinedKey, keyName);
  380.                 }
  381.                 result = CompileSingleKey(compiler.Keys[qname], key, env);
  382.             }
  383.             else {
  384.                 if (generalKey == null) {
  385.                     generalKey = CreateGeneralKeyFunction();
  386.                 }
  387.                 QilIterator i = f.Let(name);
  388.                     /*ignoreDefaultNs:*/                QilNode resolvedName = ResolveQNameDynamic(true, i);
  389.                 result = f.Invoke(generalKey, f.ActualParameterList(i, resolvedName, key, env.GetCurrent()));
  390.                 result = f.Loop(i, result);
  391.             }
  392.             return result;
  393.         }
  394.        
  395.         private QilNode CompileSingleKey(List<Key> defList, QilNode key, IFocus env)
  396.         {
  397.             Debug.Assert(defList != null && defList.Count > 0);
  398.             if (defList.Count == 1) {
  399.                 return f.Invoke(defList[0].Function, f.ActualParameterList(env.GetCurrent(), key));
  400.             }
  401.            
  402.             QilIterator i = f.Let(key);
  403.             QilNode result = f.Sequence();
  404.             foreach (Key keyDef in defList) {
  405.                 result.Add(f.Invoke(keyDef.Function, f.ActualParameterList(env.GetCurrent(), i)));
  406.             }
  407.             return f.Loop(i, result);
  408.         }
  409.        
  410.         private QilNode CompileSingleKey(List<Key> defList, QilIterator key, QilIterator context)
  411.         {
  412.             Debug.Assert(defList != null && defList.Count > 0);
  413.             QilList result = f.BaseFactory.Sequence();
  414.             QilNode keyRef = null;
  415.            
  416.             foreach (Key keyDef in defList) {
  417.                 keyRef = f.Invoke(keyDef.Function, f.ActualParameterList(context, key));
  418.                 result.Add(keyRef);
  419.             }
  420.             return defList.Count == 1 ? keyRef : result;
  421.         }
  422.        
  423.         private QilFunction CreateGeneralKeyFunction()
  424.         {
  425.             QilIterator name = f.Parameter(T.StringX);
  426.             QilIterator resolvedName = f.Parameter(T.QNameX);
  427.             QilIterator key = f.Parameter(T.StringX);
  428.             QilIterator context = f.Parameter(T.NodeNotRtf);
  429.            
  430.             QilNode fdef = f.Error(Res.Xslt_UndefinedKey, name);
  431.             for (int idx = 0; idx < compiler.Keys.Count; idx++) {
  432.                 fdef = f.Conditional(f.Eq(resolvedName, compiler.Keys[idx][0].Name.DeepClone(f.BaseFactory)), CompileSingleKey(compiler.Keys[idx], key, context), fdef);
  433.             }
  434.            
  435.             QilFunction result = f.Function(f.FormalParameterList(name, resolvedName, key, context), fdef, f.False());
  436.             result.DebugName = "key";
  437.             this.functions.Add(result);
  438.             return result;
  439.         }
  440.        
  441.         private QilNode CompileFnDocument(QilNode uris, QilNode baseNode)
  442.         {
  443.             QilNode result;
  444.             QilIterator i;
  445.             QilIterator j;
  446.             QilIterator u;
  447.            
  448.             if (!compiler.Settings.EnableDocumentFunction) {
  449.                 ReportWarning(Res.Xslt_DocumentFuncProhibited);
  450.                 return f.Error(lastScope.SourceLine, Res.Xslt_DocumentFuncProhibited);
  451.             }
  452.             if (uris.XmlType.IsNode) {
  453.                 result = f.DocOrderDistinct(f.Loop(i = f.For(uris), CompileSingleDocument(f.ConvertToString(i), baseNode ?? i)));
  454.             }
  455.             else if (uris.XmlType.IsAtomicValue) {
  456.                 result = CompileSingleDocument(f.ConvertToString(uris), baseNode);
  457.             }
  458.             else {
  459.                 u = f.Let(uris);
  460.                 j = (baseNode != null) ? f.Let(baseNode) : null;
  461.                 result = f.Conditional(f.Not(f.IsType(u, T.AnyAtomicType)), f.DocOrderDistinct(f.Loop(i = f.For(f.TypeAssert(u, T.NodeS)), CompileSingleDocument(f.ConvertToString(i), j ?? i))), CompileSingleDocument(f.XsltConvert(u, T.StringX), j));
  462.                 result = (baseNode != null) ? f.Loop(j, result) : result;
  463.                 result = f.Loop(u, result);
  464.             }
  465.             return result;
  466.         }
  467.        
  468.         private QilNode CompileSingleDocument(QilNode uri, QilNode baseNode)
  469.         {
  470.             f.CheckString(uri);
  471.             QilNode baseUri;
  472.            
  473.             if (baseNode == null) {
  474.                 baseUri = f.String(lastScope.SourceLine.Uri);
  475.             }
  476.             else {
  477.                 f.CheckNodeSet(baseNode);
  478.                 if (baseNode.XmlType.IsSingleton) {
  479.                     baseUri = f.InvokeBaseUri(baseNode);
  480.                 }
  481.                 else {
  482.                     // According to errata E14, it is an error if the second argument node-set is empty
  483.                     // and the URI reference is relative. We pass an empty string as a baseUri to indicate
  484.                     // that case.
  485.                     QilIterator i;
  486.                     baseUri = f.StrConcat(f.Loop(i = f.FirstNode(baseNode), f.InvokeBaseUri(i)));
  487.                 }
  488.             }
  489.            
  490.             f.CheckString(baseUri);
  491.             return f.DataSource(uri, baseUri);
  492.         }
  493.        
  494.         private QilNode CompileFormatNumber(QilNode value, QilNode formatPicture, QilNode formatName)
  495.         {
  496.             f.CheckDouble(value);
  497.             f.CheckString(formatPicture);
  498.             XmlQualifiedName resolvedName;
  499.            
  500.             if (formatName == null) {
  501.                 resolvedName = new XmlQualifiedName();
  502.                 // formatName must be non-null in the f.InvokeFormatNumberDynamic() call below
  503.                 formatName = f.String(string.Empty);
  504.             }
  505.             else {
  506.                 f.CheckString(formatName);
  507.                 if (formatName.NodeType == QilNodeType.LiteralString) {
  508.                         /*ignoreDefaultNs:*/                    resolvedName = ResolveQNameThrow(true, formatName);
  509.                 }
  510.                 else {
  511.                     resolvedName = null;
  512.                 }
  513.             }
  514.            
  515.             if (resolvedName != null) {
  516.                 DecimalFormatDecl format;
  517.                 if (compiler.DecimalFormats.Contains(resolvedName)) {
  518.                     format = compiler.DecimalFormats[resolvedName];
  519.                 }
  520.                 else {
  521.                     if (resolvedName != DecimalFormatDecl.Default.Name) {
  522.                         throw new XslLoadException(Res.Xslt_NoDecimalFormat, (string)(QilLiteral)formatName);
  523.                     }
  524.                     format = DecimalFormatDecl.Default;
  525.                 }
  526.                
  527.                 // If both formatPicture and formatName are literal strings, there is no need to reparse
  528.                 // formatPicture on every execution of this format-number(). Instead, we create a DecimalFormatter
  529.                 // object on the first execution, save its index into a global variable, and reuse that object
  530.                 // on all subsequent executions.
  531.                 if (formatPicture.NodeType == QilNodeType.LiteralString) {
  532.                     QilIterator fmtIdx = f.Let(f.InvokeRegisterDecimalFormatter(formatPicture, format));
  533.                     fmtIdx.DebugName = f.QName("formatter" + this.formatterCnt++, XmlReservedNs.NsXslDebug).ToString();
  534.                     this.gloVars.Add(fmtIdx);
  535.                     return f.InvokeFormatNumberStatic(value, fmtIdx);
  536.                 }
  537.                
  538.                 formatNumberDynamicUsed = true;
  539.                 QilNode name = f.QName(resolvedName.Name, resolvedName.Namespace);
  540.                 return f.InvokeFormatNumberDynamic(value, formatPicture, name, formatName);
  541.             }
  542.             else {
  543.                 formatNumberDynamicUsed = true;
  544.                 QilIterator i = f.Let(formatName);
  545.                     /*ignoreDefaultNs:*/                QilNode name = ResolveQNameDynamic(true, i);
  546.                 return f.Loop(i, f.InvokeFormatNumberDynamic(value, formatPicture, name, i));
  547.             }
  548.         }
  549.        
  550.         private QilNode CompileUnparsedEntityUri(QilNode n)
  551.         {
  552.             f.CheckString(n);
  553.             return f.Error(lastScope.SourceLine, Res.Xslt_UnsupportedXsltFunction, "unparsed-entity-uri");
  554.         }
  555.        
  556.         private QilNode CompileGenerateId(QilNode n)
  557.         {
  558.             f.CheckNodeSet(n);
  559.             if (n.XmlType.IsSingleton) {
  560.                 return f.XsltGenerateId(n);
  561.             }
  562.             else {
  563.                 QilIterator i;
  564.                 return f.StrConcat(f.Loop(i = f.FirstNode(n), f.XsltGenerateId(i)));
  565.             }
  566.         }
  567.        
  568.         private XmlQualifiedName ResolveQNameThrow(bool ignoreDefaultNs, QilNode qilName)
  569.         {
  570.             string name = (string)(QilLiteral)qilName;
  571.             string prefix;
  572.             string local;
  573.             string nsUri;
  574.            
  575.             compiler.ParseQName(name, out prefix, out local, new ThrowErrorHelper());
  576.                 /*ignoreDefaultNs:*/            nsUri = ResolvePrefixThrow(ignoreDefaultNs, prefix);
  577.            
  578.             return new XmlQualifiedName(local, nsUri);
  579.         }
  580.        
  581.         private QilNode CompileSystemProperty(QilNode name)
  582.         {
  583.             f.CheckString(name);
  584.             if (name.NodeType == QilNodeType.LiteralString) {
  585.                     /*ignoreDefaultNs:*/                XmlQualifiedName qname = ResolveQNameThrow(true, name);
  586.                 if (EvaluateFuncCalls) {
  587.                     XPathItem propValue = XsltFunctions.SystemProperty(qname);
  588.                     if (propValue.ValueType == XsltConvert.StringType) {
  589.                         return f.String(propValue.Value);
  590.                     }
  591.                     else {
  592.                         Debug.Assert(propValue.ValueType == XsltConvert.DoubleType);
  593.                         return f.Double((double)propValue.ValueAsDouble);
  594.                     }
  595.                 }
  596.                 name = f.QName(qname.Name, qname.Namespace);
  597.             }
  598.             else {
  599.                     /*ignoreDefaultNs:*/                name = ResolveQNameDynamic(true, name);
  600.             }
  601.             return f.InvokeSystemProperty(name);
  602.         }
  603.        
  604.         private QilNode CompileElementAvailable(QilNode name)
  605.         {
  606.             f.CheckString(name);
  607.             if (name.NodeType == QilNodeType.LiteralString) {
  608.                     /*ignoreDefaultNs:*/                XmlQualifiedName qname = ResolveQNameThrow(false, name);
  609.                 if (EvaluateFuncCalls) {
  610.                     return f.Boolean(IsElementAvailable(qname));
  611.                 }
  612.                 name = f.QName(qname.Name, qname.Namespace);
  613.             }
  614.             else {
  615.                     /*ignoreDefaultNs:*/                name = ResolveQNameDynamic(false, name);
  616.             }
  617.             return f.InvokeElementAvailable(name);
  618.         }
  619.        
  620.         private QilNode CompileFunctionAvailable(QilNode name)
  621.         {
  622.             f.CheckString(name);
  623.             if (name.NodeType == QilNodeType.LiteralString) {
  624.                     /*ignoreDefaultNs:*/                XmlQualifiedName qname = ResolveQNameThrow(true, name);
  625.                 if (EvaluateFuncCalls) {
  626.                     // Script blocks and extension objects cannot implement neither null nor XSLT namespace
  627.                     if (qname.Namespace.Length == 0 || qname.Namespace == XmlReservedNs.NsXslt) {
  628.                         return f.Boolean(QilGenerator.IsFunctionAvailable(qname.Name, qname.Namespace));
  629.                     }
  630.                     // We might precalculate the result for script namespaces as well
  631.                 }
  632.                 name = f.QName(qname.Name, qname.Namespace);
  633.             }
  634.             else {
  635.                     /*ignoreDefaultNs:*/                name = ResolveQNameDynamic(true, name);
  636.             }
  637.             return f.InvokeFunctionAvailable(name);
  638.         }
  639.        
  640.         private QilNode CompileMsNodeSet(QilNode n)
  641.         {
  642.             if (n.XmlType.IsNode && n.XmlType.IsNotRtf) {
  643.                 return n;
  644.             }
  645.            
  646.             return f.XsltConvert(n, T.NodeDodS);
  647.         }
  648.        
  649.         //------------------------------------------------
  650.         // EXSLT Functions
  651.         //------------------------------------------------
  652.        
  653.         private QilNode EXslObjectType(QilNode n)
  654.         {
  655.             if (EvaluateFuncCalls) {
  656.                 switch (n.XmlType.TypeCode) {
  657.                     case XmlTypeCode.Boolean:
  658.                         return f.String("boolean");
  659.                     case XmlTypeCode.Double:
  660.                         return f.String("number");
  661.                     case XmlTypeCode.String:
  662.                         return f.String("string");
  663.                     default:
  664.                         if (n.XmlType.IsNode && n.XmlType.IsNotRtf) {
  665.                             return f.String("node-set");
  666.                         }
  667.                         break;
  668.                 }
  669.             }
  670.             return f.InvokeEXslObjectType(n);
  671.         }
  672.     }
  673. }

Developer Fusion