Welcome to mirror list, hosted at ThFree Co, Russian Federation.

XmlQueryContext.cs « Runtime « Xsl « Xml « System « System.Data.SqlXml « referencesource « class « mcs - github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 644bfa8c254b03db6a9d6ac5b3ac73b9feeaf688 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
//------------------------------------------------------------------------------
// <copyright file="XmlQueryContext.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
//------------------------------------------------------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Xml.XPath;
using System.Runtime.Versioning;

namespace System.Xml.Xsl.Runtime {
    using Res = System.Xml.Utils.Res;

    /// <summary>
    /// The context of a query consists of all user-provided information which influences the operation of the
    /// query. The context manages the following information:
    ///
    ///   1. Input data sources, including the default data source if one exists
    ///   2. Extension objects
    ///   3. External parameters
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class XmlQueryContext {
        private XmlQueryRuntime runtime;
        private XPathNavigator defaultDataSource;
        private XmlResolver dataSources;
        private Hashtable dataSourceCache;
        private XsltArgumentList argList;
        private XmlExtensionFunctionTable extFuncsLate;
        private WhitespaceRuleLookup wsRules;
        private QueryReaderSettings readerSettings; // If we create reader out of stream we will use these settings

        /// <summary>
        /// This constructor is internal so that external users cannot construct it (and therefore we do not have to test it separately).
        /// </summary>
        [ResourceConsumption(ResourceScope.Machine)]
        [ResourceExposure(ResourceScope.Machine)]
        internal XmlQueryContext(XmlQueryRuntime runtime, object defaultDataSource, XmlResolver dataSources, XsltArgumentList argList, WhitespaceRuleLookup wsRules) {
            this.runtime = runtime;
            this.dataSources = dataSources;
            this.dataSourceCache = new Hashtable();
            this.argList = argList;
            this.wsRules = wsRules;

            if (defaultDataSource is XmlReader) {
                this.readerSettings = new QueryReaderSettings((XmlReader) defaultDataSource);
            }
            else {
                // Consider allowing users to set DefaultReaderSettings in XsltArgumentList
                // readerSettings = argList.DefaultReaderSettings;
                this.readerSettings = new QueryReaderSettings(new NameTable());
            }

            if (defaultDataSource is string) {
                // Load the default document from a Uri
                this.defaultDataSource = GetDataSource(defaultDataSource as string, null);

                if (this.defaultDataSource == null)
                    throw new XslTransformException(Res.XmlIl_UnknownDocument, defaultDataSource as string);
            }
            else if (defaultDataSource != null) {
                this.defaultDataSource = ConstructDocument(defaultDataSource, null, null);
            }
        }


        //-----------------------------------------------
        // Input data sources
        //-----------------------------------------------

        /// <summary>
        /// Returns the name table that should be used in the query to atomize search names and to load
        /// new documents.
        /// </summary>
        public XmlNameTable QueryNameTable {
            get { return this.readerSettings.NameTable; }
        }

        /// <summary>
        /// Returns the name table used by the default data source, or null if there is no default data source.
        /// </summary>
        public XmlNameTable DefaultNameTable {
            get { return this.defaultDataSource != null ? this.defaultDataSource.NameTable : null; }
        }

        /// <summary>
        /// Return the document which is queried by default--i.e. no data source is explicitly selected in the query.
        /// </summary>
        public XPathNavigator DefaultDataSource {
            get {
                // Throw exception if there is no default data source to return
                if (this.defaultDataSource == null)
                    throw new XslTransformException(Res.XmlIl_NoDefaultDocument, string.Empty);

                return this.defaultDataSource;
            }
        }

        /// <summary>
        /// Fetch the data source specified by "uriRelative" and "uriBase" from the XmlResolver that the user provided.
        /// If the resolver returns a stream or reader, create an instance of XPathDocument.  If the resolver returns an
        /// XPathNavigator, return the navigator.  Throw an exception if no data source was found.
        /// </summary>
        [ResourceConsumption(ResourceScope.Machine)]
        [ResourceExposure(ResourceScope.Machine)]
        public XPathNavigator GetDataSource(string uriRelative, string uriBase) {
            object input;
            Uri uriResolvedBase, uriResolved;
            XPathNavigator nav = null;

            try {
                // If the data source has already been retrieved, then return the data source from the cache.
                uriResolvedBase = (uriBase != null) ? this.dataSources.ResolveUri(null, uriBase) : null;
                uriResolved = this.dataSources.ResolveUri(uriResolvedBase, uriRelative);
                if (uriResolved != null)
                    nav = this.dataSourceCache[uriResolved] as XPathNavigator;

                if (nav == null) {
                    // Get the entity from the resolver and ensure it is cached as a document
                    input = this.dataSources.GetEntity(uriResolved, null, null);

                    if (input != null) {
                        // Construct a document from the entity and add the document to the cache
                        nav = ConstructDocument(input, uriRelative, uriResolved);
                        this.dataSourceCache.Add(uriResolved, nav);
                    }
                }
            }
            catch (XslTransformException) {
                // Don't need to wrap XslTransformException
                throw;
            }
            catch (Exception e) {
                if (!XmlException.IsCatchableException(e)) {
                    throw;
                }
                throw new XslTransformException(e, Res.XmlIl_DocumentLoadError, uriRelative);
            }

            return nav;
        }

        /// <summary>
        /// Ensure that "dataSource" is cached as an XPathDocument and return a navigator over the document.
        /// </summary>
        private XPathNavigator ConstructDocument(object dataSource, string uriRelative, Uri uriResolved) {
            Debug.Assert(dataSource != null, "GetType() below assumes dataSource is not null");
            Stream stream = dataSource as Stream;
            if (stream != null) {
                // Create document from stream
                XmlReader reader = readerSettings.CreateReader(stream, uriResolved != null ? uriResolved.ToString() : null);

                try {
                    // Create WhitespaceRuleReader if whitespace should be stripped
                    return new XPathDocument(WhitespaceRuleReader.CreateReader(reader, this.wsRules), XmlSpace.Preserve).CreateNavigator();
                }
                finally {
                    // Always close reader that was opened here
                    reader.Close();
                }
            }
            else if (dataSource is XmlReader) {
                // Create document from reader
                // Create WhitespaceRuleReader if whitespace should be stripped
                return new XPathDocument(WhitespaceRuleReader.CreateReader(dataSource as XmlReader, this.wsRules), XmlSpace.Preserve).CreateNavigator();
            }
            else if (dataSource is IXPathNavigable) {
                if (this.wsRules != null)
                    throw new XslTransformException(Res.XmlIl_CantStripNav, string.Empty);

                return (dataSource as IXPathNavigable).CreateNavigator();
            }

            Debug.Assert(uriRelative != null, "Relative URI should not be null");
            throw new XslTransformException(Res.XmlIl_CantResolveEntity, uriRelative, dataSource.GetType().ToString());
        }


        //-----------------------------------------------
        // External parameters
        //-----------------------------------------------

        /// <summary>
        /// Get a named parameter from the external argument list.  Return null if no argument list was provided, or if
        /// there is no parameter by that name.
        /// </summary>
        public object GetParameter(string localName, string namespaceUri) {
            return (this.argList != null) ? this.argList.GetParam(localName, namespaceUri) : null;
        }


        //-----------------------------------------------
        // Extension objects
        //-----------------------------------------------

        /// <summary>
        /// Return the extension object that is mapped to the specified namespace, or null if no object is mapped.
        /// </summary>
        public object GetLateBoundObject(string namespaceUri) {
            return (this.argList != null) ? this.argList.GetExtensionObject(namespaceUri) : null;
        }

        /// <summary>
        /// Return true if the late bound object identified by "namespaceUri" contains a method that matches "name".
        /// </summary>
        public bool LateBoundFunctionExists(string name, string namespaceUri) {
            object instance;

            if (this.argList == null)
                return false;

            instance = this.argList.GetExtensionObject(namespaceUri);
            if (instance == null)
                return false;

            return new XmlExtensionFunction(name, namespaceUri, -1, instance.GetType(), XmlQueryRuntime.LateBoundFlags).CanBind();
        }

        /// <summary>
        /// Get a late-bound extension object from the external argument list.  Bind to a method on the object and invoke it,
        /// passing "args" as arguments.
        /// </summary>
        public IList<XPathItem> InvokeXsltLateBoundFunction(string name, string namespaceUri, IList<XPathItem>[] args) {
            object instance;
            object[] objActualArgs;
            XmlQueryType xmlTypeFormalArg;
            Type clrTypeFormalArg;
            object objRet;

            // Get external object instance from argument list (throw if either the list or the instance doesn't exist)
            instance = (this.argList != null) ? this.argList.GetExtensionObject(namespaceUri) : null;
            if (instance == null)
                throw new XslTransformException(Res.XmlIl_UnknownExtObj, namespaceUri);

            // Bind to a method on the instance object
            if (this.extFuncsLate == null)
                this.extFuncsLate = new XmlExtensionFunctionTable();

            // Bind to the instance, looking for a matching method (throws if no matching method)
            XmlExtensionFunction extFunc = this.extFuncsLate.Bind(name, namespaceUri, args.Length, instance.GetType(), XmlQueryRuntime.LateBoundFlags);

            // Create array which will contain the actual arguments
            objActualArgs = new object[args.Length];

            for (int i = 0; i < args.Length; i++) {
                // 1. Assume that the input value can only have one of the following 5 Xslt types:
                //      xs:double, xs:string, xs:boolean, node* (can be rtf)
                // 2. Convert each Rtf value to a NodeSet containing one node.  Now the value may only have one of the 4 Xslt types.
                // 3. Convert from one of the 4 Xslt internal types to the Xslt internal type which is closest to the formal
                //    argument's Xml type (inferred from the Clr type of the formal argument).

                xmlTypeFormalArg = extFunc.GetXmlArgumentType(i);
                switch (xmlTypeFormalArg.TypeCode) {
                    case XmlTypeCode.Boolean:   objActualArgs[i] = XsltConvert.ToBoolean(args[i]); break;
                    case XmlTypeCode.Double:    objActualArgs[i] = XsltConvert.ToDouble(args[i]); break;
                    case XmlTypeCode.String:    objActualArgs[i] = XsltConvert.ToString(args[i]); break;
                    case XmlTypeCode.Node:
                        if (xmlTypeFormalArg.IsSingleton)
                            objActualArgs[i] = XsltConvert.ToNode(args[i]);
                        else
                            objActualArgs[i] = XsltConvert.ToNodeSet(args[i]);
                        break;
                    case XmlTypeCode.Item:
                        objActualArgs[i] = args[i];
                        break;
                    default:
                        Debug.Fail("This XmlTypeCode should never be inferred from a Clr type: " + xmlTypeFormalArg.TypeCode);
                        break;
                }

                // 4. Change the Clr representation to the Clr type of the formal argument
                clrTypeFormalArg = extFunc.GetClrArgumentType(i);
                if (xmlTypeFormalArg.TypeCode == XmlTypeCode.Item || !clrTypeFormalArg.IsAssignableFrom(objActualArgs[i].GetType()))
                    objActualArgs[i] = this.runtime.ChangeTypeXsltArgument(xmlTypeFormalArg, objActualArgs[i], clrTypeFormalArg);
            }

            // 1. Invoke the late bound method
            objRet = extFunc.Invoke(instance, objActualArgs);

            // 2. Convert to IList<XPathItem>
            if (objRet == null && extFunc.ClrReturnType == XsltConvert.VoidType)
                return XmlQueryNodeSequence.Empty;

            return (IList<XPathItem>) this.runtime.ChangeTypeXsltResult(XmlQueryTypeFactory.ItemS, objRet);
        }


        //-----------------------------------------------
        // Event
        //-----------------------------------------------

        /// <summary>
        /// Fire the XsltMessageEncounteredEvent, passing the specified text as the message.
        /// </summary>
        public void OnXsltMessageEncountered(string message) {
            XsltMessageEncounteredEventHandler onMessage = (this.argList != null) ? argList.xsltMessageEncountered : null;

            if (onMessage != null)
                onMessage(this, new XmlILQueryEventArgs(message));
            else
                Console.WriteLine(message);
        }
    }

    /// <summary>
    /// Simple implementation of XsltMessageEncounteredEventArgs.
    /// </summary>
    internal class XmlILQueryEventArgs : XsltMessageEncounteredEventArgs {
        private string message;

        public XmlILQueryEventArgs(string message) {
            this.message = message;
        }

        public override string Message {
            get { return this.message; }
        }
    }
}