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

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XmlQueryRuntime.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.IO;
  17. using System.Xml;
  18. using System.Xml.XPath;
  19. using System.Xml.Schema;
  20. using System.Collections;
  21. using System.Collections.Generic;
  22. using System.Diagnostics;
  23. using System.Text;
  24. using System.Globalization;
  25. using System.Reflection;
  26. using System.Reflection.Emit;
  27. using System.Xml.Xsl.Qil;
  28. using System.Xml.Xsl.IlGen;
  29. using System.ComponentModel;
  30. using MS.Internal.Xml.XPath;
  31. namespace System.Xml.Xsl.Runtime
  32. {
  33.     using Res = System.Xml.Utils.Res;
  34.    
  35.     /// <summary>
  36.     /// XmlQueryRuntime is passed as the first parameter to all generated query methods.
  37.     ///
  38.     /// XmlQueryRuntime contains runtime support for generated ILGen queries:
  39.     /// 1. Stack of output writers (stack handles nested document construction)
  40.     /// 2. Manages list of all xml types that are used within the query
  41.     /// 3. Manages list of all atomized names that are used within the query
  42.     /// </summary>
  43.     [EditorBrowsable(EditorBrowsableState.Never)]
  44.     public sealed class XmlQueryRuntime
  45.     {
  46.         // Back-link to XmlILCommand that created this
  47.         private XmlILCommand cmd;
  48.        
  49.         // Early-Bound Library Objects
  50.         private XmlQueryContext ctxt;
  51.         private XsltLibrary xsltLib;
  52.         private EarlyBoundInfo[] earlyInfo;
  53.         private object[] earlyObjects;
  54.        
  55.         // Global variables and parameters
  56.         private string[] globalNames;
  57.         private object[] globalValues;
  58.        
  59.         // Names, prefix mappings, and name filters
  60.         private XmlNameTable nameTableQuery;
  61.         private string[] atomizedNames;
  62.         // Names after atomization
  63.         private XmlNavigatorFilter[] filters;
  64.         // Name filters (contain atomized names)
  65.         private StringPair[][] prefixMappingsList;
  66.         // Lists of prefix mappings (used to resolve computed names)
  67.         // Xml types
  68.         private XmlQueryType[] types;
  69.        
  70.         // Collations
  71.         private XmlCollation[] collations;
  72.        
  73.         // Document ordering
  74.         private DocumentOrderComparer docOrderCmp;
  75.        
  76.         // Indexes
  77.         private ArrayList[] indexes;
  78.        
  79.         // Output construction
  80.         private XmlQueryOutput output;
  81.         private Stack<XmlQueryOutput> stkOutput;
  82.        
  83.        
  84.         //-----------------------------------------------
  85.         // Constructors
  86.         //-----------------------------------------------
  87.        
  88.         /// <summary>
  89.         /// This constructor is internal so that external users cannot construct it (and therefore we do not have to test it separately).
  90.         /// </summary>
  91.         internal XmlQueryRuntime(XmlILCommand cmd, object defaultDataSource, XmlResolver dataSources, XsltArgumentList argList, XmlSequenceWriter seqWrt)
  92.         {
  93.             Debug.Assert(cmd != null, "Command object must be non-null");
  94.             string[] names = cmd.Names;
  95.             Int32Pair[] filters = cmd.Filters;
  96.             WhitespaceRuleLookup wsRules;
  97.             int i;
  98.            
  99.             this.cmd = cmd;
  100.            
  101.             // Early-Bound Library Objects
  102.             wsRules = (cmd.WhitespaceRules.Count != 0) ? new WhitespaceRuleLookup(cmd.WhitespaceRules) : null;
  103.             this.ctxt = new XmlQueryContext(this, defaultDataSource, dataSources, argList, wsRules);
  104.             this.xsltLib = null;
  105.             this.earlyInfo = cmd.EarlyBound;
  106.             this.earlyObjects = (this.earlyInfo != null) ? new object[earlyInfo.Length] : null;
  107.            
  108.             // Global variables and parameters
  109.             this.globalNames = cmd.GlobalNames;
  110.             this.globalValues = (this.globalNames != null) ? new object[this.globalNames.Length] : null;
  111.            
  112.             // Names
  113.             this.nameTableQuery = this.ctxt.QueryNameTable;
  114.             this.atomizedNames = null;
  115.            
  116.             if (names != null) {
  117.                 // Atomize all names in "nameTableQuery". Use names from the default data source's
  118.                 // name table when possible.
  119.                 XmlNameTable nameTableDefault = ctxt.DefaultNameTable;
  120.                 this.atomizedNames = new string[names.Length];
  121.                
  122.                 if (nameTableDefault != this.nameTableQuery && nameTableDefault != null) {
  123.                     // Ensure that atomized names from the default data source are added to the
  124.                     // name table used in this query
  125.                     for (i = 0; i < names.Length; i++) {
  126.                         string name = nameTableDefault.Get(names[i]);
  127.                         this.atomizedNames[i] = this.nameTableQuery.Add(name ?? names[i]);
  128.                     }
  129.                 }
  130.                 else {
  131.                     // Enter names into nametable used in this query
  132.                     for (i = 0; i < names.Length; i++)
  133.                         this.atomizedNames[i] = this.nameTableQuery.Add(names[i]);
  134.                 }
  135.             }
  136.            
  137.             // Name filters
  138.             this.filters = null;
  139.             if (filters != null) {
  140.                 // Construct name filters. Each pair of integers in the filters[] array specifies the
  141.                 // (localName, namespaceUri) of the NameFilter to be created.
  142.                 this.filters = new XmlNavigatorFilter[filters.Length];
  143.                
  144.                 for (i = 0; i < filters.Length; i++)
  145.                     this.filters[i] = XmlNavNameFilter.Create(this.atomizedNames[filters[i].Left], this.atomizedNames[filters[i].Right]);
  146.             }
  147.            
  148.             // Prefix maping lists
  149.             this.prefixMappingsList = cmd.PrefixMappingsList;
  150.            
  151.             // Xml types
  152.             this.types = cmd.Types;
  153.            
  154.             // Xml collations
  155.             this.collations = cmd.Collations;
  156.            
  157.             // Document ordering
  158.             this.docOrderCmp = new DocumentOrderComparer();
  159.            
  160.             // Indexes
  161.             this.indexes = null;
  162.            
  163.             // Output construction
  164.             this.stkOutput = new Stack<XmlQueryOutput>(16);
  165.             this.output = new XmlQueryOutput(this, seqWrt);
  166.         }
  167.        
  168.        
  169.         //-----------------------------------------------
  170.         // Debugger Utility Methods
  171.         //-----------------------------------------------
  172.        
  173.         /// <summary>
  174.         /// Return array containing the names of all the global variables and parameters used in this query, in this format:
  175.         /// {namespace}prefix:local-name
  176.         /// </summary>
  177.         public string[] DebugGetGlobalNames()
  178.         {
  179.             return this.globalNames;
  180.         }
  181.        
  182.         /// <summary>
  183.         /// Get the value of a global value having the specified name. Always return the global value as a list of XPathItem.
  184.         /// Return null if there is no global value having the specified name.
  185.         /// </summary>
  186.         public IList DebugGetGlobalValue(string name)
  187.         {
  188.             for (int idx = 0; idx < this.globalNames.Length; idx++) {
  189.                 if (this.globalNames[idx] == name) {
  190.                     Debug.Assert(IsGlobalComputed(idx), "Cannot get the value of a global value until it has been computed.");
  191.                     Debug.Assert(this.globalValues[idx] is IList<XPathItem>, "Only debugger should call this method, and all global values should have type item* in debugging scenarios.");
  192.                     return (IList)this.globalValues[idx];
  193.                 }
  194.             }
  195.             return null;
  196.         }
  197.        
  198.         /// <summary>
  199.         /// Set the value of a global value having the specified name. If there is no such value, this method is a no-op.
  200.         /// </summary>
  201.         public void DebugSetGlobalValue(string name, object value)
  202.         {
  203.             for (int idx = 0; idx < this.globalNames.Length; idx++) {
  204.                 if (this.globalNames[idx] == name) {
  205.                     Debug.Assert(IsGlobalComputed(idx), "Cannot get the value of a global value until it has been computed.");
  206.                     Debug.Assert(this.globalValues[idx] is IList<XPathItem>, "Only debugger should call this method, and all global values should have type item* in debugging scenarios.");
  207.                    
  208.                     // Always convert "value" to a list of XPathItem using the item* converter
  209.                     this.globalValues[idx] = (IList<XPathItem>)XmlAnyListConverter.ItemList.ChangeType(value, typeof(XPathItem[]), null);
  210.                     break;
  211.                 }
  212.             }
  213.         }
  214.        
  215.         /// <summary>
  216.         /// Convert sequence to it's appropriate XSLT type and return to caller.
  217.         /// </summary>
  218.         public object DebugGetXsltValue(IList seq)
  219.         {
  220.             if (seq != null && seq.Count == 1) {
  221.                 XPathItem item = seq[0] as XPathItem;
  222.                 if (item != null && !item.IsNode) {
  223.                     return item.TypedValue;
  224.                 }
  225.                 else if (item is RtfNavigator) {
  226.                     return ((RtfNavigator)item).ToNavigator();
  227.                 }
  228.             }
  229.            
  230.             return seq;
  231.         }
  232.        
  233.        
  234.         //-----------------------------------------------
  235.         // Early-Bound Library Objects
  236.         //-----------------------------------------------
  237.        
  238.         internal const BindingFlags EarlyBoundFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
  239.         internal const BindingFlags LateBoundFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
  240.        
  241.         /// <summary>
  242.         /// Return the object that manages external user context information such as data sources, parameters, extension objects, etc.
  243.         /// </summary>
  244.         public XmlQueryContext ExternalContext {
  245.             get { return this.ctxt; }
  246.         }
  247.        
  248.         /// <summary>
  249.         /// Return the object that manages the state needed to implement various Xslt functions.
  250.         /// </summary>
  251.         public XsltLibrary XsltFunctions {
  252.             get {
  253.                 if (this.xsltLib == null) {
  254.                     this.xsltLib = new XsltLibrary(this);
  255.                 }
  256.                
  257.                 return this.xsltLib;
  258.             }
  259.         }
  260.        
  261.         /// <summary>
  262.         /// Get the early-bound extension object identified by "index". If it does not yet exist, create an instance using the
  263.         /// corresponding ConstructorInfo.
  264.         /// </summary>
  265.         public object GetEarlyBoundObject(int index)
  266.         {
  267.             object obj;
  268.             Debug.Assert(this.earlyObjects != null && index < this.earlyObjects.Length, "Early bound object does not exist");
  269.            
  270.             obj = this.earlyObjects[index];
  271.             if (obj == null) {
  272.                 // Early-bound object does not yet exist, so create it now
  273.                 obj = this.earlyInfo[index].CreateObject();
  274.                 this.earlyObjects[index] = obj;
  275.             }
  276.            
  277.             return obj;
  278.         }
  279.        
  280.         /// <summary>
  281.         /// Return true if the early bound object identified by "namespaceUri" contains a method that matches "name".
  282.         /// </summary>
  283.         public bool EarlyBoundFunctionExists(string name, string namespaceUri)
  284.         {
  285.             if (this.earlyInfo == null)
  286.                 return false;
  287.            
  288.             for (int idx = 0; idx < this.earlyInfo.Length; idx++) {
  289.                 if (namespaceUri == this.earlyInfo[idx].NamespaceUri)
  290.                     return new XmlExtensionFunction(name, namespaceUri, -1, this.earlyInfo[idx].EarlyBoundType, EarlyBoundFlags).CanBind();
  291.             }
  292.            
  293.             return false;
  294.         }
  295.        
  296.        
  297.         //-----------------------------------------------
  298.         // Global variables and parameters
  299.         //-----------------------------------------------
  300.        
  301.         /// <summary>
  302.         /// Return true if the global value specified by idxValue was previously computed.
  303.         /// </summary>
  304.         public bool IsGlobalComputed(int index)
  305.         {
  306.             return this.globalValues[index] != null;
  307.         }
  308.        
  309.         /// <summary>
  310.         /// Return the value that is bound to the global variable or parameter specified by idxValue.
  311.         /// If the value has not yet been computed, then compute it now and store it in this.globalValues.
  312.         /// </summary>
  313.         public object GetGlobalValue(int index)
  314.         {
  315.             Debug.Assert(IsGlobalComputed(index), "Cannot get the value of a global value until it has been computed.");
  316.             return this.globalValues[index];
  317.         }
  318.        
  319.         /// <summary>
  320.         /// Return the value that is bound to the global variable or parameter specified by idxValue.
  321.         /// If the value has not yet been computed, then compute it now and store it in this.globalValues.
  322.         /// </summary>
  323.         public void SetGlobalValue(int index, object value)
  324.         {
  325.             Debug.Assert(!IsGlobalComputed(index), "Global value should only be set once.");
  326.             this.globalValues[index] = value;
  327.         }
  328.        
  329.        
  330.         //-----------------------------------------------
  331.         // Names, prefix mappings, and name filters
  332.         //-----------------------------------------------
  333.        
  334.         /// <summary>
  335.         /// Return the name table used to atomize all names used by the query.
  336.         /// </summary>
  337.         public XmlNameTable NameTable {
  338.             get { return this.nameTableQuery; }
  339.         }
  340.        
  341.         /// <summary>
  342.         /// Get the atomized name at the specified index in the array of names.
  343.         /// </summary>
  344.         public string GetAtomizedName(int index)
  345.         {
  346.             Debug.Assert(this.atomizedNames != null);
  347.             return this.atomizedNames[index];
  348.         }
  349.        
  350.         /// <summary>
  351.         /// Get the name filter at the specified index in the array of filters.
  352.         /// </summary>
  353.         public XmlNavigatorFilter GetNameFilter(int index)
  354.         {
  355.             Debug.Assert(this.filters != null);
  356.             return this.filters[index];
  357.         }
  358.        
  359.         /// <summary>
  360.         /// XPathNodeType.All: Filters all nodes
  361.         /// XPathNodeType.Attribute: Filters attributes
  362.         /// XPathNodeType.Namespace: Not allowed
  363.         /// XPathNodeType.XXX: Filters all nodes *except* those having XPathNodeType.XXX
  364.         /// </summary>
  365.         public XmlNavigatorFilter GetTypeFilter(XPathNodeType nodeType)
  366.         {
  367.             if (nodeType == XPathNodeType.All)
  368.                 return XmlNavNeverFilter.Create();
  369.            
  370.             if (nodeType == XPathNodeType.Attribute)
  371.                 return XmlNavAttrFilter.Create();
  372.            
  373.             return XmlNavTypeFilter.Create(nodeType);
  374.         }
  375.        
  376.         /// <summary>
  377.         /// Parse the specified tag name (foo:bar) and resolve the resulting prefix. If the prefix cannot be resolved,
  378.         /// then throw an error. Return an XmlQualifiedName.
  379.         /// </summary>
  380.         public XmlQualifiedName ParseTagName(string tagName, int indexPrefixMappings)
  381.         {
  382.             string prefix;
  383.             string localName;
  384.             string ns;
  385.            
  386.             // Parse the tagName as a prefix, localName pair and resolve the prefix
  387.             ParseTagName(tagName, indexPrefixMappings, out prefix, out localName, out ns);
  388.             return new XmlQualifiedName(localName, ns);
  389.         }
  390.        
  391.         /// <summary>
  392.         /// Parse the specified tag name (foo:bar). Return an XmlQualifiedName consisting of the parsed local name
  393.         /// and the specified namespace.
  394.         /// </summary>
  395.         public XmlQualifiedName ParseTagName(string tagName, string ns)
  396.         {
  397.             string prefix;
  398.             string localName;
  399.            
  400.             // Parse the tagName as a prefix, localName pair
  401.             ValidateNames.ParseQNameThrow(tagName, out prefix, out localName);
  402.             return new XmlQualifiedName(localName, ns);
  403.         }
  404.        
  405.         /// <summary>
  406.         /// Parse the specified tag name (foo:bar) and resolve the resulting prefix. If the prefix cannot be resolved,
  407.         /// then throw an error. Return the prefix, localName, and namespace URI.
  408.         /// </summary>
  409.         internal void ParseTagName(string tagName, int idxPrefixMappings, out string prefix, out string localName, out string ns)
  410.         {
  411.             Debug.Assert(this.prefixMappingsList != null);
  412.            
  413.             // Parse the tagName as a prefix, localName pair
  414.             ValidateNames.ParseQNameThrow(tagName, out prefix, out localName);
  415.            
  416.             // Map the prefix to a namespace URI
  417.             ns = null;
  418.             foreach (StringPair pair in this.prefixMappingsList[idxPrefixMappings]) {
  419.                 if (prefix == pair.Left) {
  420.                     ns = pair.Right;
  421.                     break;
  422.                 }
  423.             }
  424.            
  425.             // Throw exception if prefix could not be resolved
  426.             if (ns == null) {
  427.                 // Check for mappings that are always in-scope
  428.                 if (prefix.Length == 0)
  429.                     ns = "";
  430.                 else if (prefix.Equals("xml"))
  431.                     ns = XmlReservedNs.NsXml;
  432.                 else if (prefix.Equals("xmlns"))
  433.                     ns = XmlReservedNs.NsXmlNs;
  434.                 else
  435.                     throw new XslTransformException(Res.Xslt_InvalidPrefix, prefix);
  436.             }
  437.         }
  438.        
  439.         /// <summary>
  440.         /// Return true if the nav1's LocalName and NamespaceURI properties equal nav2's corresponding properties.
  441.         /// </summary>
  442.         public bool IsQNameEqual(XPathNavigator n1, XPathNavigator n2)
  443.         {
  444.             if ((object)n1.NameTable == (object)n2.NameTable) {
  445.                 // Use atomized comparison
  446.                 return (object)n1.LocalName == (object)n2.LocalName && (object)n1.NamespaceURI == (object)n2.NamespaceURI;
  447.             }
  448.            
  449.             return (n1.LocalName == n2.LocalName) && (n1.NamespaceURI == n2.NamespaceURI);
  450.         }
  451.        
  452.         /// <summary>
  453.         /// Return true if the specified navigator's LocalName and NamespaceURI properties equal the argument names.
  454.         /// </summary>
  455.         public bool IsQNameEqual(XPathNavigator navigator, int indexLocalName, int indexNamespaceUri)
  456.         {
  457.             if ((object)navigator.NameTable == (object)this.nameTableQuery) {
  458.                 // Use atomized comparison
  459.                 return ((object)GetAtomizedName(indexLocalName) == (object)navigator.LocalName && (object)GetAtomizedName(indexNamespaceUri) == (object)navigator.NamespaceURI);
  460.             }
  461.            
  462.             // Use string comparison
  463.             return (GetAtomizedName(indexLocalName) == navigator.LocalName) && (GetAtomizedName(indexNamespaceUri) == navigator.NamespaceURI);
  464.         }
  465.        
  466.        
  467.         //-----------------------------------------------
  468.         // Xml types
  469.         //-----------------------------------------------
  470.        
  471.         /// <summary>
  472.         /// Get the array of xml types that are used within this query.
  473.         /// </summary>
  474.         internal XmlQueryType[] XmlTypes {
  475.             get { return this.types; }
  476.         }
  477.        
  478.         /// <summary>
  479.         /// Get the Xml query type at the specified index in the array of types.
  480.         /// </summary>
  481.         internal XmlQueryType GetXmlType(int idxType)
  482.         {
  483.             Debug.Assert(this.types != null);
  484.             return this.types[idxType];
  485.         }
  486.        
  487.         /// <summary>
  488.         /// Forward call to ChangeTypeXsltArgument(XmlQueryType, object, Type).
  489.         /// </summary>
  490.         public object ChangeTypeXsltArgument(int indexType, object value, Type destinationType)
  491.         {
  492.             return ChangeTypeXsltArgument(GetXmlType(indexType), value, destinationType);
  493.         }
  494.        
  495.         /// <summary>
  496.         /// Convert from the Clr type of "value" to Clr type "destinationType" using V1 Xslt rules.
  497.         /// These rules include converting any Rtf values to Nodes.
  498.         /// </summary>
  499.         internal object ChangeTypeXsltArgument(XmlQueryType xmlType, object value, Type destinationType)
  500.         {
  501.             Debug.Assert(XmlILTypeHelper.GetStorageType(xmlType).IsAssignableFrom(value.GetType()), "Values passed to ChangeTypeXsltArgument should be in ILGen's default Clr representation.");
  502.             Debug.Assert(destinationType == XsltConvert.ObjectType || !destinationType.IsAssignableFrom(value.GetType()), "No need to call ChangeTypeXsltArgument since value is already assignable to destinationType " + destinationType);
  503.            
  504.             switch (xmlType.TypeCode) {
  505.                 case XmlTypeCode.String:
  506.                     if (destinationType == XsltConvert.DateTimeType)
  507.                         value = XsltConvert.ToDateTime((string)value);
  508.                     break;
  509.                 case XmlTypeCode.Double:
  510.                    
  511.                     if (destinationType != XsltConvert.DoubleType)
  512.                         value = Convert.ChangeType(value, destinationType, CultureInfo.InvariantCulture);
  513.                     break;
  514.                 case XmlTypeCode.Node:
  515.                    
  516.                     Debug.Assert(xmlType != XmlQueryTypeFactory.Node && xmlType != XmlQueryTypeFactory.NodeS, "Rtf values should have been eliminated by caller.");
  517.                    
  518.                     if (destinationType == XsltConvert.XPathNodeIteratorType) {
  519.                         value = new XPathArrayIterator((IList)value);
  520.                     }
  521.                     else if (destinationType == XsltConvert.XPathNavigatorArrayType) {
  522.                         // Copy sequence to XPathNavigator[]
  523.                         IList<XPathNavigator> seq = (IList<XPathNavigator>)value;
  524.                         XPathNavigator[] navArray = new XPathNavigator[seq.Count];
  525.                        
  526.                         for (int i = 0; i < seq.Count; i++)
  527.                             navArray[i] = seq[i];
  528.                        
  529.                         value = navArray;
  530.                     }
  531.                     break;
  532.                 case XmlTypeCode.Item:
  533.                    
  534.                    
  535.                     {
  536.                         // Only typeof(object) is supported as a destination type
  537.                         if (destinationType != XsltConvert.ObjectType)
  538.                             throw new XslTransformException(Res.Xslt_UnsupportedClrType, destinationType.Name);
  539.                        
  540.                         // Convert to default, backwards-compatible representation
  541.                         // 1. NodeSet: System.Xml.XPath.XPathNodeIterator
  542.                         // 2. Rtf: System.Xml.XPath.XPathNavigator
  543.                         // 3. Other: Default V1 representation
  544.                         IList<XPathItem> seq = (IList<XPathItem>)value;
  545.                         if (seq.Count == 1) {
  546.                             XPathItem item = seq[0];
  547.                            
  548.                             if (item.IsNode) {
  549.                                 // Node or Rtf
  550.                                 RtfNavigator rtf = item as RtfNavigator;
  551.                                 if (rtf != null)
  552.                                     value = rtf.ToNavigator();
  553.                                 else
  554.                                     value = new XPathArrayIterator((IList)value);
  555.                             }
  556.                             else {
  557.                                 // Atomic value
  558.                                 value = item.TypedValue;
  559.                             }
  560.                         }
  561.                         else {
  562.                             // Nodeset
  563.                             value = new XPathArrayIterator((IList)value);
  564.                         }
  565.                         break;
  566.                     }
  567.                     break;
  568.             }
  569.            
  570.             Debug.Assert(destinationType.IsAssignableFrom(value.GetType()), "ChangeType from type " + value.GetType().Name + " to type " + destinationType.Name + " failed");
  571.             return value;
  572.         }
  573.        
  574.         /// <summary>
  575.         /// Forward call to ChangeTypeXsltResult(XmlQueryType, object)
  576.         /// </summary>
  577.         public object ChangeTypeXsltResult(int indexType, object value)
  578.         {
  579.             return ChangeTypeXsltResult(GetXmlType(indexType), value);
  580.         }
  581.        
  582.         /// <summary>
  583.         /// Convert from the Clr type of "value" to the default Clr type that ILGen uses to represent the xml type, using
  584.         /// the conversion rules of the xml type.
  585.         /// </summary>
  586.         internal object ChangeTypeXsltResult(XmlQueryType xmlType, object value)
  587.         {
  588.             if (value == null)
  589.                 throw new XslTransformException(Res.Xslt_ItemNull, string.Empty);
  590.            
  591.             switch (xmlType.TypeCode) {
  592.                 case XmlTypeCode.String:
  593.                     if (value.GetType() == XsltConvert.DateTimeType)
  594.                         value = XsltConvert.ToString((DateTime)value);
  595.                     break;
  596.                 case XmlTypeCode.Double:
  597.                    
  598.                     if (value.GetType() != XsltConvert.DoubleType)
  599.                         value = ((IConvertible)value).ToDouble(null);
  600.                    
  601.                     break;
  602.                 case XmlTypeCode.Node:
  603.                    
  604.                     if (!xmlType.IsSingleton) {
  605.                         XPathArrayIterator iter = value as XPathArrayIterator;
  606.                        
  607.                         // Special-case XPathArrayIterator in order to avoid copies
  608.                         if (iter != null && iter.AsList is XmlQueryNodeSequence) {
  609.                             value = iter.AsList as XmlQueryNodeSequence;
  610.                         }
  611.                         else {
  612.                             // Iterate over list and ensure it only contains nodes
  613.                             XmlQueryNodeSequence seq = new XmlQueryNodeSequence();
  614.                             IList list = value as IList;
  615.                            
  616.                             if (list != null) {
  617.                                 for (int i = 0; i < list.Count; i++)
  618.                                     seq.Add(EnsureNavigator(list[i]));
  619.                             }
  620.                             else {
  621.                                 foreach (object o in (IEnumerable)value)
  622.                                     seq.Add(EnsureNavigator(o));
  623.                             }
  624.                            
  625.                             value = seq;
  626.                         }
  627.                        
  628.                         // Always sort node-set by document order
  629.                         value = ((XmlQueryNodeSequence)value).DocOrderDistinct(this.docOrderCmp);
  630.                     }
  631.                     break;
  632.                 case XmlTypeCode.Item:
  633.                    
  634.                    
  635.                     {
  636.                         Type sourceType = value.GetType();
  637.                         IXPathNavigable navigable;
  638.                        
  639.                         // If static type is item, then infer type based on dynamic value
  640.                         switch (XsltConvert.InferXsltType(sourceType).TypeCode) {
  641.                             case XmlTypeCode.Boolean:
  642.                                 value = new XmlQueryItemSequence(new XmlAtomicValue(XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.Boolean), value));
  643.                                 break;
  644.                             case XmlTypeCode.Double:
  645.                                
  646.                                 value = new XmlQueryItemSequence(new XmlAtomicValue(XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.Double), ((IConvertible)value).ToDouble(null)));
  647.                                 break;
  648.                             case XmlTypeCode.String:
  649.                                
  650.                                 if (sourceType == XsltConvert.DateTimeType)
  651.                                     value = new XmlQueryItemSequence(new XmlAtomicValue(XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.String), XsltConvert.ToString((DateTime)value)));
  652.                                 else
  653.                                     value = new XmlQueryItemSequence(new XmlAtomicValue(XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.String), value));
  654.                                 break;
  655.                             case XmlTypeCode.Node:
  656.                                
  657.                                 // Support XPathNavigator[]
  658.                                 value = ChangeTypeXsltResult(XmlQueryTypeFactory.NodeS, value);
  659.                                 break;
  660.                             case XmlTypeCode.Item:
  661.                                
  662.                                 // Support XPathNodeIterator
  663.                                 if (value is XPathNodeIterator) {
  664.                                     value = ChangeTypeXsltResult(XmlQueryTypeFactory.NodeS, value);
  665.                                     break;
  666.                                 }
  667.                                
  668.                                 // Support IXPathNavigable and XPathNavigator
  669.                                 navigable = value as IXPathNavigable;
  670.                                 if (navigable != null) {
  671.                                     if (value is XPathNavigator)
  672.                                         value = new XmlQueryNodeSequence((XPathNavigator)value);
  673.                                     else
  674.                                         value = new XmlQueryNodeSequence(navigable.CreateNavigator());
  675.                                     break;
  676.                                 }
  677.                                
  678.                                 throw new XslTransformException(Res.Xslt_UnsupportedClrType, sourceType.Name);
  679.                                 break;
  680.                         }
  681.                         break;
  682.                     }
  683.                     break;
  684.             }
  685.            
  686.             Debug.Assert(XmlILTypeHelper.GetStorageType(xmlType).IsAssignableFrom(value.GetType()), "Xml type " + xmlType + " is not represented in ILGen as " + value.GetType().Name);
  687.             return value;
  688.         }
  689.        
  690.         /// <summary>
  691.         /// Ensure that "value" is a navigator and not null.
  692.         /// </summary>
  693.         private static XPathNavigator EnsureNavigator(object value)
  694.         {
  695.             XPathNavigator nav = value as XPathNavigator;
  696.            
  697.             if (nav == null)
  698.                 throw new XslTransformException(Res.Xslt_ItemNull, string.Empty);
  699.            
  700.             return nav;
  701.         }
  702.        
  703.         /// <summary>
  704.         /// Return true if the type of every item in "seq" matches the xml type identified by "idxType".
  705.         /// </summary>
  706.         public bool MatchesXmlType(IList<XPathItem> seq, int indexType)
  707.         {
  708.             XmlQueryType typBase = GetXmlType(indexType);
  709.             XmlQueryCardinality card;
  710.            
  711.             switch (seq.Count) {
  712.                 case 0:
  713.                     card = XmlQueryCardinality.Zero;
  714.                     break;
  715.                 case 1:
  716.                     card = XmlQueryCardinality.One;
  717.                     break;
  718.                 default:
  719.                     card = XmlQueryCardinality.More;
  720.                     break;
  721.             }
  722.            
  723.             if (!(card <= typBase.Cardinality))
  724.                 return false;
  725.            
  726.             typBase = typBase.Prime;
  727.             for (int i = 0; i < seq.Count; i++) {
  728.                 if (!CreateXmlType(seq[0]).IsSubtypeOf(typBase))
  729.                     return false;
  730.             }
  731.            
  732.             return true;
  733.         }
  734.        
  735.         /// <summary>
  736.         /// Return true if the type of "item" matches the xml type identified by "idxType".
  737.         /// </summary>
  738.         public bool MatchesXmlType(XPathItem item, int indexType)
  739.         {
  740.             return CreateXmlType(item).IsSubtypeOf(GetXmlType(indexType));
  741.         }
  742.        
  743.         /// <summary>
  744.         /// Return true if the type of "seq" is a subtype of a singleton type identified by "code".
  745.         /// </summary>
  746.         public bool MatchesXmlType(IList<XPathItem> seq, XmlTypeCode code)
  747.         {
  748.             if (seq.Count != 1)
  749.                 return false;
  750.            
  751.             return MatchesXmlType(seq[0], code);
  752.         }
  753.        
  754.         /// <summary>
  755.         /// Return true if the type of "item" is a subtype of the type identified by "code".
  756.         /// </summary>
  757.         public bool MatchesXmlType(XPathItem item, XmlTypeCode code)
  758.         {
  759.             // All atomic type codes appear after AnyAtomicType
  760.             if (code > XmlTypeCode.AnyAtomicType)
  761.                 return !item.IsNode && item.XmlType.TypeCode == code;
  762.            
  763.             // Handle node code and AnyAtomicType
  764.             switch (code) {
  765.                 case XmlTypeCode.AnyAtomicType:
  766.                     return !item.IsNode;
  767.                 case XmlTypeCode.Node:
  768.                     return item.IsNode;
  769.                 case XmlTypeCode.Item:
  770.                     return true;
  771.                 default:
  772.                     if (!item.IsNode)
  773.                         return false;
  774.                    
  775.                     switch (((XPathNavigator)item).NodeType) {
  776.                         case XPathNodeType.Root:
  777.                             return code == XmlTypeCode.Document;
  778.                         case XPathNodeType.Element:
  779.                             return code == XmlTypeCode.Element;
  780.                         case XPathNodeType.Attribute:
  781.                             return code == XmlTypeCode.Attribute;
  782.                         case XPathNodeType.Namespace:
  783.                             return code == XmlTypeCode.Namespace;
  784.                         case XPathNodeType.Text:
  785.                             return code == XmlTypeCode.Text;
  786.                         case XPathNodeType.SignificantWhitespace:
  787.                             return code == XmlTypeCode.Text;
  788.                         case XPathNodeType.Whitespace:
  789.                             return code == XmlTypeCode.Text;
  790.                         case XPathNodeType.ProcessingInstruction:
  791.                             return code == XmlTypeCode.ProcessingInstruction;
  792.                         case XPathNodeType.Comment:
  793.                             return code == XmlTypeCode.Comment;
  794.                     }
  795.                     break;
  796.             }
  797.            
  798.             Debug.Fail("XmlTypeCode " + code + " was not fully handled.");
  799.             return false;
  800.         }
  801.        
  802.         /// <summary>
  803.         /// Create an XmlQueryType that represents the type of "item".
  804.         /// </summary>
  805.         private XmlQueryType CreateXmlType(XPathItem item)
  806.         {
  807.             if (item.IsNode) {
  808.                 // Rtf
  809.                 RtfNavigator rtf = item as RtfNavigator;
  810.                 if (rtf != null)
  811.                     return XmlQueryTypeFactory.Node;
  812.                
  813.                 // Node
  814.                 XPathNavigator nav = (XPathNavigator)item;
  815.                 switch (nav.NodeType) {
  816.                     case XPathNodeType.Root:
  817.                     case XPathNodeType.Element:
  818.                         if (nav.XmlType == null)
  819.                             return XmlQueryTypeFactory.Type(nav.NodeType, XmlQualifiedNameTest.New(nav.LocalName, nav.NamespaceURI), XmlSchemaComplexType.UntypedAnyType, false);
  820.                        
  821.                         return XmlQueryTypeFactory.Type(nav.NodeType, XmlQualifiedNameTest.New(nav.LocalName, nav.NamespaceURI), nav.XmlType, nav.SchemaInfo.SchemaElement.IsNillable);
  822.                     case XPathNodeType.Attribute:
  823.                        
  824.                         if (nav.XmlType == null)
  825.                             return XmlQueryTypeFactory.Type(nav.NodeType, XmlQualifiedNameTest.New(nav.LocalName, nav.NamespaceURI), DatatypeImplementation.UntypedAtomicType, false);
  826.                        
  827.                         return XmlQueryTypeFactory.Type(nav.NodeType, XmlQualifiedNameTest.New(nav.LocalName, nav.NamespaceURI), nav.XmlType, false);
  828.                 }
  829.                
  830.                 return XmlQueryTypeFactory.Type(nav.NodeType, XmlQualifiedNameTest.Wildcard, XmlSchemaComplexType.AnyType, false);
  831.             }
  832.            
  833.             // Atomic value
  834.             return XmlQueryTypeFactory.Type((XmlSchemaSimpleType)item.XmlType, true);
  835.         }
  836.        
  837.        
  838.         //-----------------------------------------------
  839.         // Xml collations
  840.         //-----------------------------------------------
  841.        
  842.         /// <summary>
  843.         /// Get a collation that was statically created.
  844.         /// </summary>
  845.         public XmlCollation GetCollation(int index)
  846.         {
  847.             Debug.Assert(this.collations != null);
  848.             return this.collations[index];
  849.         }
  850.        
  851.         /// <summary>
  852.         /// Create a collation from a string.
  853.         /// </summary>
  854.         public XmlCollation CreateCollation(string collation)
  855.         {
  856.             return XmlCollation.Create(collation);
  857.         }
  858.        
  859.        
  860.         //-----------------------------------------------
  861.         // Document Ordering and Identity
  862.         //-----------------------------------------------
  863.        
  864.         /// <summary>
  865.         /// Compare the relative positions of two navigators. Return -1 if navThis is before navThat, 1 if after, and
  866.         /// 0 if they are positioned to the same node.
  867.         /// </summary>
  868.         public int ComparePosition(XPathNavigator navigatorThis, XPathNavigator navigatorThat)
  869.         {
  870.             return this.docOrderCmp.Compare(navigatorThis, navigatorThat);
  871.         }
  872.        
  873.         /// <summary>
  874.         /// Get a comparer which guarantees a stable ordering among nodes, even those from different documents.
  875.         /// </summary>
  876.         public IList<XPathNavigator> DocOrderDistinct(IList<XPathNavigator> seq)
  877.         {
  878.             if (seq.Count <= 1)
  879.                 return seq;
  880.            
  881.             XmlQueryNodeSequence nodeSeq = (XmlQueryNodeSequence)seq;
  882.             if (nodeSeq == null)
  883.                 nodeSeq = new XmlQueryNodeSequence(seq);
  884.            
  885.             return nodeSeq.DocOrderDistinct(this.docOrderCmp);
  886.         }
  887.        
  888.         /// <summary>
  889.         /// Generate a unique string identifier for the specified node. Do this by asking the navigator for an identifier
  890.         /// that is unique within the document, and then prepend a document index.
  891.         /// </summary>
  892.         public string GenerateId(XPathNavigator navigator)
  893.         {
  894.             return string.Concat("ID", this.docOrderCmp.GetDocumentIndex(navigator).ToString(CultureInfo.InvariantCulture), navigator.UniqueId);
  895.         }
  896.        
  897.        
  898.         //-----------------------------------------------
  899.         // Indexes
  900.         //-----------------------------------------------
  901.        
  902.         /// <summary>
  903.         /// If an index having the specified Id has already been created over the "context" document, then return it
  904.         /// in "index" and return true. Otherwise, create a new, empty index and return false.
  905.         /// </summary>
  906.         public bool FindIndex(XPathNavigator context, int indexId, out XmlILIndex index)
  907.         {
  908.             XPathNavigator navRoot;
  909.             ArrayList docIndexes;
  910.             Debug.Assert(context != null);
  911.            
  912.             // Get root of document
  913.             navRoot = context.Clone();
  914.             navRoot.MoveToRoot();
  915.            
  916.             // Search pre-existing indexes in order to determine whether the specified index has already been created
  917.             if (this.indexes != null && indexId < this.indexes.Length) {
  918.                 docIndexes = (ArrayList)this.indexes[indexId];
  919.                 if (docIndexes != null) {
  920.                     // Search for an index defined over the specified document
  921.                     for (int i = 0; i < docIndexes.Count; i += 2) {
  922.                         // If we find a matching document, then return the index saved in the next slot
  923.                         if (((XPathNavigator)docIndexes[i]).IsSamePosition(navRoot)) {
  924.                             index = (XmlILIndex)docIndexes[i + 1];
  925.                             return true;
  926.                         }
  927.                     }
  928.                 }
  929.             }
  930.            
  931.             // Return a new, empty index
  932.             index = new XmlILIndex();
  933.             return false;
  934.         }
  935.        
  936.         /// <summary>
  937.         /// Add a newly built index over the specified "context" document to the existing collection of indexes.
  938.         /// </summary>
  939.         public void AddNewIndex(XPathNavigator context, int indexId, XmlILIndex index)
  940.         {
  941.             XPathNavigator navRoot;
  942.             ArrayList docIndexes;
  943.             Debug.Assert(context != null);
  944.            
  945.             // Get root of document
  946.             navRoot = context.Clone();
  947.             navRoot.MoveToRoot();
  948.            
  949.             // Ensure that a slot exists for the new index
  950.             if (this.indexes == null) {
  951.                 this.indexes = new ArrayList[indexId + 4];
  952.             }
  953.             else if (indexId >= this.indexes.Length) {
  954.                 // Resize array
  955.                 ArrayList[] indexesNew = new ArrayList[indexId + 4];
  956.                 Array.Copy(this.indexes, 0, indexesNew, 0, this.indexes.Length);
  957.                 this.indexes = indexesNew;
  958.             }
  959.            
  960.             docIndexes = (ArrayList)this.indexes[indexId];
  961.             if (docIndexes == null) {
  962.                 docIndexes = new ArrayList();
  963.                 this.indexes[indexId] = docIndexes;
  964.             }
  965.            
  966.             docIndexes.Add(navRoot);
  967.             docIndexes.Add(index);
  968.         }
  969.        
  970.        
  971.         //-----------------------------------------------
  972.         // Output construction
  973.         //-----------------------------------------------
  974.        
  975.         /// <summary>
  976.         /// Get output writer object.
  977.         /// </summary>
  978.         public XmlQueryOutput Output {
  979.             get { return this.output; }
  980.         }
  981.        
  982.         /// <summary>
  983.         /// Start construction of a nested sequence of items. Return a new XmlQueryOutput that will be
  984.         /// used to construct this new sequence.
  985.         /// </summary>
  986.         public void StartSequenceConstruction(out XmlQueryOutput output)
  987.         {
  988.             // Push current writer
  989.             this.stkOutput.Push(this.output);
  990.            
  991.             // Create new writers
  992.             output = this.output = new XmlQueryOutput(this, new XmlCachedSequenceWriter());
  993.         }
  994.        
  995.         /// <summary>
  996.         /// End construction of a nested sequence of items and return the items as an IList<XPathItem>
  997.         /// internal class. Return previous XmlQueryOutput.
  998.         /// </summary>
  999.         public IList<XPathItem> EndSequenceConstruction(out XmlQueryOutput output)
  1000.         {
  1001.             IList<XPathItem> seq = ((XmlCachedSequenceWriter)this.output.SequenceWriter).ResultSequence;
  1002.            
  1003.             // Restore previous XmlQueryOutput
  1004.             output = this.output = this.stkOutput.Pop();
  1005.            
  1006.             return seq;
  1007.         }
  1008.        
  1009.         /// <summary>
  1010.         /// Start construction of an Rtf. Return a new XmlQueryOutput object that will be used to construct this Rtf.
  1011.         /// </summary>
  1012.         public void StartRtfConstruction(string baseUri, out XmlQueryOutput output)
  1013.         {
  1014.             // Push current writer
  1015.             this.stkOutput.Push(this.output);
  1016.            
  1017.             // Create new XmlQueryOutput over an Rtf writer
  1018.             output = this.output = new XmlQueryOutput(this, new XmlEventCache(baseUri, true));
  1019.         }
  1020.        
  1021.         /// <summary>
  1022.         /// End construction of an Rtf and return it as an RtfNavigator. Return previous XmlQueryOutput object.
  1023.         /// </summary>
  1024.         public XPathNavigator EndRtfConstruction(out XmlQueryOutput output)
  1025.         {
  1026.             XmlEventCache events;
  1027.            
  1028.             events = (XmlEventCache)this.output.Writer;
  1029.            
  1030.             // Restore previous XmlQueryOutput
  1031.             output = this.output = this.stkOutput.Pop();
  1032.            
  1033.             // Return Rtf as an RtfNavigator
  1034.             events.EndEvents();
  1035.             return new RtfTreeNavigator(events, this.nameTableQuery);
  1036.         }
  1037.        
  1038.         /// <summary>
  1039.         /// Construct a new RtfTextNavigator from the specified "text". This is much more efficient than calling
  1040.         /// StartNodeConstruction(), StartRtf(), WriteString(), EndRtf(), and EndNodeConstruction().
  1041.         /// </summary>
  1042.         public XPathNavigator TextRtfConstruction(string text, string baseUri)
  1043.         {
  1044.             return new RtfTextNavigator(text, baseUri);
  1045.         }
  1046.        
  1047.        
  1048.         //-----------------------------------------------
  1049.         // Miscellaneous
  1050.         //-----------------------------------------------
  1051.        
  1052.         /// <summary>
  1053.         /// Report query execution information to event handler.
  1054.         /// </summary>
  1055.         public void SendMessage(string message)
  1056.         {
  1057.             this.cmd.FireOnMessageEvent(message);
  1058.         }
  1059.        
  1060.         /// <summary>
  1061.         /// Throw an Xml exception having the specified message text.
  1062.         /// </summary>
  1063.         public void ThrowException(string text)
  1064.         {
  1065.             throw new XslTransformException(text);
  1066.         }
  1067.        
  1068.         /// <summary>
  1069.         /// Position navThis to the same location as navThat.
  1070.         /// </summary>
  1071.         static internal XPathNavigator SyncToNavigator(XPathNavigator navigatorThis, XPathNavigator navigatorThat)
  1072.         {
  1073.             if (navigatorThis == null || !navigatorThis.MoveTo(navigatorThat))
  1074.                 return navigatorThat.Clone();
  1075.            
  1076.             return navigatorThis;
  1077.         }
  1078.     }
  1079. }

Developer Fusion