Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / whidbey / netfxsp / ndp / fx / src / XmlUtils / System / Xml / Xsl / Runtime / XmlQueryRuntime.cs / 5 / XmlQueryRuntime.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //[....] //----------------------------------------------------------------------------- using System; using System.IO; using System.Xml; using System.Xml.XPath; using System.Xml.Schema; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Text; using System.Globalization; using System.Reflection; using System.Reflection.Emit; using System.Xml.Xsl.Qil; using System.Xml.Xsl.IlGen; using System.ComponentModel; using MS.Internal.Xml.XPath; namespace System.Xml.Xsl.Runtime { using Res = System.Xml.Utils.Res; ////// XmlQueryRuntime is passed as the first parameter to all generated query methods. /// /// XmlQueryRuntime contains runtime support for generated ILGen queries: /// 1. Stack of output writers (stack handles nested document construction) /// 2. Manages list of all xml types that are used within the query /// 3. Manages list of all atomized names that are used within the query /// [EditorBrowsable(EditorBrowsableState.Never)] public sealed class XmlQueryRuntime { // Early-Bound Library Objects private XmlQueryContext ctxt; private XsltLibrary xsltLib; private EarlyBoundInfo[] earlyInfo; private object[] earlyObjects; // Global variables and parameters private string[] globalNames; private object[] globalValues; // Names, prefix mappings, and name filters private XmlNameTable nameTableQuery; private string[] atomizedNames; // Names after atomization private XmlNavigatorFilter[] filters; // Name filters (contain atomized names) private StringPair[][] prefixMappingsList; // Lists of prefix mappings (used to resolve computed names) // Xml types private XmlQueryType[] types; // Collations private XmlCollation[] collations; // Document ordering private DocumentOrderComparer docOrderCmp; // Indexes private ArrayList[] indexes; // Output construction private XmlQueryOutput output; private StackstkOutput; //----------------------------------------------- // Constructors //----------------------------------------------- /// /// This constructor is internal so that external users cannot construct it (and therefore we do not have to test it separately). /// internal XmlQueryRuntime(XmlQueryStaticData data, object defaultDataSource, XmlResolver dataSources, XsltArgumentList argList, XmlSequenceWriter seqWrt) { Debug.Assert(data != null); string[] names = data.Names; Int32Pair[] filters = data.Filters; WhitespaceRuleLookup wsRules; int i; // Early-Bound Library Objects wsRules = (data.WhitespaceRules != null && data.WhitespaceRules.Count != 0) ? new WhitespaceRuleLookup(data.WhitespaceRules) : null; this.ctxt = new XmlQueryContext(this, defaultDataSource, dataSources, argList, wsRules); this.xsltLib = null; this.earlyInfo = data.EarlyBound; this.earlyObjects = (this.earlyInfo != null) ? new object[earlyInfo.Length] : null; // Global variables and parameters this.globalNames = data.GlobalNames; this.globalValues = (this.globalNames != null) ? new object[this.globalNames.Length] : null; // Names this.nameTableQuery = this.ctxt.QueryNameTable; this.atomizedNames = null; if (names != null) { // Atomize all names in "nameTableQuery". Use names from the default data source's // name table when possible. XmlNameTable nameTableDefault = ctxt.DefaultNameTable; this.atomizedNames = new string[names.Length]; if (nameTableDefault != this.nameTableQuery && nameTableDefault != null) { // Ensure that atomized names from the default data source are added to the // name table used in this query for (i = 0; i < names.Length; i++) { string name = nameTableDefault.Get(names[i]); this.atomizedNames[i] = this.nameTableQuery.Add(name ?? names[i]); } } else { // Enter names into nametable used in this query for (i = 0; i < names.Length; i++) this.atomizedNames[i] = this.nameTableQuery.Add(names[i]); } } // Name filters this.filters = null; if (filters != null) { // Construct name filters. Each pair of integers in the filters[] array specifies the // (localName, namespaceUri) of the NameFilter to be created. this.filters = new XmlNavigatorFilter[filters.Length]; for (i = 0; i < filters.Length; i++) this.filters[i] = XmlNavNameFilter.Create(this.atomizedNames[filters[i].Left], this.atomizedNames[filters[i].Right]); } // Prefix maping lists this.prefixMappingsList = data.PrefixMappingsList; // Xml types this.types = data.Types; // Xml collations this.collations = data.Collations; // Document ordering this.docOrderCmp = new DocumentOrderComparer(); // Indexes this.indexes = null; // Output construction this.stkOutput = new Stack(16); this.output = new XmlQueryOutput(this, seqWrt); } //----------------------------------------------- // Debugger Utility Methods //----------------------------------------------- /// /// Return array containing the names of all the global variables and parameters used in this query, in this format: /// {namespace}prefix:local-name /// public string[] DebugGetGlobalNames() { return this.globalNames; } ////// Get the value of a global value having the specified name. Always return the global value as a list of XPathItem. /// Return null if there is no global value having the specified name. /// public IList DebugGetGlobalValue(string name) { for (int idx = 0; idx < this.globalNames.Length; idx++) { if (this.globalNames[idx] == name) { Debug.Assert(IsGlobalComputed(idx), "Cannot get the value of a global value until it has been computed."); Debug.Assert(this.globalValues[idx] is IList, "Only debugger should call this method, and all global values should have type item* in debugging scenarios."); return (IList) this.globalValues[idx]; } } return null; } /// /// Set the value of a global value having the specified name. If there is no such value, this method is a no-op. /// public void DebugSetGlobalValue(string name, object value) { for (int idx = 0; idx < this.globalNames.Length; idx++) { if (this.globalNames[idx] == name) { Debug.Assert(IsGlobalComputed(idx), "Cannot get the value of a global value until it has been computed."); Debug.Assert(this.globalValues[idx] is IList, "Only debugger should call this method, and all global values should have type item* in debugging scenarios."); // Always convert "value" to a list of XPathItem using the item* converter this.globalValues[idx] = (IList ) XmlAnyListConverter.ItemList.ChangeType(value, typeof(XPathItem[]), null); break; } } } /// /// Convert sequence to it's appropriate XSLT type and return to caller. /// public object DebugGetXsltValue(IList seq) { if (seq != null && seq.Count == 1) { XPathItem item = seq[0] as XPathItem; if (item != null && !item.IsNode) { return item.TypedValue; } else if (item is RtfNavigator) { return ((RtfNavigator) item).ToNavigator(); } } return seq; } //----------------------------------------------- // Early-Bound Library Objects //----------------------------------------------- internal const BindingFlags EarlyBoundFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static; internal const BindingFlags LateBoundFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static; ////// Return the object that manages external user context information such as data sources, parameters, extension objects, etc. /// public XmlQueryContext ExternalContext { get { return this.ctxt; } } ////// Return the object that manages the state needed to implement various Xslt functions. /// public XsltLibrary XsltFunctions { get { if (this.xsltLib == null) { this.xsltLib = new XsltLibrary(this); } return this.xsltLib; } } ////// Get the early-bound extension object identified by "index". If it does not yet exist, create an instance using the /// corresponding ConstructorInfo. /// public object GetEarlyBoundObject(int index) { object obj; Debug.Assert(this.earlyObjects != null && index < this.earlyObjects.Length, "Early bound object does not exist"); obj = this.earlyObjects[index]; if (obj == null) { // Early-bound object does not yet exist, so create it now obj = this.earlyInfo[index].CreateObject(); this.earlyObjects[index] = obj; } return obj; } ////// Return true if the early bound object identified by "namespaceUri" contains a method that matches "name". /// public bool EarlyBoundFunctionExists(string name, string namespaceUri) { if (this.earlyInfo == null) return false; for (int idx = 0; idx < this.earlyInfo.Length; idx++) { if (namespaceUri == this.earlyInfo[idx].NamespaceUri) return new XmlExtensionFunction(name, namespaceUri, -1, this.earlyInfo[idx].EarlyBoundType, EarlyBoundFlags).CanBind(); } return false; } //----------------------------------------------- // Global variables and parameters //----------------------------------------------- ////// Return true if the global value specified by idxValue was previously computed. /// public bool IsGlobalComputed(int index) { return this.globalValues[index] != null; } ////// Return the value that is bound to the global variable or parameter specified by idxValue. /// If the value has not yet been computed, then compute it now and store it in this.globalValues. /// public object GetGlobalValue(int index) { Debug.Assert(IsGlobalComputed(index), "Cannot get the value of a global value until it has been computed."); return this.globalValues[index]; } ////// Return the value that is bound to the global variable or parameter specified by idxValue. /// If the value has not yet been computed, then compute it now and store it in this.globalValues. /// public void SetGlobalValue(int index, object value) { Debug.Assert(!IsGlobalComputed(index), "Global value should only be set once."); this.globalValues[index] = value; } //----------------------------------------------- // Names, prefix mappings, and name filters //----------------------------------------------- ////// Return the name table used to atomize all names used by the query. /// public XmlNameTable NameTable { get { return this.nameTableQuery; } } ////// Get the atomized name at the specified index in the array of names. /// public string GetAtomizedName(int index) { Debug.Assert(this.atomizedNames != null); return this.atomizedNames[index]; } ////// Get the name filter at the specified index in the array of filters. /// public XmlNavigatorFilter GetNameFilter(int index) { Debug.Assert(this.filters != null); return this.filters[index]; } ////// XPathNodeType.All: Filters all nodes /// XPathNodeType.Attribute: Filters attributes /// XPathNodeType.Namespace: Not allowed /// XPathNodeType.XXX: Filters all nodes *except* those having XPathNodeType.XXX /// public XmlNavigatorFilter GetTypeFilter(XPathNodeType nodeType) { if (nodeType == XPathNodeType.All) return XmlNavNeverFilter.Create(); if (nodeType == XPathNodeType.Attribute) return XmlNavAttrFilter.Create(); return XmlNavTypeFilter.Create(nodeType); } ////// Parse the specified tag name (foo:bar) and resolve the resulting prefix. If the prefix cannot be resolved, /// then throw an error. Return an XmlQualifiedName. /// public XmlQualifiedName ParseTagName(string tagName, int indexPrefixMappings) { string prefix, localName, ns; // Parse the tagName as a prefix, localName pair and resolve the prefix ParseTagName(tagName, indexPrefixMappings, out prefix, out localName, out ns); return new XmlQualifiedName(localName, ns); } ////// Parse the specified tag name (foo:bar). Return an XmlQualifiedName consisting of the parsed local name /// and the specified namespace. /// public XmlQualifiedName ParseTagName(string tagName, string ns) { string prefix, localName; // Parse the tagName as a prefix, localName pair ValidateNames.ParseQNameThrow(tagName, out prefix, out localName); return new XmlQualifiedName(localName, ns); } ////// Parse the specified tag name (foo:bar) and resolve the resulting prefix. If the prefix cannot be resolved, /// then throw an error. Return the prefix, localName, and namespace URI. /// internal void ParseTagName(string tagName, int idxPrefixMappings, out string prefix, out string localName, out string ns) { Debug.Assert(this.prefixMappingsList != null); // Parse the tagName as a prefix, localName pair ValidateNames.ParseQNameThrow(tagName, out prefix, out localName); // Map the prefix to a namespace URI ns = null; foreach (StringPair pair in this.prefixMappingsList[idxPrefixMappings]) { if (prefix == pair.Left) { ns = pair.Right; break; } } // Throw exception if prefix could not be resolved if (ns == null) { // Check for mappings that are always in-scope if (prefix.Length == 0) ns = ""; else if (prefix.Equals("xml")) ns = XmlReservedNs.NsXml; else if (prefix.Equals("xmlns")) ns = XmlReservedNs.NsXmlNs; else throw new XslTransformException(Res.Xslt_InvalidPrefix, prefix); } } ////// Return true if the nav1's LocalName and NamespaceURI properties equal nav2's corresponding properties. /// public bool IsQNameEqual(XPathNavigator n1, XPathNavigator n2) { if ((object) n1.NameTable == (object) n2.NameTable) { // Use atomized comparison return (object) n1.LocalName == (object) n2.LocalName && (object) n1.NamespaceURI == (object) n2.NamespaceURI; } return (n1.LocalName == n2.LocalName) && (n1.NamespaceURI == n2.NamespaceURI); } ////// Return true if the specified navigator's LocalName and NamespaceURI properties equal the argument names. /// public bool IsQNameEqual(XPathNavigator navigator, int indexLocalName, int indexNamespaceUri) { if ((object) navigator.NameTable == (object) this.nameTableQuery) { // Use atomized comparison return ((object) GetAtomizedName(indexLocalName) == (object) navigator.LocalName && (object) GetAtomizedName(indexNamespaceUri) == (object) navigator.NamespaceURI); } // Use string comparison return (GetAtomizedName(indexLocalName) == navigator.LocalName) && (GetAtomizedName(indexNamespaceUri) == navigator.NamespaceURI); } //----------------------------------------------- // Xml types //----------------------------------------------- ////// Get the array of xml types that are used within this query. /// internal XmlQueryType[] XmlTypes { get { return this.types; } } ////// Get the Xml query type at the specified index in the array of types. /// internal XmlQueryType GetXmlType(int idxType) { Debug.Assert(this.types != null); return this.types[idxType]; } ////// Forward call to ChangeTypeXsltArgument(XmlQueryType, object, Type). /// public object ChangeTypeXsltArgument(int indexType, object value, Type destinationType) { return ChangeTypeXsltArgument(GetXmlType(indexType), value, destinationType); } ////// Convert from the Clr type of "value" to Clr type "destinationType" using V1 Xslt rules. /// These rules include converting any Rtf values to Nodes. /// internal object ChangeTypeXsltArgument(XmlQueryType xmlType, object value, Type destinationType) { Debug.Assert(XmlILTypeHelper.GetStorageType(xmlType).IsAssignableFrom(value.GetType()), "Values passed to ChangeTypeXsltArgument should be in ILGen's default Clr representation."); Debug.Assert(destinationType == XsltConvert.ObjectType || !destinationType.IsAssignableFrom(value.GetType()), "No need to call ChangeTypeXsltArgument since value is already assignable to destinationType " + destinationType); switch (xmlType.TypeCode) { case XmlTypeCode.String: if (destinationType == XsltConvert.DateTimeType) value = XsltConvert.ToDateTime((string) value); break; case XmlTypeCode.Double: if (destinationType != XsltConvert.DoubleType) value = Convert.ChangeType(value, destinationType, CultureInfo.InvariantCulture); break; case XmlTypeCode.Node: Debug.Assert(xmlType != XmlQueryTypeFactory.Node && xmlType != XmlQueryTypeFactory.NodeS, "Rtf values should have been eliminated by caller."); if (destinationType == XsltConvert.XPathNodeIteratorType) { value = new XPathArrayIterator((IList) value); } else if (destinationType == XsltConvert.XPathNavigatorArrayType) { // Copy sequence to XPathNavigator[] IListseq = (IList ) value; XPathNavigator[] navArray = new XPathNavigator[seq.Count]; for (int i = 0; i < seq.Count; i++) navArray[i] = seq[i]; value = navArray; } break; case XmlTypeCode.Item: { // Only typeof(object) is supported as a destination type if (destinationType != XsltConvert.ObjectType) throw new XslTransformException(Res.Xslt_UnsupportedClrType, destinationType.Name); // Convert to default, backwards-compatible representation // 1. NodeSet: System.Xml.XPath.XPathNodeIterator // 2. Rtf: System.Xml.XPath.XPathNavigator // 3. Other: Default V1 representation IList seq = (IList ) value; if (seq.Count == 1) { XPathItem item = seq[0]; if (item.IsNode) { // Node or Rtf RtfNavigator rtf = item as RtfNavigator; if (rtf != null) value = rtf.ToNavigator(); else value = new XPathArrayIterator((IList) value); } else { // Atomic value value = item.TypedValue; } } else { // Nodeset value = new XPathArrayIterator((IList) value); } break; } } Debug.Assert(destinationType.IsAssignableFrom(value.GetType()), "ChangeType from type " + value.GetType().Name + " to type " + destinationType.Name + " failed"); return value; } /// /// Forward call to ChangeTypeXsltResult(XmlQueryType, object) /// public object ChangeTypeXsltResult(int indexType, object value) { return ChangeTypeXsltResult(GetXmlType(indexType), value); } ////// Convert from the Clr type of "value" to the default Clr type that ILGen uses to represent the xml type, using /// the conversion rules of the xml type. /// internal object ChangeTypeXsltResult(XmlQueryType xmlType, object value) { if (value == null) throw new XslTransformException(Res.Xslt_ItemNull, string.Empty); switch (xmlType.TypeCode) { case XmlTypeCode.String: if (value.GetType() == XsltConvert.DateTimeType) value = XsltConvert.ToString((DateTime) value); break; case XmlTypeCode.Double: if (value.GetType() != XsltConvert.DoubleType) value = ((IConvertible) value).ToDouble(null); break; case XmlTypeCode.Node: if (!xmlType.IsSingleton) { XPathArrayIterator iter = value as XPathArrayIterator; // Special-case XPathArrayIterator in order to avoid copies if (iter != null && iter.AsList is XmlQueryNodeSequence) { value = iter.AsList as XmlQueryNodeSequence; } else { // Iterate over list and ensure it only contains nodes XmlQueryNodeSequence seq = new XmlQueryNodeSequence(); IList list = value as IList; if (list != null) { for (int i = 0; i < list.Count; i++) seq.Add(EnsureNavigator(list[i])); } else { foreach (object o in (IEnumerable) value) seq.Add(EnsureNavigator(o)); } value = seq; } // Always sort node-set by document order value = ((XmlQueryNodeSequence) value).DocOrderDistinct(this.docOrderCmp); } break; case XmlTypeCode.Item: { Type sourceType = value.GetType(); IXPathNavigable navigable; // If static type is item, then infer type based on dynamic value switch (XsltConvert.InferXsltType(sourceType).TypeCode) { case XmlTypeCode.Boolean: value = new XmlQueryItemSequence(new XmlAtomicValue(XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.Boolean), value)); break; case XmlTypeCode.Double: value = new XmlQueryItemSequence(new XmlAtomicValue(XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.Double), ((IConvertible) value).ToDouble(null))); break; case XmlTypeCode.String: if (sourceType == XsltConvert.DateTimeType) value = new XmlQueryItemSequence(new XmlAtomicValue(XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.String), XsltConvert.ToString((DateTime) value))); else value = new XmlQueryItemSequence(new XmlAtomicValue(XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.String), value)); break; case XmlTypeCode.Node: // Support XPathNavigator[] value = ChangeTypeXsltResult(XmlQueryTypeFactory.NodeS, value); break; case XmlTypeCode.Item: // Support XPathNodeIterator if (value is XPathNodeIterator) { value = ChangeTypeXsltResult(XmlQueryTypeFactory.NodeS, value); break; } // Support IXPathNavigable and XPathNavigator navigable = value as IXPathNavigable; if (navigable != null) { if (value is XPathNavigator) value = new XmlQueryNodeSequence((XPathNavigator) value); else value = new XmlQueryNodeSequence(navigable.CreateNavigator()); break; } throw new XslTransformException(Res.Xslt_UnsupportedClrType, sourceType.Name); } break; } } Debug.Assert(XmlILTypeHelper.GetStorageType(xmlType).IsAssignableFrom(value.GetType()), "Xml type " + xmlType + " is not represented in ILGen as " + value.GetType().Name); return value; } ////// Ensure that "value" is a navigator and not null. /// private static XPathNavigator EnsureNavigator(object value) { XPathNavigator nav = value as XPathNavigator; if (nav == null) throw new XslTransformException(Res.Xslt_ItemNull, string.Empty); return nav; } ////// Return true if the type of every item in "seq" matches the xml type identified by "idxType". /// public bool MatchesXmlType(IListseq, int indexType) { XmlQueryType typBase = GetXmlType(indexType); XmlQueryCardinality card; switch (seq.Count) { case 0: card = XmlQueryCardinality.Zero; break; case 1: card = XmlQueryCardinality.One; break; default: card = XmlQueryCardinality.More; break; } if (!(card <= typBase.Cardinality)) return false; typBase = typBase.Prime; for (int i = 0; i < seq.Count; i++) { if (!CreateXmlType(seq[0]).IsSubtypeOf(typBase)) return false; } return true; } /// /// Return true if the type of "item" matches the xml type identified by "idxType". /// public bool MatchesXmlType(XPathItem item, int indexType) { return CreateXmlType(item).IsSubtypeOf(GetXmlType(indexType)); } ////// Return true if the type of "seq" is a subtype of a singleton type identified by "code". /// public bool MatchesXmlType(IListseq, XmlTypeCode code) { if (seq.Count != 1) return false; return MatchesXmlType(seq[0], code); } /// /// Return true if the type of "item" is a subtype of the type identified by "code". /// public bool MatchesXmlType(XPathItem item, XmlTypeCode code) { // All atomic type codes appear after AnyAtomicType if (code > XmlTypeCode.AnyAtomicType) return !item.IsNode && item.XmlType.TypeCode == code; // Handle node code and AnyAtomicType switch (code) { case XmlTypeCode.AnyAtomicType: return !item.IsNode; case XmlTypeCode.Node: return item.IsNode; case XmlTypeCode.Item: return true; default: if (!item.IsNode) return false; switch (((XPathNavigator) item).NodeType) { case XPathNodeType.Root: return code == XmlTypeCode.Document; case XPathNodeType.Element: return code == XmlTypeCode.Element; case XPathNodeType.Attribute: return code == XmlTypeCode.Attribute; case XPathNodeType.Namespace: return code == XmlTypeCode.Namespace; case XPathNodeType.Text: return code == XmlTypeCode.Text; case XPathNodeType.SignificantWhitespace: return code == XmlTypeCode.Text; case XPathNodeType.Whitespace: return code == XmlTypeCode.Text; case XPathNodeType.ProcessingInstruction: return code == XmlTypeCode.ProcessingInstruction; case XPathNodeType.Comment: return code == XmlTypeCode.Comment; } break; } Debug.Fail("XmlTypeCode " + code + " was not fully handled."); return false; } ////// Create an XmlQueryType that represents the type of "item". /// private XmlQueryType CreateXmlType(XPathItem item) { if (item.IsNode) { // Rtf RtfNavigator rtf = item as RtfNavigator; if (rtf != null) return XmlQueryTypeFactory.Node; // Node XPathNavigator nav = (XPathNavigator) item; switch (nav.NodeType) { case XPathNodeType.Root: case XPathNodeType.Element: if (nav.XmlType == null) return XmlQueryTypeFactory.Type(nav.NodeType, XmlQualifiedNameTest.New(nav.LocalName, nav.NamespaceURI), XmlSchemaComplexType.UntypedAnyType, false); return XmlQueryTypeFactory.Type(nav.NodeType, XmlQualifiedNameTest.New(nav.LocalName, nav.NamespaceURI), nav.XmlType, nav.SchemaInfo.SchemaElement.IsNillable); case XPathNodeType.Attribute: if (nav.XmlType == null) return XmlQueryTypeFactory.Type(nav.NodeType, XmlQualifiedNameTest.New(nav.LocalName, nav.NamespaceURI), DatatypeImplementation.UntypedAtomicType, false); return XmlQueryTypeFactory.Type(nav.NodeType, XmlQualifiedNameTest.New(nav.LocalName, nav.NamespaceURI), nav.XmlType, false); } return XmlQueryTypeFactory.Type(nav.NodeType, XmlQualifiedNameTest.Wildcard, XmlSchemaComplexType.AnyType, false); } // Atomic value return XmlQueryTypeFactory.Type((XmlSchemaSimpleType)item.XmlType, true); } //----------------------------------------------- // Xml collations //----------------------------------------------- ////// Get a collation that was statically created. /// public XmlCollation GetCollation(int index) { Debug.Assert(this.collations != null); return this.collations[index]; } ////// Create a collation from a string. /// public XmlCollation CreateCollation(string collation) { return XmlCollation.Create(collation); } //----------------------------------------------- // Document Ordering and Identity //----------------------------------------------- ////// Compare the relative positions of two navigators. Return -1 if navThis is before navThat, 1 if after, and /// 0 if they are positioned to the same node. /// public int ComparePosition(XPathNavigator navigatorThis, XPathNavigator navigatorThat) { return this.docOrderCmp.Compare(navigatorThis, navigatorThat); } ////// Get a comparer which guarantees a stable ordering among nodes, even those from different documents. /// public IListDocOrderDistinct(IList seq) { if (seq.Count <= 1) return seq; XmlQueryNodeSequence nodeSeq = (XmlQueryNodeSequence) seq; if (nodeSeq == null) nodeSeq = new XmlQueryNodeSequence(seq); return nodeSeq.DocOrderDistinct(this.docOrderCmp); } /// /// Generate a unique string identifier for the specified node. Do this by asking the navigator for an identifier /// that is unique within the document, and then prepend a document index. /// public string GenerateId(XPathNavigator navigator) { return string.Concat("ID", this.docOrderCmp.GetDocumentIndex(navigator).ToString(CultureInfo.InvariantCulture), navigator.UniqueId); } //----------------------------------------------- // Indexes //----------------------------------------------- ////// If an index having the specified Id has already been created over the "context" document, then return it /// in "index" and return true. Otherwise, create a new, empty index and return false. /// public bool FindIndex(XPathNavigator context, int indexId, out XmlILIndex index) { XPathNavigator navRoot; ArrayList docIndexes; Debug.Assert(context != null); // Get root of document navRoot = context.Clone(); navRoot.MoveToRoot(); // Search pre-existing indexes in order to determine whether the specified index has already been created if (this.indexes != null && indexId < this.indexes.Length) { docIndexes = (ArrayList) this.indexes[indexId]; if (docIndexes != null) { // Search for an index defined over the specified document for (int i = 0; i < docIndexes.Count; i += 2) { // If we find a matching document, then return the index saved in the next slot if (((XPathNavigator) docIndexes[i]).IsSamePosition(navRoot)) { index = (XmlILIndex) docIndexes[i + 1]; return true; } } } } // Return a new, empty index index = new XmlILIndex(); return false; } ////// Add a newly built index over the specified "context" document to the existing collection of indexes. /// public void AddNewIndex(XPathNavigator context, int indexId, XmlILIndex index) { XPathNavigator navRoot; ArrayList docIndexes; Debug.Assert(context != null); // Get root of document navRoot = context.Clone(); navRoot.MoveToRoot(); // Ensure that a slot exists for the new index if (this.indexes == null) { this.indexes = new ArrayList[indexId + 4]; } else if (indexId >= this.indexes.Length) { // Resize array ArrayList[] indexesNew = new ArrayList[indexId + 4]; Array.Copy(this.indexes, 0, indexesNew, 0, this.indexes.Length); this.indexes = indexesNew; } docIndexes = (ArrayList) this.indexes[indexId]; if (docIndexes == null) { docIndexes = new ArrayList(); this.indexes[indexId] = docIndexes; } docIndexes.Add(navRoot); docIndexes.Add(index); } //----------------------------------------------- // Output construction //----------------------------------------------- ////// Get output writer object. /// public XmlQueryOutput Output { get { return this.output; } } ////// Start construction of a nested sequence of items. Return a new XmlQueryOutput that will be /// used to construct this new sequence. /// public void StartSequenceConstruction(out XmlQueryOutput output) { // Push current writer this.stkOutput.Push(this.output); // Create new writers output = this.output = new XmlQueryOutput(this, new XmlCachedSequenceWriter()); } ////// End construction of a nested sequence of items and return the items as an IList public IList/// internal class. Return previous XmlQueryOutput. /// EndSequenceConstruction(out XmlQueryOutput output) { IList seq = ((XmlCachedSequenceWriter) this.output.SequenceWriter).ResultSequence; // Restore previous XmlQueryOutput output = this.output = this.stkOutput.Pop(); return seq; } /// /// Start construction of an Rtf. Return a new XmlQueryOutput object that will be used to construct this Rtf. /// public void StartRtfConstruction(string baseUri, out XmlQueryOutput output) { // Push current writer this.stkOutput.Push(this.output); // Create new XmlQueryOutput over an Rtf writer output = this.output = new XmlQueryOutput(this, new XmlEventCache(baseUri, true)); } ////// End construction of an Rtf and return it as an RtfNavigator. Return previous XmlQueryOutput object. /// public XPathNavigator EndRtfConstruction(out XmlQueryOutput output) { XmlEventCache events; events = (XmlEventCache) this.output.Writer; // Restore previous XmlQueryOutput output = this.output = this.stkOutput.Pop(); // Return Rtf as an RtfNavigator events.EndEvents(); return new RtfTreeNavigator(events, this.nameTableQuery); } ////// Construct a new RtfTextNavigator from the specified "text". This is much more efficient than calling /// StartNodeConstruction(), StartRtf(), WriteString(), EndRtf(), and EndNodeConstruction(). /// public XPathNavigator TextRtfConstruction(string text, string baseUri) { return new RtfTextNavigator(text, baseUri); } //----------------------------------------------- // Miscellaneous //----------------------------------------------- ////// Report query execution information to event handler. /// public void SendMessage(string message) { this.ctxt.OnXsltMessageEncountered(message); } ////// Throw an Xml exception having the specified message text. /// public void ThrowException(string text) { throw new XslTransformException(text); } ////// Position navThis to the same location as navThat. /// internal static XPathNavigator SyncToNavigator(XPathNavigator navigatorThis, XPathNavigator navigatorThat) { if (navigatorThis == null || !navigatorThis.MoveTo(navigatorThat)) return navigatorThat.Clone(); return navigatorThis; } ////// Function is called in Debug mode on each time context node change. /// public static int OnCurrentNodeChanged(XPathNavigator currentNode) { IXmlLineInfo lineInfo = currentNode as IXmlLineInfo; // In case of a namespace node, check whether it is inherited or locally defined if (lineInfo != null && ! (currentNode.NodeType == XPathNodeType.Namespace && IsInheritedNamespace(currentNode))) { OnCurrentNodeChanged2(currentNode.BaseURI, lineInfo.LineNumber, lineInfo.LinePosition); } return 0; } // 'true' if current Namespace "inherited" from it's parent. Not defined localy. private static bool IsInheritedNamespace(XPathNavigator node) { Debug.Assert(node.NodeType == XPathNodeType.Namespace); XPathNavigator nav = node.Clone(); if (nav.MoveToParent()) { if (nav.MoveToFirstNamespace(XPathNamespaceScope.Local)) { do { if ((object)nav.LocalName == (object)node.LocalName) { return false; } } while (nav.MoveToNextNamespace(XPathNamespaceScope.Local)); } } return true; } private static void OnCurrentNodeChanged2(string baseUri, int lineNumber, int linePosition) {} } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //[....] //----------------------------------------------------------------------------- using System; using System.IO; using System.Xml; using System.Xml.XPath; using System.Xml.Schema; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Text; using System.Globalization; using System.Reflection; using System.Reflection.Emit; using System.Xml.Xsl.Qil; using System.Xml.Xsl.IlGen; using System.ComponentModel; using MS.Internal.Xml.XPath; namespace System.Xml.Xsl.Runtime { using Res = System.Xml.Utils.Res; ////// XmlQueryRuntime is passed as the first parameter to all generated query methods. /// /// XmlQueryRuntime contains runtime support for generated ILGen queries: /// 1. Stack of output writers (stack handles nested document construction) /// 2. Manages list of all xml types that are used within the query /// 3. Manages list of all atomized names that are used within the query /// [EditorBrowsable(EditorBrowsableState.Never)] public sealed class XmlQueryRuntime { // Early-Bound Library Objects private XmlQueryContext ctxt; private XsltLibrary xsltLib; private EarlyBoundInfo[] earlyInfo; private object[] earlyObjects; // Global variables and parameters private string[] globalNames; private object[] globalValues; // Names, prefix mappings, and name filters private XmlNameTable nameTableQuery; private string[] atomizedNames; // Names after atomization private XmlNavigatorFilter[] filters; // Name filters (contain atomized names) private StringPair[][] prefixMappingsList; // Lists of prefix mappings (used to resolve computed names) // Xml types private XmlQueryType[] types; // Collations private XmlCollation[] collations; // Document ordering private DocumentOrderComparer docOrderCmp; // Indexes private ArrayList[] indexes; // Output construction private XmlQueryOutput output; private StackstkOutput; //----------------------------------------------- // Constructors //----------------------------------------------- /// /// This constructor is internal so that external users cannot construct it (and therefore we do not have to test it separately). /// internal XmlQueryRuntime(XmlQueryStaticData data, object defaultDataSource, XmlResolver dataSources, XsltArgumentList argList, XmlSequenceWriter seqWrt) { Debug.Assert(data != null); string[] names = data.Names; Int32Pair[] filters = data.Filters; WhitespaceRuleLookup wsRules; int i; // Early-Bound Library Objects wsRules = (data.WhitespaceRules != null && data.WhitespaceRules.Count != 0) ? new WhitespaceRuleLookup(data.WhitespaceRules) : null; this.ctxt = new XmlQueryContext(this, defaultDataSource, dataSources, argList, wsRules); this.xsltLib = null; this.earlyInfo = data.EarlyBound; this.earlyObjects = (this.earlyInfo != null) ? new object[earlyInfo.Length] : null; // Global variables and parameters this.globalNames = data.GlobalNames; this.globalValues = (this.globalNames != null) ? new object[this.globalNames.Length] : null; // Names this.nameTableQuery = this.ctxt.QueryNameTable; this.atomizedNames = null; if (names != null) { // Atomize all names in "nameTableQuery". Use names from the default data source's // name table when possible. XmlNameTable nameTableDefault = ctxt.DefaultNameTable; this.atomizedNames = new string[names.Length]; if (nameTableDefault != this.nameTableQuery && nameTableDefault != null) { // Ensure that atomized names from the default data source are added to the // name table used in this query for (i = 0; i < names.Length; i++) { string name = nameTableDefault.Get(names[i]); this.atomizedNames[i] = this.nameTableQuery.Add(name ?? names[i]); } } else { // Enter names into nametable used in this query for (i = 0; i < names.Length; i++) this.atomizedNames[i] = this.nameTableQuery.Add(names[i]); } } // Name filters this.filters = null; if (filters != null) { // Construct name filters. Each pair of integers in the filters[] array specifies the // (localName, namespaceUri) of the NameFilter to be created. this.filters = new XmlNavigatorFilter[filters.Length]; for (i = 0; i < filters.Length; i++) this.filters[i] = XmlNavNameFilter.Create(this.atomizedNames[filters[i].Left], this.atomizedNames[filters[i].Right]); } // Prefix maping lists this.prefixMappingsList = data.PrefixMappingsList; // Xml types this.types = data.Types; // Xml collations this.collations = data.Collations; // Document ordering this.docOrderCmp = new DocumentOrderComparer(); // Indexes this.indexes = null; // Output construction this.stkOutput = new Stack(16); this.output = new XmlQueryOutput(this, seqWrt); } //----------------------------------------------- // Debugger Utility Methods //----------------------------------------------- /// /// Return array containing the names of all the global variables and parameters used in this query, in this format: /// {namespace}prefix:local-name /// public string[] DebugGetGlobalNames() { return this.globalNames; } ////// Get the value of a global value having the specified name. Always return the global value as a list of XPathItem. /// Return null if there is no global value having the specified name. /// public IList DebugGetGlobalValue(string name) { for (int idx = 0; idx < this.globalNames.Length; idx++) { if (this.globalNames[idx] == name) { Debug.Assert(IsGlobalComputed(idx), "Cannot get the value of a global value until it has been computed."); Debug.Assert(this.globalValues[idx] is IList, "Only debugger should call this method, and all global values should have type item* in debugging scenarios."); return (IList) this.globalValues[idx]; } } return null; } /// /// Set the value of a global value having the specified name. If there is no such value, this method is a no-op. /// public void DebugSetGlobalValue(string name, object value) { for (int idx = 0; idx < this.globalNames.Length; idx++) { if (this.globalNames[idx] == name) { Debug.Assert(IsGlobalComputed(idx), "Cannot get the value of a global value until it has been computed."); Debug.Assert(this.globalValues[idx] is IList, "Only debugger should call this method, and all global values should have type item* in debugging scenarios."); // Always convert "value" to a list of XPathItem using the item* converter this.globalValues[idx] = (IList ) XmlAnyListConverter.ItemList.ChangeType(value, typeof(XPathItem[]), null); break; } } } /// /// Convert sequence to it's appropriate XSLT type and return to caller. /// public object DebugGetXsltValue(IList seq) { if (seq != null && seq.Count == 1) { XPathItem item = seq[0] as XPathItem; if (item != null && !item.IsNode) { return item.TypedValue; } else if (item is RtfNavigator) { return ((RtfNavigator) item).ToNavigator(); } } return seq; } //----------------------------------------------- // Early-Bound Library Objects //----------------------------------------------- internal const BindingFlags EarlyBoundFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static; internal const BindingFlags LateBoundFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static; ////// Return the object that manages external user context information such as data sources, parameters, extension objects, etc. /// public XmlQueryContext ExternalContext { get { return this.ctxt; } } ////// Return the object that manages the state needed to implement various Xslt functions. /// public XsltLibrary XsltFunctions { get { if (this.xsltLib == null) { this.xsltLib = new XsltLibrary(this); } return this.xsltLib; } } ////// Get the early-bound extension object identified by "index". If it does not yet exist, create an instance using the /// corresponding ConstructorInfo. /// public object GetEarlyBoundObject(int index) { object obj; Debug.Assert(this.earlyObjects != null && index < this.earlyObjects.Length, "Early bound object does not exist"); obj = this.earlyObjects[index]; if (obj == null) { // Early-bound object does not yet exist, so create it now obj = this.earlyInfo[index].CreateObject(); this.earlyObjects[index] = obj; } return obj; } ////// Return true if the early bound object identified by "namespaceUri" contains a method that matches "name". /// public bool EarlyBoundFunctionExists(string name, string namespaceUri) { if (this.earlyInfo == null) return false; for (int idx = 0; idx < this.earlyInfo.Length; idx++) { if (namespaceUri == this.earlyInfo[idx].NamespaceUri) return new XmlExtensionFunction(name, namespaceUri, -1, this.earlyInfo[idx].EarlyBoundType, EarlyBoundFlags).CanBind(); } return false; } //----------------------------------------------- // Global variables and parameters //----------------------------------------------- ////// Return true if the global value specified by idxValue was previously computed. /// public bool IsGlobalComputed(int index) { return this.globalValues[index] != null; } ////// Return the value that is bound to the global variable or parameter specified by idxValue. /// If the value has not yet been computed, then compute it now and store it in this.globalValues. /// public object GetGlobalValue(int index) { Debug.Assert(IsGlobalComputed(index), "Cannot get the value of a global value until it has been computed."); return this.globalValues[index]; } ////// Return the value that is bound to the global variable or parameter specified by idxValue. /// If the value has not yet been computed, then compute it now and store it in this.globalValues. /// public void SetGlobalValue(int index, object value) { Debug.Assert(!IsGlobalComputed(index), "Global value should only be set once."); this.globalValues[index] = value; } //----------------------------------------------- // Names, prefix mappings, and name filters //----------------------------------------------- ////// Return the name table used to atomize all names used by the query. /// public XmlNameTable NameTable { get { return this.nameTableQuery; } } ////// Get the atomized name at the specified index in the array of names. /// public string GetAtomizedName(int index) { Debug.Assert(this.atomizedNames != null); return this.atomizedNames[index]; } ////// Get the name filter at the specified index in the array of filters. /// public XmlNavigatorFilter GetNameFilter(int index) { Debug.Assert(this.filters != null); return this.filters[index]; } ////// XPathNodeType.All: Filters all nodes /// XPathNodeType.Attribute: Filters attributes /// XPathNodeType.Namespace: Not allowed /// XPathNodeType.XXX: Filters all nodes *except* those having XPathNodeType.XXX /// public XmlNavigatorFilter GetTypeFilter(XPathNodeType nodeType) { if (nodeType == XPathNodeType.All) return XmlNavNeverFilter.Create(); if (nodeType == XPathNodeType.Attribute) return XmlNavAttrFilter.Create(); return XmlNavTypeFilter.Create(nodeType); } ////// Parse the specified tag name (foo:bar) and resolve the resulting prefix. If the prefix cannot be resolved, /// then throw an error. Return an XmlQualifiedName. /// public XmlQualifiedName ParseTagName(string tagName, int indexPrefixMappings) { string prefix, localName, ns; // Parse the tagName as a prefix, localName pair and resolve the prefix ParseTagName(tagName, indexPrefixMappings, out prefix, out localName, out ns); return new XmlQualifiedName(localName, ns); } ////// Parse the specified tag name (foo:bar). Return an XmlQualifiedName consisting of the parsed local name /// and the specified namespace. /// public XmlQualifiedName ParseTagName(string tagName, string ns) { string prefix, localName; // Parse the tagName as a prefix, localName pair ValidateNames.ParseQNameThrow(tagName, out prefix, out localName); return new XmlQualifiedName(localName, ns); } ////// Parse the specified tag name (foo:bar) and resolve the resulting prefix. If the prefix cannot be resolved, /// then throw an error. Return the prefix, localName, and namespace URI. /// internal void ParseTagName(string tagName, int idxPrefixMappings, out string prefix, out string localName, out string ns) { Debug.Assert(this.prefixMappingsList != null); // Parse the tagName as a prefix, localName pair ValidateNames.ParseQNameThrow(tagName, out prefix, out localName); // Map the prefix to a namespace URI ns = null; foreach (StringPair pair in this.prefixMappingsList[idxPrefixMappings]) { if (prefix == pair.Left) { ns = pair.Right; break; } } // Throw exception if prefix could not be resolved if (ns == null) { // Check for mappings that are always in-scope if (prefix.Length == 0) ns = ""; else if (prefix.Equals("xml")) ns = XmlReservedNs.NsXml; else if (prefix.Equals("xmlns")) ns = XmlReservedNs.NsXmlNs; else throw new XslTransformException(Res.Xslt_InvalidPrefix, prefix); } } ////// Return true if the nav1's LocalName and NamespaceURI properties equal nav2's corresponding properties. /// public bool IsQNameEqual(XPathNavigator n1, XPathNavigator n2) { if ((object) n1.NameTable == (object) n2.NameTable) { // Use atomized comparison return (object) n1.LocalName == (object) n2.LocalName && (object) n1.NamespaceURI == (object) n2.NamespaceURI; } return (n1.LocalName == n2.LocalName) && (n1.NamespaceURI == n2.NamespaceURI); } ////// Return true if the specified navigator's LocalName and NamespaceURI properties equal the argument names. /// public bool IsQNameEqual(XPathNavigator navigator, int indexLocalName, int indexNamespaceUri) { if ((object) navigator.NameTable == (object) this.nameTableQuery) { // Use atomized comparison return ((object) GetAtomizedName(indexLocalName) == (object) navigator.LocalName && (object) GetAtomizedName(indexNamespaceUri) == (object) navigator.NamespaceURI); } // Use string comparison return (GetAtomizedName(indexLocalName) == navigator.LocalName) && (GetAtomizedName(indexNamespaceUri) == navigator.NamespaceURI); } //----------------------------------------------- // Xml types //----------------------------------------------- ////// Get the array of xml types that are used within this query. /// internal XmlQueryType[] XmlTypes { get { return this.types; } } ////// Get the Xml query type at the specified index in the array of types. /// internal XmlQueryType GetXmlType(int idxType) { Debug.Assert(this.types != null); return this.types[idxType]; } ////// Forward call to ChangeTypeXsltArgument(XmlQueryType, object, Type). /// public object ChangeTypeXsltArgument(int indexType, object value, Type destinationType) { return ChangeTypeXsltArgument(GetXmlType(indexType), value, destinationType); } ////// Convert from the Clr type of "value" to Clr type "destinationType" using V1 Xslt rules. /// These rules include converting any Rtf values to Nodes. /// internal object ChangeTypeXsltArgument(XmlQueryType xmlType, object value, Type destinationType) { Debug.Assert(XmlILTypeHelper.GetStorageType(xmlType).IsAssignableFrom(value.GetType()), "Values passed to ChangeTypeXsltArgument should be in ILGen's default Clr representation."); Debug.Assert(destinationType == XsltConvert.ObjectType || !destinationType.IsAssignableFrom(value.GetType()), "No need to call ChangeTypeXsltArgument since value is already assignable to destinationType " + destinationType); switch (xmlType.TypeCode) { case XmlTypeCode.String: if (destinationType == XsltConvert.DateTimeType) value = XsltConvert.ToDateTime((string) value); break; case XmlTypeCode.Double: if (destinationType != XsltConvert.DoubleType) value = Convert.ChangeType(value, destinationType, CultureInfo.InvariantCulture); break; case XmlTypeCode.Node: Debug.Assert(xmlType != XmlQueryTypeFactory.Node && xmlType != XmlQueryTypeFactory.NodeS, "Rtf values should have been eliminated by caller."); if (destinationType == XsltConvert.XPathNodeIteratorType) { value = new XPathArrayIterator((IList) value); } else if (destinationType == XsltConvert.XPathNavigatorArrayType) { // Copy sequence to XPathNavigator[] IListseq = (IList ) value; XPathNavigator[] navArray = new XPathNavigator[seq.Count]; for (int i = 0; i < seq.Count; i++) navArray[i] = seq[i]; value = navArray; } break; case XmlTypeCode.Item: { // Only typeof(object) is supported as a destination type if (destinationType != XsltConvert.ObjectType) throw new XslTransformException(Res.Xslt_UnsupportedClrType, destinationType.Name); // Convert to default, backwards-compatible representation // 1. NodeSet: System.Xml.XPath.XPathNodeIterator // 2. Rtf: System.Xml.XPath.XPathNavigator // 3. Other: Default V1 representation IList seq = (IList ) value; if (seq.Count == 1) { XPathItem item = seq[0]; if (item.IsNode) { // Node or Rtf RtfNavigator rtf = item as RtfNavigator; if (rtf != null) value = rtf.ToNavigator(); else value = new XPathArrayIterator((IList) value); } else { // Atomic value value = item.TypedValue; } } else { // Nodeset value = new XPathArrayIterator((IList) value); } break; } } Debug.Assert(destinationType.IsAssignableFrom(value.GetType()), "ChangeType from type " + value.GetType().Name + " to type " + destinationType.Name + " failed"); return value; } /// /// Forward call to ChangeTypeXsltResult(XmlQueryType, object) /// public object ChangeTypeXsltResult(int indexType, object value) { return ChangeTypeXsltResult(GetXmlType(indexType), value); } ////// Convert from the Clr type of "value" to the default Clr type that ILGen uses to represent the xml type, using /// the conversion rules of the xml type. /// internal object ChangeTypeXsltResult(XmlQueryType xmlType, object value) { if (value == null) throw new XslTransformException(Res.Xslt_ItemNull, string.Empty); switch (xmlType.TypeCode) { case XmlTypeCode.String: if (value.GetType() == XsltConvert.DateTimeType) value = XsltConvert.ToString((DateTime) value); break; case XmlTypeCode.Double: if (value.GetType() != XsltConvert.DoubleType) value = ((IConvertible) value).ToDouble(null); break; case XmlTypeCode.Node: if (!xmlType.IsSingleton) { XPathArrayIterator iter = value as XPathArrayIterator; // Special-case XPathArrayIterator in order to avoid copies if (iter != null && iter.AsList is XmlQueryNodeSequence) { value = iter.AsList as XmlQueryNodeSequence; } else { // Iterate over list and ensure it only contains nodes XmlQueryNodeSequence seq = new XmlQueryNodeSequence(); IList list = value as IList; if (list != null) { for (int i = 0; i < list.Count; i++) seq.Add(EnsureNavigator(list[i])); } else { foreach (object o in (IEnumerable) value) seq.Add(EnsureNavigator(o)); } value = seq; } // Always sort node-set by document order value = ((XmlQueryNodeSequence) value).DocOrderDistinct(this.docOrderCmp); } break; case XmlTypeCode.Item: { Type sourceType = value.GetType(); IXPathNavigable navigable; // If static type is item, then infer type based on dynamic value switch (XsltConvert.InferXsltType(sourceType).TypeCode) { case XmlTypeCode.Boolean: value = new XmlQueryItemSequence(new XmlAtomicValue(XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.Boolean), value)); break; case XmlTypeCode.Double: value = new XmlQueryItemSequence(new XmlAtomicValue(XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.Double), ((IConvertible) value).ToDouble(null))); break; case XmlTypeCode.String: if (sourceType == XsltConvert.DateTimeType) value = new XmlQueryItemSequence(new XmlAtomicValue(XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.String), XsltConvert.ToString((DateTime) value))); else value = new XmlQueryItemSequence(new XmlAtomicValue(XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.String), value)); break; case XmlTypeCode.Node: // Support XPathNavigator[] value = ChangeTypeXsltResult(XmlQueryTypeFactory.NodeS, value); break; case XmlTypeCode.Item: // Support XPathNodeIterator if (value is XPathNodeIterator) { value = ChangeTypeXsltResult(XmlQueryTypeFactory.NodeS, value); break; } // Support IXPathNavigable and XPathNavigator navigable = value as IXPathNavigable; if (navigable != null) { if (value is XPathNavigator) value = new XmlQueryNodeSequence((XPathNavigator) value); else value = new XmlQueryNodeSequence(navigable.CreateNavigator()); break; } throw new XslTransformException(Res.Xslt_UnsupportedClrType, sourceType.Name); } break; } } Debug.Assert(XmlILTypeHelper.GetStorageType(xmlType).IsAssignableFrom(value.GetType()), "Xml type " + xmlType + " is not represented in ILGen as " + value.GetType().Name); return value; } ////// Ensure that "value" is a navigator and not null. /// private static XPathNavigator EnsureNavigator(object value) { XPathNavigator nav = value as XPathNavigator; if (nav == null) throw new XslTransformException(Res.Xslt_ItemNull, string.Empty); return nav; } ////// Return true if the type of every item in "seq" matches the xml type identified by "idxType". /// public bool MatchesXmlType(IListseq, int indexType) { XmlQueryType typBase = GetXmlType(indexType); XmlQueryCardinality card; switch (seq.Count) { case 0: card = XmlQueryCardinality.Zero; break; case 1: card = XmlQueryCardinality.One; break; default: card = XmlQueryCardinality.More; break; } if (!(card <= typBase.Cardinality)) return false; typBase = typBase.Prime; for (int i = 0; i < seq.Count; i++) { if (!CreateXmlType(seq[0]).IsSubtypeOf(typBase)) return false; } return true; } /// /// Return true if the type of "item" matches the xml type identified by "idxType". /// public bool MatchesXmlType(XPathItem item, int indexType) { return CreateXmlType(item).IsSubtypeOf(GetXmlType(indexType)); } ////// Return true if the type of "seq" is a subtype of a singleton type identified by "code". /// public bool MatchesXmlType(IListseq, XmlTypeCode code) { if (seq.Count != 1) return false; return MatchesXmlType(seq[0], code); } /// /// Return true if the type of "item" is a subtype of the type identified by "code". /// public bool MatchesXmlType(XPathItem item, XmlTypeCode code) { // All atomic type codes appear after AnyAtomicType if (code > XmlTypeCode.AnyAtomicType) return !item.IsNode && item.XmlType.TypeCode == code; // Handle node code and AnyAtomicType switch (code) { case XmlTypeCode.AnyAtomicType: return !item.IsNode; case XmlTypeCode.Node: return item.IsNode; case XmlTypeCode.Item: return true; default: if (!item.IsNode) return false; switch (((XPathNavigator) item).NodeType) { case XPathNodeType.Root: return code == XmlTypeCode.Document; case XPathNodeType.Element: return code == XmlTypeCode.Element; case XPathNodeType.Attribute: return code == XmlTypeCode.Attribute; case XPathNodeType.Namespace: return code == XmlTypeCode.Namespace; case XPathNodeType.Text: return code == XmlTypeCode.Text; case XPathNodeType.SignificantWhitespace: return code == XmlTypeCode.Text; case XPathNodeType.Whitespace: return code == XmlTypeCode.Text; case XPathNodeType.ProcessingInstruction: return code == XmlTypeCode.ProcessingInstruction; case XPathNodeType.Comment: return code == XmlTypeCode.Comment; } break; } Debug.Fail("XmlTypeCode " + code + " was not fully handled."); return false; } ////// Create an XmlQueryType that represents the type of "item". /// private XmlQueryType CreateXmlType(XPathItem item) { if (item.IsNode) { // Rtf RtfNavigator rtf = item as RtfNavigator; if (rtf != null) return XmlQueryTypeFactory.Node; // Node XPathNavigator nav = (XPathNavigator) item; switch (nav.NodeType) { case XPathNodeType.Root: case XPathNodeType.Element: if (nav.XmlType == null) return XmlQueryTypeFactory.Type(nav.NodeType, XmlQualifiedNameTest.New(nav.LocalName, nav.NamespaceURI), XmlSchemaComplexType.UntypedAnyType, false); return XmlQueryTypeFactory.Type(nav.NodeType, XmlQualifiedNameTest.New(nav.LocalName, nav.NamespaceURI), nav.XmlType, nav.SchemaInfo.SchemaElement.IsNillable); case XPathNodeType.Attribute: if (nav.XmlType == null) return XmlQueryTypeFactory.Type(nav.NodeType, XmlQualifiedNameTest.New(nav.LocalName, nav.NamespaceURI), DatatypeImplementation.UntypedAtomicType, false); return XmlQueryTypeFactory.Type(nav.NodeType, XmlQualifiedNameTest.New(nav.LocalName, nav.NamespaceURI), nav.XmlType, false); } return XmlQueryTypeFactory.Type(nav.NodeType, XmlQualifiedNameTest.Wildcard, XmlSchemaComplexType.AnyType, false); } // Atomic value return XmlQueryTypeFactory.Type((XmlSchemaSimpleType)item.XmlType, true); } //----------------------------------------------- // Xml collations //----------------------------------------------- ////// Get a collation that was statically created. /// public XmlCollation GetCollation(int index) { Debug.Assert(this.collations != null); return this.collations[index]; } ////// Create a collation from a string. /// public XmlCollation CreateCollation(string collation) { return XmlCollation.Create(collation); } //----------------------------------------------- // Document Ordering and Identity //----------------------------------------------- ////// Compare the relative positions of two navigators. Return -1 if navThis is before navThat, 1 if after, and /// 0 if they are positioned to the same node. /// public int ComparePosition(XPathNavigator navigatorThis, XPathNavigator navigatorThat) { return this.docOrderCmp.Compare(navigatorThis, navigatorThat); } ////// Get a comparer which guarantees a stable ordering among nodes, even those from different documents. /// public IListDocOrderDistinct(IList seq) { if (seq.Count <= 1) return seq; XmlQueryNodeSequence nodeSeq = (XmlQueryNodeSequence) seq; if (nodeSeq == null) nodeSeq = new XmlQueryNodeSequence(seq); return nodeSeq.DocOrderDistinct(this.docOrderCmp); } /// /// Generate a unique string identifier for the specified node. Do this by asking the navigator for an identifier /// that is unique within the document, and then prepend a document index. /// public string GenerateId(XPathNavigator navigator) { return string.Concat("ID", this.docOrderCmp.GetDocumentIndex(navigator).ToString(CultureInfo.InvariantCulture), navigator.UniqueId); } //----------------------------------------------- // Indexes //----------------------------------------------- ////// If an index having the specified Id has already been created over the "context" document, then return it /// in "index" and return true. Otherwise, create a new, empty index and return false. /// public bool FindIndex(XPathNavigator context, int indexId, out XmlILIndex index) { XPathNavigator navRoot; ArrayList docIndexes; Debug.Assert(context != null); // Get root of document navRoot = context.Clone(); navRoot.MoveToRoot(); // Search pre-existing indexes in order to determine whether the specified index has already been created if (this.indexes != null && indexId < this.indexes.Length) { docIndexes = (ArrayList) this.indexes[indexId]; if (docIndexes != null) { // Search for an index defined over the specified document for (int i = 0; i < docIndexes.Count; i += 2) { // If we find a matching document, then return the index saved in the next slot if (((XPathNavigator) docIndexes[i]).IsSamePosition(navRoot)) { index = (XmlILIndex) docIndexes[i + 1]; return true; } } } } // Return a new, empty index index = new XmlILIndex(); return false; } ////// Add a newly built index over the specified "context" document to the existing collection of indexes. /// public void AddNewIndex(XPathNavigator context, int indexId, XmlILIndex index) { XPathNavigator navRoot; ArrayList docIndexes; Debug.Assert(context != null); // Get root of document navRoot = context.Clone(); navRoot.MoveToRoot(); // Ensure that a slot exists for the new index if (this.indexes == null) { this.indexes = new ArrayList[indexId + 4]; } else if (indexId >= this.indexes.Length) { // Resize array ArrayList[] indexesNew = new ArrayList[indexId + 4]; Array.Copy(this.indexes, 0, indexesNew, 0, this.indexes.Length); this.indexes = indexesNew; } docIndexes = (ArrayList) this.indexes[indexId]; if (docIndexes == null) { docIndexes = new ArrayList(); this.indexes[indexId] = docIndexes; } docIndexes.Add(navRoot); docIndexes.Add(index); } //----------------------------------------------- // Output construction //----------------------------------------------- ////// Get output writer object. /// public XmlQueryOutput Output { get { return this.output; } } ////// Start construction of a nested sequence of items. Return a new XmlQueryOutput that will be /// used to construct this new sequence. /// public void StartSequenceConstruction(out XmlQueryOutput output) { // Push current writer this.stkOutput.Push(this.output); // Create new writers output = this.output = new XmlQueryOutput(this, new XmlCachedSequenceWriter()); } ////// End construction of a nested sequence of items and return the items as an IList public IList/// internal class. Return previous XmlQueryOutput. /// EndSequenceConstruction(out XmlQueryOutput output) { IList seq = ((XmlCachedSequenceWriter) this.output.SequenceWriter).ResultSequence; // Restore previous XmlQueryOutput output = this.output = this.stkOutput.Pop(); return seq; } /// /// Start construction of an Rtf. Return a new XmlQueryOutput object that will be used to construct this Rtf. /// public void StartRtfConstruction(string baseUri, out XmlQueryOutput output) { // Push current writer this.stkOutput.Push(this.output); // Create new XmlQueryOutput over an Rtf writer output = this.output = new XmlQueryOutput(this, new XmlEventCache(baseUri, true)); } ////// End construction of an Rtf and return it as an RtfNavigator. Return previous XmlQueryOutput object. /// public XPathNavigator EndRtfConstruction(out XmlQueryOutput output) { XmlEventCache events; events = (XmlEventCache) this.output.Writer; // Restore previous XmlQueryOutput output = this.output = this.stkOutput.Pop(); // Return Rtf as an RtfNavigator events.EndEvents(); return new RtfTreeNavigator(events, this.nameTableQuery); } ////// Construct a new RtfTextNavigator from the specified "text". This is much more efficient than calling /// StartNodeConstruction(), StartRtf(), WriteString(), EndRtf(), and EndNodeConstruction(). /// public XPathNavigator TextRtfConstruction(string text, string baseUri) { return new RtfTextNavigator(text, baseUri); } //----------------------------------------------- // Miscellaneous //----------------------------------------------- ////// Report query execution information to event handler. /// public void SendMessage(string message) { this.ctxt.OnXsltMessageEncountered(message); } ////// Throw an Xml exception having the specified message text. /// public void ThrowException(string text) { throw new XslTransformException(text); } ////// Position navThis to the same location as navThat. /// internal static XPathNavigator SyncToNavigator(XPathNavigator navigatorThis, XPathNavigator navigatorThat) { if (navigatorThis == null || !navigatorThis.MoveTo(navigatorThat)) return navigatorThat.Clone(); return navigatorThis; } ////// Function is called in Debug mode on each time context node change. /// public static int OnCurrentNodeChanged(XPathNavigator currentNode) { IXmlLineInfo lineInfo = currentNode as IXmlLineInfo; // In case of a namespace node, check whether it is inherited or locally defined if (lineInfo != null && ! (currentNode.NodeType == XPathNodeType.Namespace && IsInheritedNamespace(currentNode))) { OnCurrentNodeChanged2(currentNode.BaseURI, lineInfo.LineNumber, lineInfo.LinePosition); } return 0; } // 'true' if current Namespace "inherited" from it's parent. Not defined localy. private static bool IsInheritedNamespace(XPathNavigator node) { Debug.Assert(node.NodeType == XPathNodeType.Namespace); XPathNavigator nav = node.Clone(); if (nav.MoveToParent()) { if (nav.MoveToFirstNamespace(XPathNamespaceScope.Local)) { do { if ((object)nav.LocalName == (object)node.LocalName) { return false; } } while (nav.MoveToNextNamespace(XPathNamespaceScope.Local)); } } return true; } private static void OnCurrentNodeChanged2(string baseUri, int lineNumber, int linePosition) {} } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- StylusPointPropertyInfoDefaults.cs
- FontStretchConverter.cs
- XmlSchemaIdentityConstraint.cs
- References.cs
- NavigationCommands.cs
- DependencyStoreSurrogate.cs
- XmlNodeChangedEventManager.cs
- CodeObjectCreateExpression.cs
- ScopelessEnumAttribute.cs
- BindingWorker.cs
- ScriptingSectionGroup.cs
- OleDbErrorCollection.cs
- DataError.cs
- LinqDataSourceSelectEventArgs.cs
- InvokeWebServiceDesigner.cs
- SqlCacheDependencyDatabaseCollection.cs
- ChangeInterceptorAttribute.cs
- VisualStateChangedEventArgs.cs
- BuildProvidersCompiler.cs
- ItemChangedEventArgs.cs
- ChineseLunisolarCalendar.cs
- CellTreeNode.cs
- MonitoringDescriptionAttribute.cs
- GenerateTemporaryTargetAssembly.cs
- SHA512.cs
- SendKeys.cs
- DbDataAdapter.cs
- TreeNode.cs
- TraceXPathNavigator.cs
- WinEventHandler.cs
- CodeTypeReferenceExpression.cs
- Point4DConverter.cs
- InternalDispatchObject.cs
- PagePropertiesChangingEventArgs.cs
- HttpTransportBindingElement.cs
- ConsoleCancelEventArgs.cs
- DebugView.cs
- CodeTypeMemberCollection.cs
- BindingGroup.cs
- ConfigurationLocation.cs
- AutomationPropertyInfo.cs
- ColorContextHelper.cs
- FilePrompt.cs
- XmlSerializerVersionAttribute.cs
- DataControlLinkButton.cs
- XmlDataSourceNodeDescriptor.cs
- baseaxisquery.cs
- SqlNode.cs
- ScalarType.cs
- TableItemStyle.cs
- TypeLibConverter.cs
- Int32CAMarshaler.cs
- ViewStateModeByIdAttribute.cs
- HostingEnvironmentException.cs
- Color.cs
- XMLSchema.cs
- CompositeActivityCodeGenerator.cs
- SessionStateContainer.cs
- ToolStripSplitButton.cs
- Polyline.cs
- InitializerFacet.cs
- WebBaseEventKeyComparer.cs
- SectionVisual.cs
- RoleManagerSection.cs
- StreamGeometry.cs
- ContainerCodeDomSerializer.cs
- MetadataUtilsSmi.cs
- SqlProviderServices.cs
- ManagementEventArgs.cs
- FixedPageAutomationPeer.cs
- AssemblySettingAttributes.cs
- WebConfigurationHost.cs
- DataGridrowEditEndingEventArgs.cs
- XmlAutoDetectWriter.cs
- VectorAnimation.cs
- AttributeCollection.cs
- IncrementalReadDecoders.cs
- XmlElementAttributes.cs
- AsyncCompletedEventArgs.cs
- MaskPropertyEditor.cs
- MarkupCompiler.cs
- XmlSchema.cs
- InfoCardAsymmetricCrypto.cs
- Msec.cs
- Rectangle.cs
- BooleanFunctions.cs
- ListViewAutomationPeer.cs
- XmlNode.cs
- TypeSource.cs
- AttributeCollection.cs
- WebEventTraceProvider.cs
- DataGridViewCellMouseEventArgs.cs
- OdbcParameter.cs
- EncodingInfo.cs
- WorkerRequest.cs
- MD5Cng.cs
- BaseProcessor.cs
- FileLoadException.cs
- EventDescriptor.cs
- ContextMenu.cs