Project

Profile

Help

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

he / tags / 9.6.0.7 / hen / csource / api / Saxon.Api / XQuery.cs @ aa733b18

1
using System;
2
using System.IO;
3
using System.Xml;
4
using System.Collections;
5
using System.Globalization;
6
using JConfiguration = net.sf.saxon.Configuration;
7
using JController = net.sf.saxon.Controller;
8
using net.sf.saxon.om;
9
using net.sf.saxon.value;
10
using net.sf.saxon.query;
11
using net.sf.saxon.dotnet;
12
using JValidation = net.sf.saxon.lib.Validation;
13
using JXPathException = net.sf.saxon.trans.XPathException;
14
using JStreamSource = javax.xml.transform.stream.StreamSource;
15
using JUserFunction = net.sf.saxon.expr.instruct.UserFunction;
16
using JBoolean = java.lang.Boolean;
17
using JDecimalValue = net.sf.saxon.value.DecimalValue;
18

    
19

    
20
namespace Saxon.Api
21
{
22

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

    
39
    [Serializable]
40
    public class XQueryCompiler
41
    {
42

    
43
        private JConfiguration config;
44
        private Processor processor;
45
        private StaticQueryContext env;
46
        private IQueryResolver moduleResolver;
47
        private IList errorList;
48

    
49
        // internal constructor: the public interface is a factory method
50
        // on the Processor object
51

    
52
        internal XQueryCompiler(Processor processor)
53
        {
54
            this.processor = processor;
55
			this.config = processor.Implementation;
56
            this.env = config.newStaticQueryContext();
57
            env.setModuleURIResolver(new DotNetStandardModuleURIResolver(processor.XmlResolver));
58
        }
59

    
60
        /// <summary>
61
        /// Create a collation based on a given <c>CompareInfo</c> and <c>CompareOptions</c>    
62
        /// </summary>
63
        /// <param name="uri">The collation URI to be used within the XPath expression to refer to this collation</param>
64
        /// <param name="comparerInfo">The <c>CompareInfo</c>, which determines the language-specific
65
        /// collation rules to be used</param>
66
        /// <param name="options">Options to be used in performing comparisons, for example
67
        /// whether they are to be case-blind and/or accent-blind</param>
68
        /// <param name="isDefault">If true, this collation will be used as the default collation</param>
69

    
70
        public void DeclareCollation(Uri uri, CompareInfo compareInfo, CompareOptions options, Boolean isDefault) {
71
            DotNetComparator comparator = new DotNetComparator(compareInfo, options);
72
			config.registerCollation(uri.ToString(), comparator);
73
            if(isDefault) {
74
                env.declareDefaultCollation(uri.ToString());
75
            }
76
        }
77

    
78
        /// <summary>
79
        /// Declare a namespace for use by the query. This has the same
80
        /// status as a namespace appearing within the query prolog (though
81
        /// a declaration in the query prolog of the same prefix will take
82
        /// precedence)
83
        /// </summary>
84
        /// <param name="prefix">The namespace prefix to be declared. Use
85
        /// a zero-length string to declare the default namespace (that is, the
86
        /// default namespace for elements and types).</param>
87
        /// <param name="uri">The namespace URI. It is possible to specify
88
        /// a zero-length string to "undeclare" a namespace.</param>
89

    
90
        public void DeclareNamespace(String prefix, String uri)
91
        {
92
            env.declareNamespace(prefix, uri);
93
        }
94

    
95

    
96
        /// <summary>
97
        /// Get the Processor from which this XQueryCompiler was constructed
98
        /// </summary>
99
        public Processor Processor
100
        {
101
            get {return processor;}
102
            set {processor = value;}
103
        }
104

    
105
        /// <summary>
106
        /// The required context item type for the expression. This is used for
107
        /// optimizing the expression at compile time, and to check at run-time
108
        /// that the value supplied for the context item is the correct type.
109
        /// </summary>
110

    
111
        public XdmItemType ContextItemType
112
        {
113
            get { return XdmItemType.MakeXdmItemType(env.getRequiredContextItemType()); }
114
            set { env.setRequiredContextItemType(value.Unwrap()); }
115
        }
116

    
117
        /// <summary>
118
        /// The base URI of the query, which forms part of the static context
119
        /// of the query. This is used for resolving any relative URIs appearing
120
        /// within the query, for example in references to library modules, schema
121
        /// locations, or as an argument to the <c>doc()</c> function.
122
        /// </summary>
123

    
124

    
125
        public String BaseUri
126
        {
127
            get { return env.getBaseURI(); }
128
            set { env.setBaseURI(value); }
129
        }
130

    
131

    
132
        /// <summary>
133
        /// Say that the query must be compiled to be schema-aware, even if it contains no
134
        /// "import schema" declarations. Normally a query is treated as schema-aware
135
        /// only if it contains one or more "import schema" declarations. If it is not schema-aware,
136
        /// then all input documents must be untyped (or xs:anyType), and validation of temporary nodes is disallowed
137
        /// (though validation of the final result tree is permitted). Setting the argument to true
138
        /// means that schema-aware code will be compiled regardless.
139
        /// schemaAware If true, the stylesheet will be compiled with schema-awareness
140
        /// enabled even if it contains no xsl:import-schema declarations. If false, the stylesheet
141
        /// is treated as schema-aware only if it contains one or more xsl:import-schema declarations.
142
        /// </summary>
143

    
144
        public Boolean SchemaAware
145
        {
146
            get
147
            {
148
                return env.isSchemaAware();
149
            }
150
            set
151
            {
152
                env.setSchemaAware(value);
153
            }
154
        }
155

    
156
        /// <summary>
157
        /// This property indicates whether XQuery Update syntax is accepted. The default
158
        /// value is false. This property must be set to true before compiling a query that
159
        /// uses update syntax.
160
        /// </summary>
161
        /// <remarks>
162
        /// <para>This propery must be set to true before any query can be compiled
163
        /// that uses updating syntax. This applies even if the query is not actually an updating
164
        /// query (for example, a copy-modify expression). XQuery Update syntax is accepted
165
        /// only by Saxon-SA. Non-updating queries are accepted regardless of the value of this
166
        /// property.</para>
167
        /// <para><i>Property added in Saxon 9.1</i></para></remarks>
168

    
169

    
170
        public bool UpdatingEnabled
171
        {
172
            get { return env.isUpdatingEnabled(); }
173
            set { env.setUpdatingEnabled(value); }
174
        }
175

    
176
        /// <summary>
177
        /// This property indicates which version of XQuery language syntax is accepted. The default
178
        /// value is "1.0". This property must be set to "3.0" before compiling a query that
179
        /// uses XQuery 3.0 (formerly known as XQuery 1.1) syntax.
180
        /// </summary>
181
        /// <remarks>
182
        /// <para>Support for XQuery 3.0 is currently limited: for details see the Saxon documentation.
183
        /// It cannot be used together with
184
        /// XQuery Updates. As well as enabling XQuery 3.0 via this API call, it must also be enabled
185
        /// by setting version="3.0" in the query prolog.</para>
186
        /// <para><i>Property added in Saxon 9.2</i></para></remarks>
187

    
188

    
189
        public string XQueryLanguageVersion
190
        {
191
            get { return env.getLanguageVersion().toString(); }
192
            set { env.setLanguageVersion((JDecimalValue)JDecimalValue.makeDecimalValue(value.ToString(), true)); }
193
        }
194

    
195

    
196
        /// <summary>
197
        /// A user-supplied <c>IQueryResolver</c> used to resolve location hints appearing in an
198
        /// <c>import module</c> declaration.
199
        /// </summary>
200
        /// <remarks>
201
        /// <para>In the absence of a user-supplied <c>QueryResolver</c>, an <c>import module</c> declaration
202
        /// is interpreted as follows. First, if the module URI identifies an already loaded module, that module
203
        /// is used and the location hints are ignored. Otherwise, each URI listed in the location hints is
204
        /// resolved using the <c>XmlResolver</c> registered with the <c>Processor</c>.</para>
205
        /// </remarks>
206

    
207
        public IQueryResolver QueryResolver
208
        {
209
            get { return moduleResolver; }
210
            set
211
            {
212
                moduleResolver = value;
213
                env.setModuleURIResolver(new DotNetModuleURIResolver(value));
214
            }
215
        }
216

    
217
        /// <summary>
218
        /// List of errors. The caller should supply an empty list before calling Compile;
219
        /// the processor will then populate the list with error information obtained during
220
        /// the compilation. Each error will be included as an object of type StaticError.
221
        /// If no error list is supplied by the caller, error information will be written to
222
        /// the standard error stream.
223
        /// </summary>
224
        /// <remarks>
225
        /// By supplying a custom List with a user-written add() method, it is possible to
226
        /// intercept error conditions as they occur.
227
        /// </remarks>
228

    
229
        public IList ErrorList
230
        {
231
            set
232
            {
233
                errorList = value;
234
                env.setErrorListener(new ErrorGatherer(value));
235
            }
236
            get
237
            {
238
                return errorList;
239
            }
240
        }
241

    
242
        /// <summary>
243
        /// Compile a query supplied as a Stream.
244
        /// </summary>
245
        /// <remarks>
246
        /// <para>The XQuery processor attempts to deduce the encoding of the query
247
        /// by looking for a byte-order-mark, or if none is present, by looking
248
        /// for the encoding declaration in the XQuery version declaration.
249
        /// For this to work, the stream must have the <c>CanSeek</c> property.
250
        /// If no encoding information is present, UTF-8 is assumed.</para>
251
        /// <para>The base URI of the query is set to the value of the <c>BaseUri</c>
252
        /// property. If this has not been set, then the base URI will be undefined, which
253
        /// means that any use of an expression that depends on the base URI will cause
254
        /// an error.</para>
255
        /// </remarks>
256
        /// <example>
257
        /// <code>
258
        /// XQueryExecutable q = compiler.Compile(new FileStream("input.xq", FileMode.Open, FileAccess.Read));
259
        /// </code>
260
        /// </example>
261
        /// <param name="query">A stream containing the source text of the query</param>
262
        /// <returns>An <c>XQueryExecutable</c> which represents the compiled query object.
263
        /// The XQueryExecutable may be run as many times as required, in the same or a different
264
        /// thread. The <c>XQueryExecutable</c> is not affected by any changes made to the <c>XQueryCompiler</c>
265
        /// once it has been compiled.</returns>
266
        /// <exception cref="StaticError">Throws a StaticError if errors were detected
267
        /// during static analysis of the query. Details of the errors will be added as StaticError
268
        /// objects to the ErrorList if supplied; otherwise they will be written to the standard
269
        /// error stream. The exception that is returned is merely a summary indicating the
270
        /// status.</exception>
271

    
272
        public XQueryExecutable Compile(Stream query)
273
        {
274
            try
275
            {
276
                XQueryExpression exp = env.compileQuery(new DotNetInputStream(query), null);
277
                return new XQueryExecutable(exp);
278
            }
279
            catch (JXPathException e)
280
            {
281
                throw new StaticError(e);
282
            }
283
        }
284

    
285
        /// <summary>
286
        /// Compile a query supplied as a String.
287
        /// </summary>
288
        /// <remarks>
289
        /// Using this method the query processor is provided with a string of Unicode
290
        /// characters, so no decoding is necessary. Any encoding information present in the
291
        /// version declaration is therefore ignored.
292
        /// </remarks>
293
        /// <example>
294
        /// <code>
295
        /// XQueryExecutable q = compiler.Compile("distinct-values(//*/node-name()");
296
        /// </code>
297
        /// </example>
298
        /// <param name="query">A string containing the source text of the query</param>
299
        /// <returns>An <c>XQueryExecutable</c> which represents the compiled query object.
300
        /// The XQueryExecutable may be run as many times as required, in the same or a different
301
        /// thread. The <c>XQueryExecutable</c> is not affected by any changes made to the <c>XQueryCompiler</c>
302
        /// once it has been compiled.</returns>
303
        /// <exception cref="StaticError">Throws a StaticError if errors were detected
304
        /// during static analysis of the query. Details of the errors will be added as StaticError
305
        /// objects to the ErrorList if supplied; otherwise they will be written to the standard
306
        /// error stream. The exception that is returned is merely a summary indicating the
307
        /// status.</exception>        
308

    
309
        public XQueryExecutable Compile(String query)
310
        {
311
            try
312
            {
313
                XQueryExpression exp = env.compileQuery(query);
314
                return new XQueryExecutable(exp);
315
            }
316
            catch (JXPathException e)
317
            {
318
                throw new StaticError(e);
319
            }
320
        }
321

    
322
        /// <summary>
323
        /// Escape hatch to the underying Java implementation
324
        /// </summary>
325

    
326
        public net.sf.saxon.query.StaticQueryContext Implementation
327
        {
328
            get { return env; }
329
        }
330
    }
331

    
332
    /// <summary>
333
    /// An <c>XQueryExecutable</c> represents the compiled form of a query. To execute the query,
334
    /// it must first be loaded to form an <c>XQueryEvaluator</c>.
335
    /// </summary>
336
    /// <remarks>
337
    /// <para>An <c>XQueryExecutable</c> is immutable, and therefore thread-safe. It is simplest to
338
    /// load a new <c>XQueryEvaluator</c> each time the query is to be run. However, the 
339
    /// <c>XQueryEvaluator</c> is serially reusable within a single thread.</para>
340
    /// <para>An <c>XQueryExecutable</c> is created by using one of the <c>Compile</c>
341
    /// methods on the <c>XQueryCompiler</c> class.</para>
342
    /// </remarks>    
343

    
344
    [Serializable]
345
    public class XQueryExecutable
346
    {
347

    
348
        private XQueryExpression exp;
349

    
350
        // internal constructor
351

    
352
        internal XQueryExecutable(XQueryExpression exp)
353
        {
354
            this.exp = exp;
355
        }
356

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

    
361
        public bool IsUpdateQuery
362
        {
363
            get { return exp.isUpdateQuery(); }
364
        }
365

    
366
		public XQueryExpression getUnderlyingCompiledQuery(){
367
			return exp;
368
		}
369

    
370
        /// <summary>
371
        /// Load the query to prepare it for execution.
372
        /// </summary>
373
        /// <returns>
374
        /// An <c>XQueryEvaluator</c>. The returned <c>XQueryEvaluator</c> can be used to
375
        /// set up the dynamic context for query evaluation, and to run the query.
376
        /// </returns>
377

    
378
        public XQueryEvaluator Load()
379
        {
380
            return new XQueryEvaluator(exp);
381
        }
382
    }
383

    
384
    /// <summary inherits="IEnumerable">
385
    /// An <c>XQueryEvaluator</c> represents a compiled and loaded query ready for execution.
386
    /// The <c>XQueryEvaluator</c> holds details of the dynamic evaluation context for the query.
387
    /// </summary>
388
    /// <remarks>
389
    /// <para>An <c>XQueryEvaluator</c> should not be used concurrently in multiple threads. It is safe,
390
    /// however, to reuse the object within a single thread to run the same query several times.
391
    /// Running the query does not change the context that has been established.</para>
392
    /// <para>An <c>XQueryEvaluator</c> is always constructed by running the <c>Load</c> method of
393
    /// an <c>XQueryExecutable</c>.</para>
394
    /// </remarks>     
395

    
396
    [Serializable]
397
    public class XQueryEvaluator : IEnumerable
398
    {
399

    
400
        private XQueryExpression exp;
401
        private DynamicQueryContext context;
402
        private JController controller;
403
		private StandardLogger traceFunctionDestination;
404

    
405
        // internal constructor
406

    
407
        internal XQueryEvaluator(XQueryExpression exp)
408
        {
409
            this.exp = exp;
410
            this.context =
411
				new DynamicQueryContext(exp.getConfiguration());
412
        }
413

    
414
        /// <summary>
415
        /// The context item for the query.
416
        /// </summary>
417
        /// <remarks> This may be either a node or an atomic
418
        /// value. Most commonly it will be a document node, which might be constructed
419
        /// using the <c>LoadDocument</c> method of the <c>Processor</c> object.
420
        /// </remarks>
421

    
422
        public XdmItem ContextItem
423
        {
424
            get { return (XdmItem)XdmValue.Wrap(context.getContextItem()); }
425
            set { context.setContextItem((Item)value.Unwrap()); }
426
        }
427

    
428
        /// <summary>
429
        /// The <c>SchemaValidationMode</c> to be used in this transformation, especially for documents
430
        /// loaded using the <c>doc()</c>, <c>document()</c>, or <c>collection()</c> functions.
431
        /// </summary>
432
        /// 
433

    
434
        public SchemaValidationMode SchemaValidationMode
435
        {
436
            get
437
            {
438
                switch (context.getSchemaValidationMode())
439
                {
440
                    case JValidation.STRICT:
441
                        return SchemaValidationMode.Strict;
442
                    case JValidation.LAX:
443
                        return SchemaValidationMode.Lax;
444
                    case JValidation.STRIP:
445
                        return SchemaValidationMode.None;
446
                    case JValidation.PRESERVE:
447
                        return SchemaValidationMode.Preserve;
448
                    case JValidation.DEFAULT:
449
                    default:
450
                        return SchemaValidationMode.Unspecified;
451
                }
452
            }
453

    
454
            set
455
            {
456
                switch (value)
457
                {
458
                    case SchemaValidationMode.Strict:
459
                        context.setSchemaValidationMode(JValidation.STRICT);
460
                        break;
461
                    case SchemaValidationMode.Lax:
462
                        context.setSchemaValidationMode(JValidation.LAX);
463
                        break;
464
                    case SchemaValidationMode.None:
465
                        context.setSchemaValidationMode(JValidation.STRIP);
466
                        break;
467
                    case SchemaValidationMode.Preserve:
468
                        context.setSchemaValidationMode(JValidation.PRESERVE);
469
                        break;
470
                    case SchemaValidationMode.Unspecified:
471
                    default:
472
                        context.setSchemaValidationMode(JValidation.DEFAULT);
473
                        break;
474
                }
475
            }
476
        }
477

    
478
        /// <summary>
479
        /// The <code>XmlResolver</code> to be used at run-time to resolve and dereference URIs
480
        /// supplied to the <c>doc()</c> function.
481
        /// </summary>
482

    
483
        public XmlResolver InputXmlResolver
484
        {
485
            get
486
            {
487
                return ((DotNetURIResolver)context.getURIResolver()).getXmlResolver();
488
            }
489
            set
490
            {
491
                context.setURIResolver(new DotNetURIResolver(value));
492
            }
493
        }
494

    
495
        /// <summary>
496
        /// Set the value of an external variable declared in the query.
497
        /// </summary>
498
        /// <param name="name">The name of the external variable, expressed
499
        /// as a QName. If an external variable of this name has been declared in the
500
        /// query prolog, the given value will be assigned to the variable. If the
501
        /// variable has not been declared, calling this method has no effect (it is
502
        /// not an error).</param>
503
        /// <param name="value">The value to be given to the external variable.
504
        /// If the variable declaration defines a required type for the variable, then
505
        /// this value must match the required type: no conversions are applied.</param>
506

    
507
        public void SetExternalVariable(QName name, XdmValue value)
508
        {
509
			context.setParameter(name.ToStructuredQName(), value.Unwrap());
510
        }
511

    
512
        /// <summary>
513
        /// Destination for output of messages produced using &lt;trace()&gt;. 
514
        /// <para>If no specific destination is supplied by the caller, message information will be written to
515
        /// the standard error stream.</para>
516
        /// </summary>
517
        /// <remarks>
518
        /// <para>The supplied destination is ignored if a <c>TraceListener</c> is in use.</para>
519
        /// <para><i>Property added in Saxon 9.1</i></para>
520
		/// <para>Since 9.6. Changed in 9.6 to use a StandardLogger</para>
521
        /// </remarks>
522

    
523
        public StandardLogger TraceFunctionDestination
524
        {
525
            set
526
            {
527
				traceFunctionDestination = value;
528
				context.setTraceFunctionDestination(value);
529
            }
530
            get
531
            {
532
                return traceFunctionDestination;
533
            }
534
        }
535

    
536

    
537
        /// <summary>
538
        /// Evaluate the query, returning the result as an <c>XdmValue</c> (that is,
539
        /// a sequence of nodes and/or atomic values).
540
        /// </summary>
541
        /// <returns>
542
        /// An <c>XdmValue</c> representing the results of the query
543
        /// </returns>
544
        /// <exception cref="DynamicError">Throws a DynamicError if any run-time failure
545
        /// occurs while evaluating the query.</exception>
546

    
547
        public XdmValue Evaluate()
548
        {
549
            try
550
            {
551
                GroundedValue value = SequenceExtent.makeSequenceExtent(exp.iterator(context));
552
                return XdmValue.Wrap(value);
553
            }
554
            catch (JXPathException err)
555
            {
556
                throw new DynamicError(err);
557
            }
558
        }
559

    
560
        /// <summary>
561
        /// Evaluate the query, returning the result as an <c>XdmItem</c> (that is,
562
        /// a single node or atomic value).
563
        /// </summary>
564
        /// <returns>
565
        /// An <c>XdmItem</c> representing the result of the query, or null if the query
566
        /// returns an empty sequence. If the query returns a sequence of more than one item,
567
        /// any items after the first are ignored.
568
        /// </returns>
569
        /// <exception cref="DynamicError">Throws a DynamicError if any run-time failure
570
        /// occurs while evaluating the expression.</exception>
571

    
572
        public XdmItem EvaluateSingle()
573
        {
574
            try
575
            {
576
                return (XdmItem)XdmValue.Wrap(exp.iterator(context).next());
577
            }
578
            catch (JXPathException err)
579
            {
580
                throw new DynamicError(err);
581
            }
582
        }
583

    
584
        /// <summary>
585
        /// Evaluate the query, returning the result as an <c>IEnumerator</c> (that is,
586
        /// an enumerator over a sequence of nodes and/or atomic values).
587
        /// </summary>
588
        /// <returns>
589
        /// An enumerator over the sequence that represents the results of the query.
590
        /// Each object in this sequence will be an instance of <c>XdmItem</c>. Note
591
        /// that the query may be evaluated lazily, which means that a successful response
592
        /// from this method does not imply that the query has executed successfully: failures
593
        /// may be reported later while retrieving items from the iterator. 
594
        /// </returns>
595
        /// <exception cref="DynamicError">Throws a DynamicError if any run-time failure
596
        /// occurs while evaluating the expression.</exception>
597

    
598
        public IEnumerator GetEnumerator()
599
        {
600
            try
601
            {
602
                return new SequenceEnumerator(exp.iterator(context));
603
            }
604
            catch (JXPathException err)
605
            {
606
                throw new DynamicError(err);
607
            }
608
        }
609

    
610
        /// <summary>
611
        /// Evaluate the query, sending the result to a specified destination.
612
        /// </summary>
613
        /// <param name="destination">
614
        /// The destination for the results of the query. The class <c>XmlDestination</c>
615
        /// is an abstraction that allows a number of different kinds of destination
616
        /// to be specified.
617
        /// </param>
618
        /// <exception cref="DynamicError">Throws a DynamicError if any run-time failure
619
        /// occurs while evaluating the expression.</exception>
620

    
621
        public void Run(XmlDestination destination)
622
        {
623
            try
624
            {
625
                exp.run(context, destination.GetReceiver(context.getConfiguration().makePipelineConfiguration()), destination.GetOutputProperties());
626
            }
627
            catch (JXPathException err)
628
            {
629
                throw new DynamicError(err);
630
            }
631
            destination.Close();
632
        }
633

    
634
        /// <summary>
635
        /// Execute an updating query.
636
        /// </summary>
637
        /// <returns>An array containing the root nodes of documents that have been
638
        /// updated by the query.</returns>
639
        /// <exception cref="DynamicError">Throws a DynamicError if any run-time failure
640
        /// occurs while evaluating the expression, or if the expression is not an
641
        /// updating query.</exception>
642

    
643
        public XdmNode[] RunUpdate()
644
        {
645
            if (!exp.isUpdateQuery())
646
            {
647
                throw new DynamicError("Not an updating query");
648
            }
649
            try
650
            {
651
                java.util.Set updatedDocs = exp.runUpdate(context);
652
                XdmNode[] result = new XdmNode[updatedDocs.size()];
653
                int i = 0;
654
                for (java.util.Iterator iter = updatedDocs.iterator(); iter.hasNext(); )
655
                {
656
                    result[i++] = (XdmNode)XdmValue.Wrap((NodeInfo)iter.next());
657
                }
658
                return result;
659
            }
660
            catch (JXPathException err)
661
            {
662
                throw new DynamicError(err);
663
            }
664
        }
665

    
666
    ///<summary>
667
    /// Call a global user-defined function in the compiled query.
668
    ///</summary>
669
    ///<remarks>
670
    /// If this is called more than once (to evaluate the same function repeatedly with different arguments,
671
    /// or to evaluate different functions) then the sequence of evaluations uses the same values of global
672
    /// variables including external variables (query parameters); the effect of any changes made to query parameters
673
    /// between calls is undefined.
674
    /// </remarks>
675
    /// <param name="function">
676
    /// The name of the function to be called
677
    /// </param>
678
    /// <param name="arguments">
679
    /// The values of the arguments to be supplied to the function. These
680
    /// must be of the correct type as defined in the function signature (there is no automatic
681
    /// conversion to the required type).
682
    /// </param>
683
    /// <exception cref="ArgumentException">If no function has been defined with the given name and arity
684
    /// or if any of the arguments does not match its required type according to the function
685
    /// signature.</exception>
686
    /// <exception cref="DynamicError">If a dynamic error occurs in evaluating the function.
687
    /// </exception>
688

    
689
    public XdmValue CallFunction(QName function, XdmValue[] arguments) {
690
			JUserFunction fn = exp.getMainModule().getUserDefinedFunction(
691
                function.Uri, function.LocalName, arguments.Length);
692
        if (fn == null) {
693
            throw new ArgumentException("No function with name " + function.ClarkName +
694
                    " and arity " + arguments.Length + " has been declared in the query");
695
        }
696
        try {
697
            // TODO: use the same controller in other interfaces such as run(), and expose it in a trapdoor API
698
            if (controller == null) {
699
					controller = exp.newController(context);
700
                context.initializeController(controller);
701
					controller.initializeController(context.getParameters());                
702
            }
703
            Sequence[] vr = new Sequence[arguments.Length];
704

    
705
            for (int i=0; i<arguments.Length; i++) {
706
                SequenceType type = fn.getParameterDefinitions()[i].getRequiredType();
707
                vr[i] = arguments[i].Unwrap();
708
                if (!type.matches(vr[i], controller.getConfiguration())) {
709
                    throw new ArgumentException("Argument " + (i+1) +
710
                            " of function " + function.ClarkName +
711
                            " does not match the required type " + type.toString());
712
                }
713
            }
714
            Sequence result = fn.call(vr, controller);
715
            return XdmValue.Wrap(result);
716
        } catch (JXPathException e) {
717
            throw new DynamicError(e);
718
        }
719
    }
720

    
721

    
722

    
723
        /// <summary>
724
        /// Escape hatch to the <c>net.sf.saxon.query.DynamicQueryContext</c> object in the underlying Java implementation
725
        /// </summary>
726

    
727
        public DynamicQueryContext Implementation
728
        {
729
            get { return context; }
730
        }
731

    
732
    }
733

    
734

    
735
    /// <summary>
736
    /// Interface defining a user-supplied class used to retrieve XQuery library modules listed
737
    /// in an <c>import module</c> declaration in the query prolog.
738
    /// </summary>
739

    
740

    
741
    public interface IQueryResolver
742
    {
743

    
744
        /// <summary>
745
        /// Given a module URI and a set of location hints, return a set of query modules.
746
        /// </summary>
747
        /// <param name="moduleUri">The URI of the required library module as written in the
748
        /// <c>import module</c> declaration</param>
749
        /// <param name="baseUri">The base URI of the module containing the <c>import module</c>
750
        /// declaration</param>
751
        /// <param name="locationHints">The sequence of URIs (if any) listed as location hints
752
        /// in the <c>import module</c> declaration in the query prolog.</param>
753
        /// <returns>A set of absolute Uris identifying the query modules to be loaded. There is no requirement
754
        /// that these correspond one-to-one with the URIs defined in the <c>locationHints</c>. The 
755
        /// returned URIs will be dereferenced by calling the <c>GetEntity</c> method.
756
        /// </returns>
757

    
758
        /**public**/ Uri[] GetModules(String moduleUri, Uri baseUri, String[] locationHints);
759

    
760
        /// <summary>
761
        /// Dereference a URI returned by <c>GetModules</c> to retrieve a <c>Stream</c> containing
762
        /// the actual query text.
763
        /// </summary>
764
        /// <param name="absoluteUri">A URI returned by the <code>GetModules</code> method.</param>
765
        /// <returns>Either a <c>Stream</c> or a <c>String</c> containing the query text. 
766
        /// The supplied URI will be used as the base URI of the query module.</returns>
767

    
768
        /**public**/ Object GetEntity(Uri absoluteUri);
769

    
770
    }
771

    
772
   /// <summary>
773
	/// Internal class that wraps a (.NET) IQueryResolver to create a (Java) ModuleURIResolver
774
	/// <para>A ModuleURIResolver is used when resolving references to
775
	/// query modules. It takes as input a URI that identifies the module to be loaded, and a set of
776
	/// location hints, and returns one or more StreamSource obects containing the queries
777
	/// to be imported.</para>
778
   /// </summary>
779

    
780
    internal class DotNetModuleURIResolver : net.sf.saxon.lib.ModuleURIResolver
781
    {
782

    
783
        private IQueryResolver resolver;
784

    
785
		/// <summary>
786
		/// Initializes a new instance of the <see cref="Saxon.Api.DotNetModuleURIResolver"/> class.
787
		/// </summary>
788
		/// <param name="resolver">Resolver.</param>
789
        public DotNetModuleURIResolver(IQueryResolver resolver)
790
        {
791
            this.resolver = resolver;
792
        }
793

    
794

    
795
		/// <summary>
796
		/// Resolve a module URI and associated location hints.
797
		/// </summary>
798
		/// <param name="moduleURI">ModuleURI. The module namespace URI of the module to be imported; or null when
799
		/// loading a non-library module.</param>
800
		/// <param name="baseURI">BaseURI. The base URI of the module containing the "import module" declaration;
801
		/// null if no base URI is known</param>
802
		/// <param name="locations">Locations. The set of URIs specified in the "at" clause of "import module",
803
		/// which serve as location hints for the module</param>
804
		/// <returns>an array of StreamSource objects each identifying the contents of a module to be
805
		/// imported. Each StreamSource must contain a
806
		/// non-null absolute System ID which will be used as the base URI of the imported module,
807
		/// and either an InputSource or a Reader representing the text of the module. The method
808
		/// may also return null, in which case the system attempts to resolve the URI using the
809
		/// standard module URI resolver.</returns>
810
        public JStreamSource[] resolve(String moduleURI, String baseURI, String[] locations)
811
        {
812
            Uri baseU = (baseURI == null ? null : new Uri(baseURI));
813
            Uri[] modules = resolver.GetModules(moduleURI, baseU, locations);
814
            JStreamSource[] ss = new JStreamSource[modules.Length];
815
            for (int i = 0; i < ss.Length; i++)
816
            {
817
                ss[i] = new JStreamSource();
818
                ss[i].setSystemId(modules[i].ToString());
819
                Object query = resolver.GetEntity(modules[i]);
820
                if (query is Stream)
821
                {
822
                    ss[i].setInputStream(new DotNetInputStream((Stream)query));
823
                }
824
                else if (query is String)
825
                {
826
                    ss[i].setReader(new DotNetReader(new StringReader((String)query)));
827
                }
828
                else
829
                {
830
                    throw new ArgumentException("Invalid response from GetEntity()");
831
                }
832
            }
833
            return ss;
834
        }
835
    }
836

    
837

    
838

    
839

    
840

    
841

    
842
}
843

    
844

    
845
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
846
// Copyright (c) 2013 Saxonica Limited.
847
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
848
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
849
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
850
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
(12-12/13)