The Labs \ Source Viewer \ SSCLI \ System.Xml.Xsl.Runtime \ XmlQueryContext

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XmlQueryContext.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;
  16. using System.Collections;
  17. using System.Collections.Generic;
  18. using System.IO;
  19. using System.Xml;
  20. using System.Xml.XPath;
  21. using System.Xml.Schema;
  22. using System.Diagnostics;
  23. using System.Reflection;
  24. using System.ComponentModel;
  25. namespace System.Xml.Xsl.Runtime
  26. {
  27.     using Res = System.Xml.Utils.Res;
  28.    
  29.     /// <summary>
  30.     /// The context of a query consists of all user-provided information which influences the operation of the
  31.     /// query. The context manages the following information:
  32.     ///
  33.     /// 1. Input data sources, including the default data source if one exists
  34.     /// 2. Extension objects
  35.     /// 3. External parameters
  36.     /// </summary>
  37.     [EditorBrowsable(EditorBrowsableState.Never)]
  38.     public sealed class XmlQueryContext
  39.     {
  40.         private XmlQueryRuntime runtime;
  41.         private XPathNavigator defaultDataSource;
  42.         private XmlResolver dataSources;
  43.         private Hashtable dataSourceCache;
  44.         private XsltArgumentList argList;
  45.         private XmlExtensionFunctionTable extFuncsLate;
  46.         private WhitespaceRuleLookup wsRules;
  47.         private QueryReaderSettings readerSettings;
  48.         // If we create reader out of stream we will use these settings
  49.         /// <summary>
  50.         /// This constructor is internal so that external users cannot construct it (and therefore we do not have to test it separately).
  51.         /// </summary>
  52.         internal XmlQueryContext(XmlQueryRuntime runtime, object defaultDataSource, XmlResolver dataSources, XsltArgumentList argList, WhitespaceRuleLookup wsRules)
  53.         {
  54.             this.runtime = runtime;
  55.             this.dataSources = dataSources;
  56.             this.dataSourceCache = new Hashtable();
  57.             this.argList = argList;
  58.             this.wsRules = wsRules;
  59.            
  60.             if (defaultDataSource is XmlReader) {
  61.                 this.readerSettings = new QueryReaderSettings((XmlReader)defaultDataSource);
  62.             }
  63.             else {
  64.                 this.readerSettings = new QueryReaderSettings(new NameTable());
  65.             }
  66.            
  67.             if (defaultDataSource is string) {
  68.                 // Load the default document from a Uri
  69.                 this.defaultDataSource = GetDataSource(defaultDataSource as string, null);
  70.                
  71.                 if (this.defaultDataSource == null)
  72.                     throw new XslTransformException(Res.XmlIl_UnknownDocument, defaultDataSource as string);
  73.             }
  74.             else if (defaultDataSource != null) {
  75.                 this.defaultDataSource = ConstructDocument(defaultDataSource, null, null);
  76.             }
  77.         }
  78.        
  79.        
  80.         //-----------------------------------------------
  81.         // Input data sources
  82.         //-----------------------------------------------
  83.        
  84.         /// <summary>
  85.         /// Returns the name table that should be used in the query to atomize search names and to load
  86.         /// new documents.
  87.         /// </summary>
  88.         public XmlNameTable QueryNameTable {
  89.             get { return this.readerSettings.NameTable; }
  90.         }
  91.        
  92.         /// <summary>
  93.         /// Returns the name table used by the default data source, or null if there is no default data source.
  94.         /// </summary>
  95.         public XmlNameTable DefaultNameTable {
  96.             get { return this.defaultDataSource != null ? this.defaultDataSource.NameTable : null; }
  97.         }
  98.        
  99.         /// <summary>
  100.         /// Return the document which is queried by default--i.e. no data source is explicitly selected in the query.
  101.         /// </summary>
  102.         public XPathNavigator DefaultDataSource {
  103.             get {
  104.                 // Throw exception if there is no default data source to return
  105.                 if (this.defaultDataSource == null)
  106.                     throw new XslTransformException(Res.XmlIl_NoDefaultDocument, string.Empty);
  107.                
  108.                 return this.defaultDataSource;
  109.             }
  110.         }
  111.        
  112.         /// <summary>
  113.         /// Fetch the data source specified by "uriRelative" and "uriBase" from the XmlResolver that the user provided.
  114.         /// If the resolver returns a stream or reader, create an instance of XPathDocument. If the resolver returns an
  115.         /// XPathNavigator, return the navigator. Throw an exception if no data source was found.
  116.         /// </summary>
  117.         public XPathNavigator GetDataSource(string uriRelative, string uriBase)
  118.         {
  119.             object input;
  120.             Uri uriResolvedBase;
  121.             Uri uriResolved;
  122.             XPathNavigator nav = null;
  123.            
  124.             try {
  125.                 // If the data source has already been retrieved, then return the data source from the cache.
  126.                 uriResolvedBase = (uriBase != null) ? this.dataSources.ResolveUri(null, uriBase) : null;
  127.                 uriResolved = this.dataSources.ResolveUri(uriResolvedBase, uriRelative);
  128.                 if (uriResolved != null)
  129.                     nav = this.dataSourceCache[uriResolved] as XPathNavigator;
  130.                
  131.                 if (nav == null) {
  132.                     // Get the entity from the resolver and ensure it is cached as a document
  133.                     input = this.dataSources.GetEntity(uriResolved, null, null);
  134.                    
  135.                     if (input != null) {
  136.                         // Construct a document from the entity and add the document to the cache
  137.                         nav = ConstructDocument(input, uriRelative, uriResolved);
  138.                         this.dataSourceCache.Add(uriResolved, nav);
  139.                     }
  140.                 }
  141.             }
  142.             catch (XslTransformException) {
  143.                 // Don't need to wrap XslTransformException
  144.                 throw;
  145.             }
  146.             catch (Exception e) {
  147.                 if (!XmlException.IsCatchableException(e)) {
  148.                     throw;
  149.                 }
  150.                 throw new XslTransformException(e, Res.XmlIl_DocumentLoadError, uriRelative);
  151.             }
  152.            
  153.             return nav;
  154.         }
  155.        
  156.         /// <summary>
  157.         /// Ensure that "dataSource" is cached as an XPathDocument and return a navigator over the document.
  158.         /// </summary>
  159.         private XPathNavigator ConstructDocument(object dataSource, string uriRelative, Uri uriResolved)
  160.         {
  161.             Debug.Assert(dataSource != null, "GetType() below assumes dataSource is not null");
  162.             Stream stream = dataSource as Stream;
  163.             if (stream != null) {
  164.                 // Create document from stream
  165.                 XmlReader reader = readerSettings.CreateReader(stream, uriResolved != null ? uriResolved.ToString() : null);
  166.                
  167.                 try {
  168.                     // Create WhitespaceRuleReader if whitespace should be stripped
  169.                     return new XPathDocument(WhitespaceRuleReader.CreateReader(reader, this.wsRules), XmlSpace.Preserve).CreateNavigator();
  170.                 }
  171.                 finally {
  172.                     // Always close reader that was opened here
  173.                     reader.Close();
  174.                 }
  175.             }
  176.             else if (dataSource is XmlReader) {
  177.                 // Create document from reader
  178.                 // Create WhitespaceRuleReader if whitespace should be stripped
  179.                 return new XPathDocument(WhitespaceRuleReader.CreateReader(dataSource as XmlReader, this.wsRules), XmlSpace.Preserve).CreateNavigator();
  180.             }
  181.             else if (dataSource is IXPathNavigable) {
  182.                 if (this.wsRules != null)
  183.                     throw new XslTransformException(Res.XmlIl_CantStripNav, string.Empty);
  184.                
  185.                 return (dataSource as IXPathNavigable).CreateNavigator();
  186.             }
  187.            
  188.             Debug.Assert(uriRelative != null, "Relative URI should not be null");
  189.             throw new XslTransformException(Res.XmlIl_CantResolveEntity, uriRelative, dataSource.GetType().ToString());
  190.         }
  191.        
  192.        
  193.         //-----------------------------------------------
  194.         // External parameters
  195.         //-----------------------------------------------
  196.        
  197.         /// <summary>
  198.         /// Get a named parameter from the external argument list. Return null if no argument list was provided, or if
  199.         /// there is no parameter by that name.
  200.         /// </summary>
  201.         public object GetParameter(string localName, string namespaceUri)
  202.         {
  203.             return (this.argList != null) ? this.argList.GetParam(localName, namespaceUri) : null;
  204.         }
  205.        
  206.        
  207.         //-----------------------------------------------
  208.         // Extension objects
  209.         //-----------------------------------------------
  210.        
  211.         /// <summary>
  212.         /// Return the extension object that is mapped to the specified namespace, or null if no object is mapped.
  213.         /// </summary>
  214.         public object GetLateBoundObject(string namespaceUri)
  215.         {
  216.             return (this.argList != null) ? this.argList.GetExtensionObject(namespaceUri) : null;
  217.         }
  218.        
  219.         /// <summary>
  220.         /// Return true if the late bound object identified by "namespaceUri" contains a method that matches "name".
  221.         /// </summary>
  222.         public bool LateBoundFunctionExists(string name, string namespaceUri)
  223.         {
  224.             object instance;
  225.            
  226.             if (this.argList == null)
  227.                 return false;
  228.            
  229.             instance = this.argList.GetExtensionObject(namespaceUri);
  230.             if (instance == null)
  231.                 return false;
  232.            
  233.             return new XmlExtensionFunction(name, namespaceUri, -1, instance.GetType(), XmlQueryRuntime.LateBoundFlags).CanBind();
  234.         }
  235.        
  236.         /// <summary>
  237.         /// Get a late-bound extension object from the external argument list. Bind to a method on the object and invoke it,
  238.         /// passing "args" as arguments.
  239.         /// </summary>
  240.         public IList<XPathItem> InvokeXsltLateBoundFunction(string name, string namespaceUri, IList<XPathItem>[] args)
  241.         {
  242.             object instance;
  243.             object[] objActualArgs;
  244.             XmlQueryType xmlTypeFormalArg;
  245.             Type clrTypeFormalArg;
  246.             object objRet;
  247.            
  248.             // Get external object instance from argument list (throw if either the list or the instance doesn't exist)
  249.             instance = (this.argList != null) ? this.argList.GetExtensionObject(namespaceUri) : null;
  250.             if (instance == null)
  251.                 throw new XslTransformException(Res.Xslt_ScriptInvalidPrefix, namespaceUri);
  252.            
  253.             // Bind to a method on the instance object
  254.             if (this.extFuncsLate == null)
  255.                 this.extFuncsLate = new XmlExtensionFunctionTable();
  256.            
  257.             // Bind to the instance, looking for a matching method (throws if no matching method)
  258.             XmlExtensionFunction extFunc = this.extFuncsLate.Bind(name, namespaceUri, args.Length, instance.GetType(), XmlQueryRuntime.LateBoundFlags);
  259.            
  260.             // Create array which will contain the actual arguments
  261.             objActualArgs = new object[args.Length];
  262.            
  263.             for (int i = 0; i < args.Length; i++) {
  264.                 // 1. Assume that the input value can only have one of the following 5 Xslt types:
  265.                 // xs:double, xs:string, xs:boolean, node* (can be rtf)
  266.                 // 2. Convert each Rtf value to a NodeSet containing one node. Now the value may only have one of the 4 Xslt types.
  267.                 // 3. Convert from one of the 4 Xslt internal types to the Xslt internal type which is closest to the formal
  268.                 // argument's Xml type (inferred from the Clr type of the formal argument).
  269.                
  270.                 xmlTypeFormalArg = extFunc.GetXmlArgumentType(i);
  271.                 switch (xmlTypeFormalArg.TypeCode) {
  272.                     case XmlTypeCode.Boolean:
  273.                         objActualArgs[i] = XsltConvert.ToBoolean(args[i]);
  274.                         break;
  275.                     case XmlTypeCode.Double:
  276.                         objActualArgs[i] = XsltConvert.ToDouble(args[i]);
  277.                         break;
  278.                     case XmlTypeCode.String:
  279.                         objActualArgs[i] = XsltConvert.ToString(args[i]);
  280.                         break;
  281.                     case XmlTypeCode.Node:
  282.                         if (xmlTypeFormalArg.IsSingleton)
  283.                             objActualArgs[i] = XsltConvert.ToNode(args[i]);
  284.                         else
  285.                             objActualArgs[i] = XsltConvert.ToNodeSet(args[i]);
  286.                         break;
  287.                     case XmlTypeCode.Item:
  288.                         objActualArgs[i] = args[i];
  289.                         break;
  290.                     default:
  291.                         Debug.Fail("This XmlTypeCode should never be inferred from a Clr type: " + xmlTypeFormalArg.TypeCode);
  292.                         break;
  293.                 }
  294.                
  295.                 // 4. Change the Clr representation to the Clr type of the formal argument
  296.                 clrTypeFormalArg = extFunc.GetClrArgumentType(i);
  297.                 if (xmlTypeFormalArg.TypeCode == XmlTypeCode.Item || !clrTypeFormalArg.IsAssignableFrom(objActualArgs[i].GetType()))
  298.                     objActualArgs[i] = this.runtime.ChangeTypeXsltArgument(xmlTypeFormalArg, objActualArgs[i], clrTypeFormalArg);
  299.             }
  300.            
  301.             // 1. Invoke the late bound method
  302.             objRet = extFunc.Invoke(instance, objActualArgs);
  303.            
  304.             // 2. Convert to IList<XPathItem>
  305.             if (objRet == null && extFunc.ClrReturnType == XsltConvert.VoidType)
  306.                 return XmlQueryNodeSequence.Empty;
  307.            
  308.             return (IList<XPathItem>)this.runtime.ChangeTypeXsltResult(XmlQueryTypeFactory.ItemS, objRet);
  309.         }
  310.     }
  311. }

Developer Fusion