Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Data.SqlXml / System / Xml / Xsl / Runtime / XmlQueryContext.cs
blob644bfa8c254b03db6a9d6ac5b3ac73b9feeaf688
1 //------------------------------------------------------------------------------
2 // <copyright file="XmlQueryContext.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
8 using System;
9 using System.Collections;
10 using System.Collections.Generic;
11 using System.ComponentModel;
12 using System.Diagnostics;
13 using System.IO;
14 using System.Xml;
15 using System.Xml.Schema;
16 using System.Xml.XPath;
17 using System.Runtime.Versioning;
19 namespace System.Xml.Xsl.Runtime {
20 using Res = System.Xml.Utils.Res;
22 /// <summary>
23 /// The context of a query consists of all user-provided information which influences the operation of the
24 /// query. The context manages the following information:
25 ///
26 /// 1. Input data sources, including the default data source if one exists
27 /// 2. Extension objects
28 /// 3. External parameters
29 /// </summary>
30 [EditorBrowsable(EditorBrowsableState.Never)]
31 public sealed class XmlQueryContext {
32 private XmlQueryRuntime runtime;
33 private XPathNavigator defaultDataSource;
34 private XmlResolver dataSources;
35 private Hashtable dataSourceCache;
36 private XsltArgumentList argList;
37 private XmlExtensionFunctionTable extFuncsLate;
38 private WhitespaceRuleLookup wsRules;
39 private QueryReaderSettings readerSettings; // If we create reader out of stream we will use these settings
41 /// <summary>
42 /// This constructor is internal so that external users cannot construct it (and therefore we do not have to test it separately).
43 /// </summary>
44 [ResourceConsumption(ResourceScope.Machine)]
45 [ResourceExposure(ResourceScope.Machine)]
46 internal XmlQueryContext(XmlQueryRuntime runtime, object defaultDataSource, XmlResolver dataSources, XsltArgumentList argList, WhitespaceRuleLookup wsRules) {
47 this.runtime = runtime;
48 this.dataSources = dataSources;
49 this.dataSourceCache = new Hashtable();
50 this.argList = argList;
51 this.wsRules = wsRules;
53 if (defaultDataSource is XmlReader) {
54 this.readerSettings = new QueryReaderSettings((XmlReader) defaultDataSource);
56 else {
57 // Consider allowing users to set DefaultReaderSettings in XsltArgumentList
58 // readerSettings = argList.DefaultReaderSettings;
59 this.readerSettings = new QueryReaderSettings(new NameTable());
62 if (defaultDataSource is string) {
63 // Load the default document from a Uri
64 this.defaultDataSource = GetDataSource(defaultDataSource as string, null);
66 if (this.defaultDataSource == null)
67 throw new XslTransformException(Res.XmlIl_UnknownDocument, defaultDataSource as string);
69 else if (defaultDataSource != null) {
70 this.defaultDataSource = ConstructDocument(defaultDataSource, null, null);
75 //-----------------------------------------------
76 // Input data sources
77 //-----------------------------------------------
79 /// <summary>
80 /// Returns the name table that should be used in the query to atomize search names and to load
81 /// new documents.
82 /// </summary>
83 public XmlNameTable QueryNameTable {
84 get { return this.readerSettings.NameTable; }
87 /// <summary>
88 /// Returns the name table used by the default data source, or null if there is no default data source.
89 /// </summary>
90 public XmlNameTable DefaultNameTable {
91 get { return this.defaultDataSource != null ? this.defaultDataSource.NameTable : null; }
94 /// <summary>
95 /// Return the document which is queried by default--i.e. no data source is explicitly selected in the query.
96 /// </summary>
97 public XPathNavigator DefaultDataSource {
98 get {
99 // Throw exception if there is no default data source to return
100 if (this.defaultDataSource == null)
101 throw new XslTransformException(Res.XmlIl_NoDefaultDocument, string.Empty);
103 return this.defaultDataSource;
107 /// <summary>
108 /// Fetch the data source specified by "uriRelative" and "uriBase" from the XmlResolver that the user provided.
109 /// If the resolver returns a stream or reader, create an instance of XPathDocument. If the resolver returns an
110 /// XPathNavigator, return the navigator. Throw an exception if no data source was found.
111 /// </summary>
112 [ResourceConsumption(ResourceScope.Machine)]
113 [ResourceExposure(ResourceScope.Machine)]
114 public XPathNavigator GetDataSource(string uriRelative, string uriBase) {
115 object input;
116 Uri uriResolvedBase, uriResolved;
117 XPathNavigator nav = null;
119 try {
120 // If the data source has already been retrieved, then return the data source from the cache.
121 uriResolvedBase = (uriBase != null) ? this.dataSources.ResolveUri(null, uriBase) : null;
122 uriResolved = this.dataSources.ResolveUri(uriResolvedBase, uriRelative);
123 if (uriResolved != null)
124 nav = this.dataSourceCache[uriResolved] as XPathNavigator;
126 if (nav == null) {
127 // Get the entity from the resolver and ensure it is cached as a document
128 input = this.dataSources.GetEntity(uriResolved, null, null);
130 if (input != null) {
131 // Construct a document from the entity and add the document to the cache
132 nav = ConstructDocument(input, uriRelative, uriResolved);
133 this.dataSourceCache.Add(uriResolved, nav);
137 catch (XslTransformException) {
138 // Don't need to wrap XslTransformException
139 throw;
141 catch (Exception e) {
142 if (!XmlException.IsCatchableException(e)) {
143 throw;
145 throw new XslTransformException(e, Res.XmlIl_DocumentLoadError, uriRelative);
148 return nav;
151 /// <summary>
152 /// Ensure that "dataSource" is cached as an XPathDocument and return a navigator over the document.
153 /// </summary>
154 private XPathNavigator ConstructDocument(object dataSource, string uriRelative, Uri uriResolved) {
155 Debug.Assert(dataSource != null, "GetType() below assumes dataSource is not null");
156 Stream stream = dataSource as Stream;
157 if (stream != null) {
158 // Create document from stream
159 XmlReader reader = readerSettings.CreateReader(stream, uriResolved != null ? uriResolved.ToString() : null);
161 try {
162 // Create WhitespaceRuleReader if whitespace should be stripped
163 return new XPathDocument(WhitespaceRuleReader.CreateReader(reader, this.wsRules), XmlSpace.Preserve).CreateNavigator();
165 finally {
166 // Always close reader that was opened here
167 reader.Close();
170 else if (dataSource is XmlReader) {
171 // Create document from reader
172 // Create WhitespaceRuleReader if whitespace should be stripped
173 return new XPathDocument(WhitespaceRuleReader.CreateReader(dataSource as XmlReader, this.wsRules), XmlSpace.Preserve).CreateNavigator();
175 else if (dataSource is IXPathNavigable) {
176 if (this.wsRules != null)
177 throw new XslTransformException(Res.XmlIl_CantStripNav, string.Empty);
179 return (dataSource as IXPathNavigable).CreateNavigator();
182 Debug.Assert(uriRelative != null, "Relative URI should not be null");
183 throw new XslTransformException(Res.XmlIl_CantResolveEntity, uriRelative, dataSource.GetType().ToString());
187 //-----------------------------------------------
188 // External parameters
189 //-----------------------------------------------
191 /// <summary>
192 /// Get a named parameter from the external argument list. Return null if no argument list was provided, or if
193 /// there is no parameter by that name.
194 /// </summary>
195 public object GetParameter(string localName, string namespaceUri) {
196 return (this.argList != null) ? this.argList.GetParam(localName, namespaceUri) : null;
200 //-----------------------------------------------
201 // Extension objects
202 //-----------------------------------------------
204 /// <summary>
205 /// Return the extension object that is mapped to the specified namespace, or null if no object is mapped.
206 /// </summary>
207 public object GetLateBoundObject(string namespaceUri) {
208 return (this.argList != null) ? this.argList.GetExtensionObject(namespaceUri) : null;
211 /// <summary>
212 /// Return true if the late bound object identified by "namespaceUri" contains a method that matches "name".
213 /// </summary>
214 public bool LateBoundFunctionExists(string name, string namespaceUri) {
215 object instance;
217 if (this.argList == null)
218 return false;
220 instance = this.argList.GetExtensionObject(namespaceUri);
221 if (instance == null)
222 return false;
224 return new XmlExtensionFunction(name, namespaceUri, -1, instance.GetType(), XmlQueryRuntime.LateBoundFlags).CanBind();
227 /// <summary>
228 /// Get a late-bound extension object from the external argument list. Bind to a method on the object and invoke it,
229 /// passing "args" as arguments.
230 /// </summary>
231 public IList<XPathItem> InvokeXsltLateBoundFunction(string name, string namespaceUri, IList<XPathItem>[] args) {
232 object instance;
233 object[] objActualArgs;
234 XmlQueryType xmlTypeFormalArg;
235 Type clrTypeFormalArg;
236 object objRet;
238 // Get external object instance from argument list (throw if either the list or the instance doesn't exist)
239 instance = (this.argList != null) ? this.argList.GetExtensionObject(namespaceUri) : null;
240 if (instance == null)
241 throw new XslTransformException(Res.XmlIl_UnknownExtObj, namespaceUri);
243 // Bind to a method on the instance object
244 if (this.extFuncsLate == null)
245 this.extFuncsLate = new XmlExtensionFunctionTable();
247 // Bind to the instance, looking for a matching method (throws if no matching method)
248 XmlExtensionFunction extFunc = this.extFuncsLate.Bind(name, namespaceUri, args.Length, instance.GetType(), XmlQueryRuntime.LateBoundFlags);
250 // Create array which will contain the actual arguments
251 objActualArgs = new object[args.Length];
253 for (int i = 0; i < args.Length; i++) {
254 // 1. Assume that the input value can only have one of the following 5 Xslt types:
255 // xs:double, xs:string, xs:boolean, node* (can be rtf)
256 // 2. Convert each Rtf value to a NodeSet containing one node. Now the value may only have one of the 4 Xslt types.
257 // 3. Convert from one of the 4 Xslt internal types to the Xslt internal type which is closest to the formal
258 // argument's Xml type (inferred from the Clr type of the formal argument).
260 xmlTypeFormalArg = extFunc.GetXmlArgumentType(i);
261 switch (xmlTypeFormalArg.TypeCode) {
262 case XmlTypeCode.Boolean: objActualArgs[i] = XsltConvert.ToBoolean(args[i]); break;
263 case XmlTypeCode.Double: objActualArgs[i] = XsltConvert.ToDouble(args[i]); break;
264 case XmlTypeCode.String: objActualArgs[i] = XsltConvert.ToString(args[i]); break;
265 case XmlTypeCode.Node:
266 if (xmlTypeFormalArg.IsSingleton)
267 objActualArgs[i] = XsltConvert.ToNode(args[i]);
268 else
269 objActualArgs[i] = XsltConvert.ToNodeSet(args[i]);
270 break;
271 case XmlTypeCode.Item:
272 objActualArgs[i] = args[i];
273 break;
274 default:
275 Debug.Fail("This XmlTypeCode should never be inferred from a Clr type: " + xmlTypeFormalArg.TypeCode);
276 break;
279 // 4. Change the Clr representation to the Clr type of the formal argument
280 clrTypeFormalArg = extFunc.GetClrArgumentType(i);
281 if (xmlTypeFormalArg.TypeCode == XmlTypeCode.Item || !clrTypeFormalArg.IsAssignableFrom(objActualArgs[i].GetType()))
282 objActualArgs[i] = this.runtime.ChangeTypeXsltArgument(xmlTypeFormalArg, objActualArgs[i], clrTypeFormalArg);
285 // 1. Invoke the late bound method
286 objRet = extFunc.Invoke(instance, objActualArgs);
288 // 2. Convert to IList<XPathItem>
289 if (objRet == null && extFunc.ClrReturnType == XsltConvert.VoidType)
290 return XmlQueryNodeSequence.Empty;
292 return (IList<XPathItem>) this.runtime.ChangeTypeXsltResult(XmlQueryTypeFactory.ItemS, objRet);
296 //-----------------------------------------------
297 // Event
298 //-----------------------------------------------
300 /// <summary>
301 /// Fire the XsltMessageEncounteredEvent, passing the specified text as the message.
302 /// </summary>
303 public void OnXsltMessageEncountered(string message) {
304 XsltMessageEncounteredEventHandler onMessage = (this.argList != null) ? argList.xsltMessageEncountered : null;
306 if (onMessage != null)
307 onMessage(this, new XmlILQueryEventArgs(message));
308 else
309 Console.WriteLine(message);
313 /// <summary>
314 /// Simple implementation of XsltMessageEncounteredEventArgs.
315 /// </summary>
316 internal class XmlILQueryEventArgs : XsltMessageEncounteredEventArgs {
317 private string message;
319 public XmlILQueryEventArgs(string message) {
320 this.message = message;
323 public override string Message {
324 get { return this.message; }