1 //------------------------------------------------------------------------------
2 // <copyright file="XmlQueryContext.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
9 using System
.Collections
;
10 using System
.Collections
.Generic
;
11 using System
.ComponentModel
;
12 using System
.Diagnostics
;
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
;
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:
26 /// 1. Input data sources, including the default data source if one exists
27 /// 2. Extension objects
28 /// 3. External parameters
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
42 /// This constructor is internal so that external users cannot construct it (and therefore we do not have to test it separately).
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
);
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 //-----------------------------------------------
77 //-----------------------------------------------
80 /// Returns the name table that should be used in the query to atomize search names and to load
83 public XmlNameTable QueryNameTable
{
84 get { return this.readerSettings.NameTable; }
88 /// Returns the name table used by the default data source, or null if there is no default data source.
90 public XmlNameTable DefaultNameTable
{
91 get { return this.defaultDataSource != null ? this.defaultDataSource.NameTable : null; }
95 /// Return the document which is queried by default--i.e. no data source is explicitly selected in the query.
97 public XPathNavigator DefaultDataSource
{
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
;
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.
112 [ResourceConsumption(ResourceScope
.Machine
)]
113 [ResourceExposure(ResourceScope
.Machine
)]
114 public XPathNavigator
GetDataSource(string uriRelative
, string uriBase
) {
116 Uri uriResolvedBase
, uriResolved
;
117 XPathNavigator nav
= null;
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
;
127 // Get the entity from the resolver and ensure it is cached as a document
128 input
= this.dataSources
.GetEntity(uriResolved
, null, 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
141 catch (Exception e
) {
142 if (!XmlException
.IsCatchableException(e
)) {
145 throw new XslTransformException(e
, Res
.XmlIl_DocumentLoadError
, uriRelative
);
152 /// Ensure that "dataSource" is cached as an XPathDocument and return a navigator over the document.
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);
162 // Create WhitespaceRuleReader if whitespace should be stripped
163 return new XPathDocument(WhitespaceRuleReader
.CreateReader(reader
, this.wsRules
), XmlSpace
.Preserve
).CreateNavigator();
166 // Always close reader that was opened here
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 //-----------------------------------------------
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.
195 public object GetParameter(string localName
, string namespaceUri
) {
196 return (this.argList
!= null) ? this.argList
.GetParam(localName
, namespaceUri
) : null;
200 //-----------------------------------------------
202 //-----------------------------------------------
205 /// Return the extension object that is mapped to the specified namespace, or null if no object is mapped.
207 public object GetLateBoundObject(string namespaceUri
) {
208 return (this.argList
!= null) ? this.argList
.GetExtensionObject(namespaceUri
) : null;
212 /// Return true if the late bound object identified by "namespaceUri" contains a method that matches "name".
214 public bool LateBoundFunctionExists(string name
, string namespaceUri
) {
217 if (this.argList
== null)
220 instance
= this.argList
.GetExtensionObject(namespaceUri
);
221 if (instance
== null)
224 return new XmlExtensionFunction(name
, namespaceUri
, -1, instance
.GetType(), XmlQueryRuntime
.LateBoundFlags
).CanBind();
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.
231 public IList
<XPathItem
> InvokeXsltLateBoundFunction(string name
, string namespaceUri
, IList
<XPathItem
>[] args
) {
233 object[] objActualArgs
;
234 XmlQueryType xmlTypeFormalArg
;
235 Type clrTypeFormalArg
;
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
]);
269 objActualArgs
[i
] = XsltConvert
.ToNodeSet(args
[i
]);
271 case XmlTypeCode
.Item
:
272 objActualArgs
[i
] = args
[i
];
275 Debug
.Fail("This XmlTypeCode should never be inferred from a Clr type: " + xmlTypeFormalArg
.TypeCode
);
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 //-----------------------------------------------
298 //-----------------------------------------------
301 /// Fire the XsltMessageEncounteredEvent, passing the specified text as the message.
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
));
309 Console
.WriteLine(message
);
314 /// Simple implementation of XsltMessageEncounteredEventArgs.
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; }