Project

Profile

Help

How to connect?
Download (24.6 KB) Statistics
| Branch: | Tag: | Revision:

he / latest9.1 / bn / csource / api / Saxon.Api / XQuery.cs @ e090dcb1

1
using System;
2
using System.IO;
3
using System.Xml;
4
using System.Collections;
5
using JConfiguration = net.sf.saxon.Configuration;
6
using net.sf.saxon.om;
7
using net.sf.saxon.value;
8
using net.sf.saxon.query;
9
using net.sf.saxon.dotnet;
10
using JXPathException = net.sf.saxon.trans.XPathException;
11
using JStreamSource = javax.xml.transform.stream.StreamSource;
12

    
13

    
14
namespace Saxon.Api
15
{
16

    
17
    /// <summary>
18
    /// An XQueryCompiler object allows XQuery queries to be compiled.
19
    /// </summary>
20
    /// <remarks>
21
    /// <para>To construct an <c>XQueryCompiler</c>, use the factory method
22
    /// <c>newXQueryCompiler</c> on the <c>Processor</c> object.</para>
23
    /// <para>The <c>XQueryCompiler</c> holds information that represents the static context
24
    /// for the queries that it compiles. This information remains intact after performing
25
    /// a compilation. An <c>XQueryCompiler</c> may therefore be used repeatedly to compile multiple
26
    /// queries. Any changes made to the <c>XQueryCompiler</c> (that is, to the
27
    /// static context) do not affect queries that have already been compiled.
28
    /// An <c>XQueryCompiler</c> may be used concurrently in multiple threads, but
29
    /// it should not then be modified once initialized.</para>
30
    /// 
31
    /// </remarks>
32

    
33
    [Serializable]
34
    public class XQueryCompiler
35
    {
36

    
37
        private JConfiguration config;
38
        private StaticQueryContext env;
39
        private IQueryResolver moduleResolver;
40
        private IList errorList;
41

    
42
        // internal constructor: the public interface is a factory method
43
        // on the Processor object
44

    
45
        internal XQueryCompiler(Processor processor)
46
        {
47
            this.config = processor.config;
48
            this.env = new StaticQueryContext(config);
49
            env.setModuleURIResolver(new DotNetStandardModuleURIResolver(processor.XmlResolver));
50
        }
51

    
52
        /// <summary>
53
        /// Declare a namespace for use by the query. This has the same
54
        /// status as a namespace appearing within the query prolog (though
55
        /// a declaration in the query prolog of the same prefix will take
56
        /// precedence)
57
        /// </summary>
58
        /// <param name="prefix">The namespace prefix to be declared. Use
59
        /// a zero-length string to declare the default namespace (that is, the
60
        /// default namespace for elements and types).</param>
61
        /// <param name="uri">The namespace URI. It is possible to specify
62
        /// a zero-length string to "undeclare" a namespace.</param>
63

    
64
        public void DeclareNamespace(String prefix, String uri)
65
        {
66
            env.declareNamespace(prefix, uri);
67
        }
68

    
69
        /// <summary>
70
        /// The base URI of the query, which forms part of the static context
71
        /// of the query. This is used for resolving any relative URIs appearing
72
        /// within the query, for example in references to library modules, schema
73
        /// locations, or as an argument to the <c>doc()</c> function.
74
        /// </summary>
75

    
76

    
77
        public String BaseUri
78
        {
79
            get { return env.getBaseURI(); }
80
            set { env.setBaseURI(value); }
81
        }
82

    
83
        /// <summary>
84
        /// This property indicates whether XQuery Update syntax is accepted. The default
85
        /// value is false. This property must be set to true before compiling a query that
86
        /// uses update syntax.
87
        /// </summary>
88
        /// <remarks>
89
        /// <para>This propery must be set to true before any query can be compiled
90
        /// that uses updating syntax. This applies even if the query is not actually an updating
91
        /// query (for example, a copy-modify expression). XQuery Update syntax is accepted
92
        /// only by Saxon-SA. Non-updating queries are accepted regardless of the value of this
93
        /// property.</para>
94
        /// <para><i>Property added in Saxon 9.1</i></para></remarks>
95

    
96

    
97
        public bool UpdatingEnabled
98
        {
99
            get { return env.isUpdatingEnabled(); }
100
            set { env.setUpdatingEnabled(value); }
101
        }
102

    
103

    
104
        /// <summary>
105
        /// A user-supplied <c>IQueryResolver</c> used to resolve location hints appearing in an
106
        /// <c>import module</c> declaration.
107
        /// </summary>
108
        /// <remarks>
109
        /// <para>In the absence of a user-supplied <c>QueryResolver</c>, an <c>import module</c> declaration
110
        /// is interpreted as follows. First, if the module URI identifies an already loaded module, that module
111
        /// is used and the location hints are ignored. Otherwise, each URI listed in the location hints is
112
        /// resolved using the <c>XmlResolver</c> registered with the <c>Processor</c>.</para>
113
        /// </remarks>
114

    
115
        public IQueryResolver QueryResolver
116
        {
117
            get { return moduleResolver; }
118
            set
119
            {
120
                moduleResolver = value;
121
                env.setModuleURIResolver(new DotNetModuleURIResolver(value));
122
            }
123
        }
124

    
125
        /// <summary>
126
        /// List of errors. The caller should supply an empty list before calling Compile;
127
        /// the processor will then populate the list with error information obtained during
128
        /// the compilation. Each error will be included as an object of type StaticError.
129
        /// If no error list is supplied by the caller, error information will be written to
130
        /// the standard error stream.
131
        /// </summary>
132
        /// <remarks>
133
        /// By supplying a custom List with a user-written add() method, it is possible to
134
        /// intercept error conditions as they occur.
135
        /// </remarks>
136

    
137
        public IList ErrorList
138
        {
139
            set
140
            {
141
                errorList = value;
142
                env.setErrorListener(new ErrorGatherer(value));
143
            }
144
            get
145
            {
146
                return errorList;
147
            }
148
        }
149

    
150
        /// <summary>
151
        /// Compile a query supplied as a Stream.
152
        /// </summary>
153
        /// <remarks>
154
        /// <para>The XQuery processor attempts to deduce the encoding of the query
155
        /// by looking for a byte-order-mark, or if none is present, by looking
156
        /// for the encoding declaration in the XQuery version declaration.
157
        /// For this to work, the stream must have the <c>CanSeek</c> property.
158
        /// If no encoding information is present, UTF-8 is assumed.</para>
159
        /// <para>The base URI of the query is set to the value of the <c>BaseUri</c>
160
        /// property. If this has not been set, then the base URI will be undefined, which
161
        /// means that any use of an expression that depends on the base URI will cause
162
        /// an error.</para>
163
        /// </remarks>
164
        /// <example>
165
        /// <code>
166
        /// XQueryExecutable q = compiler.Compile(new FileStream("input.xq", FileMode.Open, FileAccess.Read));
167
        /// </code>
168
        /// </example>
169
        /// <param name="query">A stream containing the source text of the query</param>
170
        /// <returns>An <c>XQueryExecutable</c> which represents the compiled query object.
171
        /// The XQueryExecutable may be run as many times as required, in the same or a different
172
        /// thread. The <c>XQueryExecutable</c> is not affected by any changes made to the <c>XQueryCompiler</c>
173
        /// once it has been compiled.</returns>
174
        /// <exception cref="StaticError">Throws a StaticError if errors were detected
175
        /// during static analysis of the query. Details of the errors will be added as StaticError
176
        /// objects to the ErrorList if supplied; otherwise they will be written to the standard
177
        /// error stream. The exception that is returned is merely a summary indicating the
178
        /// status.</exception>
179

    
180
        public XQueryExecutable Compile(Stream query)
181
        {
182
            try
183
            {
184
                XQueryExpression exp = env.compileQuery(new DotNetInputStream(query), null);
185
                return new XQueryExecutable(exp);
186
            }
187
            catch (JXPathException e)
188
            {
189
                throw new StaticError(e);
190
            }
191
        }
192

    
193
        /// <summary>
194
        /// Compile a query supplied as a String.
195
        /// </summary>
196
        /// <remarks>
197
        /// Using this method the query processor is provided with a string of Unicode
198
        /// characters, so no decoding is necessary. Any encoding information present in the
199
        /// version declaration is therefore ignored.
200
        /// </remarks>
201
        /// <example>
202
        /// <code>
203
        /// XQueryExecutable q = compiler.Compile("distinct-values(//*/node-name()");
204
        /// </code>
205
        /// </example>
206
        /// <param name="query">A string containing the source text of the query</param>
207
        /// <returns>An <c>XQueryExecutable</c> which represents the compiled query object.
208
        /// The XQueryExecutable may be run as many times as required, in the same or a different
209
        /// thread. The <c>XQueryExecutable</c> is not affected by any changes made to the <c>XQueryCompiler</c>
210
        /// once it has been compiled.</returns>
211
        /// <exception cref="StaticError">Throws a StaticError if errors were detected
212
        /// during static analysis of the query. Details of the errors will be added as StaticError
213
        /// objects to the ErrorList if supplied; otherwise they will be written to the standard
214
        /// error stream. The exception that is returned is merely a summary indicating the
215
        /// status.</exception>        
216

    
217
        public XQueryExecutable Compile(String query)
218
        {
219
            try
220
            {
221
                XQueryExpression exp = env.compileQuery(query);
222
                return new XQueryExecutable(exp);
223
            }
224
            catch (JXPathException e)
225
            {
226
                throw new StaticError(e);
227
            }
228
        }
229

    
230
        /// <summary>
231
        /// Escape hatch to the underying Java implementation
232
        /// </summary>
233

    
234
        public net.sf.saxon.query.StaticQueryContext Implementation
235
        {
236
            get { return env; }
237
        }
238
    }
239

    
240
    /// <summary>
241
    /// An <c>XQueryExecutable</c> represents the compiled form of a query. To execute the query,
242
    /// it must first be loaded to form an <c>XQueryEvaluator</c>.
243
    /// </summary>
244
    /// <remarks>
245
    /// <para>An <c>XQueryExecutable</c> is immutable, and therefore thread-safe. It is simplest to
246
    /// load a new <c>XQueryEvaluator</c> each time the query is to be run. However, the 
247
    /// <c>XQueryEvaluator</c> is serially reusable within a single thread.</para>
248
    /// <para>An <c>XQueryExecutable</c> is created by using one of the <c>Compile</c>
249
    /// methods on the <c>XQueryCompiler</c> class.</para>
250
    /// </remarks>    
251

    
252
    [Serializable]
253
    public class XQueryExecutable
254
    {
255

    
256
        private XQueryExpression exp;
257

    
258
        // internal constructor
259

    
260
        internal XQueryExecutable(XQueryExpression exp)
261
        {
262
            this.exp = exp;
263
        }
264

    
265
        /// <summary>Ask whether this is an updating query (that is, one that returns a pending
266
        /// update list rather than a convensional value).</summary>
267
        /// <remarks><para><i>Property added in Saxon 9.1</i></para></remarks>
268

    
269
        public bool IsUpdateQuery
270
        {
271
            get { return exp.isUpdateQuery(); }
272
        }
273

    
274
        /// <summary>
275
        /// Load the query to prepare it for execution.
276
        /// </summary>
277
        /// <returns>
278
        /// An <c>XQueryEvaluator</c>. The returned <c>XQueryEvaluator</c> can be used to
279
        /// set up the dynamic context for query evaluation, and to run the query.
280
        /// </returns>
281

    
282
        public XQueryEvaluator Load()
283
        {
284
            return new XQueryEvaluator(exp);
285
        }
286
    }
287

    
288
    /// <summary inherits="IEnumerable">
289
    /// An <c>XQueryEvaluator</c> represents a compiled and loaded query ready for execution.
290
    /// The <c>XQueryEvaluator</c> holds details of the dynamic evaluation context for the query.
291
    /// </summary>
292
    /// <remarks>
293
    /// <para>An <c>XQueryEvaluator</c> should not be used concurrently in multiple threads. It is safe,
294
    /// however, to reuse the object within a single thread to run the same query several times.
295
    /// Running the query does not change the context that has been established.</para>
296
    /// <para>An <c>XQueryEvaluator</c> is always constructed by running the <c>Load</c> method of
297
    /// an <c>XQueryExecutable</c>.</para>
298
    /// </remarks>     
299

    
300
    [Serializable]
301
    public class XQueryEvaluator : IEnumerable
302
    {
303

    
304
        private XQueryExpression exp;
305
        private DynamicQueryContext context;
306
        private Stream traceFunctionDestination;
307

    
308
        // internal constructor
309

    
310
        internal XQueryEvaluator(XQueryExpression exp)
311
        {
312
            this.exp = exp;
313
            this.context =
314
                new DynamicQueryContext(exp.getStaticContext().getConfiguration());
315
        }
316

    
317
        /// <summary>
318
        /// The context item for the query.
319
        /// </summary>
320
        /// <remarks> This may be either a node or an atomic
321
        /// value. Most commonly it will be a document node, which might be constructed
322
        /// using the <c>LoadDocument</c> method of the <c>Processor</c> object.
323
        /// </remarks>
324

    
325
        public XdmItem ContextItem
326
        {
327
            get { return (XdmItem)XdmValue.Wrap(context.getContextItem()); }
328
            set { context.setContextItem((Item)value.Unwrap()); }
329
        }
330

    
331
        /// <summary>
332
        /// The <code>XmlResolver</code> to be used at run-time to resolve and dereference URIs
333
        /// supplied to the <c>doc()</c> function.
334
        /// </summary>
335

    
336
        public XmlResolver InputXmlResolver
337
        {
338
            get
339
            {
340
                return ((DotNetURIResolver)context.getURIResolver()).getXmlResolver();
341
            }
342
            set
343
            {
344
                context.setURIResolver(new DotNetURIResolver(value));
345
            }
346
        }
347

    
348
        /// <summary>
349
        /// Set the value of an external variable declared in the query.
350
        /// </summary>
351
        /// <param name="name">The name of the external variable, expressed
352
        /// as a QName. If an external variable of this name has been declared in the
353
        /// query prolog, the given value will be assigned to the variable. If the
354
        /// variable has not been declared, calling this method has no effect (it is
355
        /// not an error).</param>
356
        /// <param name="value">The value to be given to the external variable.
357
        /// If the variable declaration defines a required type for the variable, then
358
        /// this value must match the required type: no conversions are applied.</param>
359

    
360
        public void SetExternalVariable(QName name, XdmValue value)
361
        {
362
            context.setParameter(name.ClarkName, value.Unwrap());
363
        }
364

    
365
        /// <summary>
366
        /// Destination for output of messages produced using &lt;trace()&gt;. 
367
        /// <para>If no specific destination is supplied by the caller, message information will be written to
368
        /// the standard error stream.</para>
369
        /// </summary>
370
        /// <remarks>
371
        /// <para>The supplied destination is ignored if a <c>TraceListener</c> is in use.</para>
372
        /// <para><i>Property added in Saxon 9.1</i></para>
373
        /// </remarks>
374

    
375
        public Stream TraceFunctionDestination
376
        {
377
            set
378
            {
379
                traceFunctionDestination = value;
380
                context.setTraceFunctionDestination(
381
                    new java.io.PrintStream(new DotNetOutputStream(value)));
382
            }
383
            get
384
            {
385
                return traceFunctionDestination;
386
            }
387
        }
388

    
389

    
390
        /// <summary>
391
        /// Evaluate the query, returning the result as an <c>XdmValue</c> (that is,
392
        /// a sequence of nodes and/or atomic values).
393
        /// </summary>
394
        /// <returns>
395
        /// An <c>XdmValue</c> representing the results of the query
396
        /// </returns>
397
        /// <exception cref="DynamicError">Throws a DynamicError if any run-time failure
398
        /// occurs while evaluating the query.</exception>
399

    
400
        public XdmValue Evaluate()
401
        {
402
            try
403
            {
404
                ValueRepresentation value = SequenceExtent.makeSequenceExtent(exp.iterator(context));
405
                return XdmValue.Wrap(value);
406
            }
407
            catch (JXPathException err)
408
            {
409
                throw new DynamicError(err);
410
            }
411
        }
412

    
413
        /// <summary>
414
        /// Evaluate the query, returning the result as an <c>XdmItem</c> (that is,
415
        /// a single node or atomic value).
416
        /// </summary>
417
        /// <returns>
418
        /// An <c>XdmItem</c> representing the result of the query, or null if the query
419
        /// returns an empty sequence. If the query returns a sequence of more than one item,
420
        /// any items after the first are ignored.
421
        /// </returns>
422
        /// <exception cref="DynamicError">Throws a DynamicError if any run-time failure
423
        /// occurs while evaluating the expression.</exception>
424

    
425
        public XdmItem EvaluateSingle()
426
        {
427
            try
428
            {
429
                return (XdmItem)XdmValue.Wrap(exp.iterator(context).next());
430
            }
431
            catch (JXPathException err)
432
            {
433
                throw new DynamicError(err);
434
            }
435
        }
436

    
437
        /// <summary>
438
        /// Evaluate the query, returning the result as an <c>IEnumerator</c> (that is,
439
        /// an enumerator over a sequence of nodes and/or atomic values).
440
        /// </summary>
441
        /// <returns>
442
        /// An enumerator over the sequence that represents the results of the query.
443
        /// Each object in this sequence will be an instance of <c>XdmItem</c>. Note
444
        /// that the query may be evaluated lazily, which means that a successful response
445
        /// from this method does not imply that the query has executed successfully: failures
446
        /// may be reported later while retrieving items from the iterator. 
447
        /// </returns>
448
        /// <exception cref="DynamicError">Throws a DynamicError if any run-time failure
449
        /// occurs while evaluating the expression.</exception>
450

    
451
        public IEnumerator GetEnumerator()
452
        {
453
            try
454
            {
455
                return new SequenceEnumerator(exp.iterator(context));
456
            }
457
            catch (JXPathException err)
458
            {
459
                throw new DynamicError(err);
460
            }
461
        }
462

    
463
        /// <summary>
464
        /// Evaluate the query, sending the result to a specified destination.
465
        /// </summary>
466
        /// <param name="destination">
467
        /// The destination for the results of the query. The class <c>XmlDestination</c>
468
        /// is an abstraction that allows a number of different kinds of destination
469
        /// to be specified.
470
        /// </param>
471
        /// <exception cref="DynamicError">Throws a DynamicError if any run-time failure
472
        /// occurs while evaluating the expression.</exception>
473

    
474
        public void Run(XmlDestination destination)
475
        {
476
            try
477
            {
478
                exp.run(context, destination.GetResult(), destination.GetOutputProperties());
479
            }
480
            catch (JXPathException err)
481
            {
482
                throw new DynamicError(err);
483
            }
484
            destination.Close();
485
        }
486

    
487
        /// <summary>
488
        /// Execute an updating query.
489
        /// </summary>
490
        /// <returns>An array containing the root nodes of documents that have been
491
        /// updated by the query.</returns>
492
        /// <exception cref="DynamicError">Throws a DynamicError if any run-time failure
493
        /// occurs while evaluating the expression, or if the expression is not an
494
        /// updating query.</exception>
495

    
496
        public XdmNode[] RunUpdate()
497
        {
498
            if (!exp.isUpdateQuery())
499
            {
500
                throw new DynamicError("Not an updating query");
501
            }
502
            try
503
            {
504
                java.util.Set updatedDocs = exp.runUpdate(context);
505
                XdmNode[] result = new XdmNode[updatedDocs.size()];
506
                int i = 0;
507
                for (java.util.Iterator iter = updatedDocs.iterator(); iter.hasNext(); )
508
                {
509
                    result[i++] = (XdmNode)XdmValue.Wrap((NodeInfo)iter.next());
510
                }
511
                return result;
512
            }
513
            catch (JXPathException err)
514
            {
515
                throw new DynamicError(err);
516
            }
517
        }
518

    
519
    }
520

    
521

    
522
    /// <summary>
523
    /// Interface defining a user-supplied class used to retrieve XQUery library modules listed
524
    /// in an <c>import module</c> declaration in the query prolog.
525
    /// </summary>
526

    
527

    
528
    public interface IQueryResolver
529
    {
530

    
531
        /// <summary>
532
        /// Given a module URI and a set of location hints, return a set of query modules.
533
        /// </summary>
534
        /// <param name="moduleUri">The URI of the required library module as written in the
535
        /// <c>import module</c> declaration</param>
536
        /// <param name="baseUri">The base URI of the module containing the <c>import module</c>
537
        /// declaration</param>
538
        /// <param name="locationHints">The sequence of URIs (if any) listed as location hints
539
        /// in the <c>import module</c> declaration in the query prolog.</param>
540
        /// <returns>A set of absolute Uris identifying the query modules to be loaded. There is no requirement
541
        /// that these correspond one-to-one with the URIs defined in the <c>locationHints</c>. The 
542
        /// returned URIs will be dereferenced by calling the <c>GetEntity</c> method.
543
        /// </returns>
544

    
545
        Uri[] GetModules(String moduleUri, Uri baseUri, String[] locationHints);
546

    
547
        /// <summary>
548
        /// Dereference a URI returned by <c>GetModules</c> to retrieve a <c>Stream</c> containing
549
        /// the actual query text.
550
        /// </summary>
551
        /// <param name="absoluteUri">A URI returned by the <code>GetModules</code> method.</param>
552
        /// <returns>Either a <c>Stream</c> or a <c>String</c> containing the query text. 
553
        /// The supplied URI will be used as the base URI of the query module.</returns>
554

    
555
        Object GetEntity(Uri absoluteUri);
556

    
557
    }
558

    
559
    // internal class that wraps a (.NET) IQueryResolver to create a (Java) ModuleURIResolver
560

    
561
    internal class DotNetModuleURIResolver : net.sf.saxon.query.ModuleURIResolver
562
    {
563

    
564
        private IQueryResolver resolver;
565

    
566
        public DotNetModuleURIResolver(IQueryResolver resolver)
567
        {
568
            this.resolver = resolver;
569
        }
570

    
571
        public JStreamSource[] resolve(String moduleURI, String baseURI, String[] locations)
572
        {
573
            Uri baseU = (baseURI == null ? null : new Uri(baseURI));
574
            Uri[] modules = resolver.GetModules(moduleURI, baseU, locations);
575
            JStreamSource[] ss = new JStreamSource[modules.Length];
576
            for (int i = 0; i < ss.Length; i++)
577
            {
578
                ss[i] = new JStreamSource();
579
                ss[i].setSystemId(modules[i].ToString());
580
                Object query = resolver.GetEntity(modules[i]);
581
                if (query is Stream)
582
                {
583
                    ss[i].setInputStream(new DotNetInputStream((Stream)query));
584
                }
585
                else if (query is String)
586
                {
587
                    ss[i].setReader(new DotNetReader(new StringReader((String)query)));
588
                }
589
                else
590
                {
591
                    throw new ArgumentException("Invalid response from GetEntity()");
592
                }
593
            }
594
            return ss;
595
        }
596
    }
597

    
598

    
599

    
600

    
601

    
602

    
603
}
604

    
605

    
606
//
607
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
608
// you may not use this file except in compliance with the License. You may obtain a copy of the
609
// License at http://www.mozilla.org/MPL/
610
//
611
// Software distributed under the License is distributed on an "AS IS" basis,
612
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
613
// See the License for the specific language governing rights and limitations under the License.
614
//
615
// The Original Code is: all this file.
616
//
617
// The Initial Developer of the Original Code is Michael H. Kay.
618
//
619
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
620
//
621
// Contributor(s): none.
622
//
(7-7/8)