Project

Profile

Help

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

he / src / main / csharp / api / Saxon.Api / XPath.cs @ 2699858e

1
using System;
2
using System.Xml;
3
using System.Collections;
4
using System.Globalization;
5
using JIterator = java.util.Iterator;
6
using JSequence = net.sf.saxon.om.Sequence;
7
using JStructuredQName = net.sf.saxon.om.StructuredQName;
8
using JIndependentContext = net.sf.saxon.sxpath.IndependentContext;
9
using JXPathExecutable = net.sf.saxon.s9api.XPathExecutable;
10
using JXPathCompiler = net.sf.saxon.s9api.XPathCompiler;
11
using JXPathSelector = net.sf.saxon.s9api.XPathSelector; 
12
using JXPathVariable = net.sf.saxon.sxpath.XPathVariable;
13
using JStaticProperty = net.sf.saxon.expr.StaticProperty;
14
using JSaxonApiException = net.sf.saxon.s9api.SaxonApiException;
15
using JXPathException = net.sf.saxon.trans.XPathException;
16
using JDotNetComparator = net.sf.saxon.dotnet.DotNetComparator;
17
using JDotNetURIResolver = net.sf.saxon.dotnet.DotNetURIResolver;
18
using JXdmItem = net.sf.saxon.s9api.XdmItem;
19
using System.Collections.Generic;
20

    
21
namespace Saxon.Api
22
{
23

    
24
    /// <summary>
25
	/// An <c>XPathCompiler</c> object allows XPath queries to be compiled.
26
    /// The compiler holds information that represents the static context
27
    /// for the expression.
28
    /// </summary>
29
    /// <remarks>
30
	/// <para>To construct an <c>XPathCompiler</c>, use the factory method
31
	/// <c>NewXPathCompiler</c> on the <see cref="Processor"/> object.</para>
32
	/// <para>An <c>XPathCompiler</c> may be used repeatedly to compile multiple
33
	/// queries. Any changes made to the <c>XPathCompiler</c> (that is, to the
34
    /// static context) do not affect queries that have already been compiled.
35
	/// An <c>XPathCompiler</c> may be used concurrently in multiple threads, but
36
    /// it should not then be modified once initialized.</para>
37
    /// <para> The <code>XPathCompiler</code> has the ability to maintain a cache of compiled
38
    /// expressions. This is active only if enabled by setting the <c>Caching</c> property.
39
    /// If caching is enabled, then the compiler will recognize an attempt to compile
40
    /// the same expression twice, and will avoid the cost of recompiling it. The cache
41
    /// is emptied by any method that changes the static context for subsequent expressions,
42
    /// for example, by setting the <c>BaseUri</c> property. Unless the cache is emptied,
43
    /// it grows indefinitely: compiled expressions are never discarded.</para>
44
    /// </remarks>
45

    
46
    [Serializable]
47
    public class XPathCompiler
48
    {
49

    
50
        private JXPathCompiler compiler;
51
        private Processor processor;
52

    
53
        // internal constructor: the public interface is a factory method
54
        // on the Processor object
55

    
56
        internal XPathCompiler(Processor processor, JXPathCompiler compiler)
57
        {
58
            this.compiler = compiler;
59
            this.processor = processor;
60
        }
61

    
62

    
63

    
64
        /// <summary>
65
        /// Declare the default collation
66
        /// </summary>
67
        /// <param name="uri">the absolute URI of the default collation. This URI must identify a known collation;
68
        /// either one that has been explicitly declared, or one that is recognized implicitly, such as a UCA collation</param>
69
        public void DeclareDefaultCollation(String uri)
70
        {
71
            compiler.declareDefaultCollation(uri);
72
        }
73

    
74
        /// <summary>
75
        /// Declare a namespace for use by the XPath expression.
76
        /// </summary>
77
        /// <param name="prefix">The namespace prefix to be declared. Use
78
        /// a zero-length string to declare the default namespace (that is, the
79
        /// default namespace for elements and types).</param>
80
        /// <param name="uri">The namespace URI. It is possible to specify
81
        /// a zero-length string to "undeclare" a namespace.</param>
82

    
83
        public void DeclareNamespace(String prefix, String uri)
84
        {
85
            compiler.declareNamespace(prefix, uri);
86
        }
87

    
88
        /// <summary>
89
        /// Set the error reporter to be used for reporting static warnings during compilation.
90
		/// By default, the <see cref="ErrorReporter"/> associated with the Saxon Configuration is used.
91
        /// Note that fatal static errors are always reported in the form
92
		/// of an exception thrown by the <see cref="Compile(String)"/> method, so this method only controls
93
        /// the handling of warnings
94
        /// </summary>
95
        /// <remarks>The property IErrorReporter to which warnings will be notified</remarks>
96
        public IErrorReporter WarningHandler {
97
            set {
98
                compiler.setWarningHandler(new ErrorReporterWrapper(value));
99
            }
100

    
101
        }
102

    
103

    
104
        /// <summary>
105
        /// Make available a set of functions defined in an XSLT 3.0 package. All functions
106
		/// defined with <c>visibility="public"</c>
107
		/// (or exposed as public using <c>xsl:expose</c>
108
        /// become part of the static context for an XPath expression created using this
109
		/// <c>XPathCompiler</c>. The functions are added to the search path after all existing
110
        /// functions, including functions added using a previous call on this method.
111
        /// <p>Note that if the library package includes functions that reference stylesheet parameters
112
        /// (or global variables that depend on the context item), then there is no way of supplying
113
        /// values for such parameters; calling such functions will cause a run-time error.</p>
114
        /// </summary>
115
        /// <param name="libraryPackage">the XSLT compiled library package whose functions are to be made available</param>
116
        public void AddXsltFunctionLibrary(XsltPackage libraryPackage) {
117
            compiler.addXsltFunctionLibrary(libraryPackage.GetUnderlyingXsltPackage());
118

    
119
        }
120

    
121
        /// <summary>
122
        /// Get the namespace URI part of a QName provided in lexical form (<c>prefix:localname</c>)
123
        /// </summary>
124
        /// <param name="lexicalName">The lexical QName. This may either be a plain <c>NCName</c> (a local name
125
        /// with no prefix or colon) or a lexical name using a prefix that is bound to a namespace.</param>
126
        /// <param name="useDefault">Set to true if the default namespace for elements and types is to be used
127
        /// in the case where there is no prefix. If false, no prefix means no namespace.</param>
128
        /// <returns>The namespace URI associated with the prefix (or absence thereof) in the supplied
129
        /// lexical QName. The "null namespace" is represented by a zero length string. The method returns null
130
        /// if there is no known binding for the prefix used in the lexical QName.</returns>
131

    
132
        public string GetNamespaceURI(string lexicalName, Boolean useDefault)
133
        {
134
            String[] parts = net.sf.saxon.om.NameChecker.checkQNameParts(net.sf.saxon.value.Whitespace.trimWhitespace(lexicalName));
135

    
136
            return compiler.getUnderlyingStaticContext().getNamespaceResolver().getURIForPrefix(parts[0], useDefault);
137
        }
138

    
139
        /// <summary>
140
        /// Get the <c>Processor</c> from which this <c>XPathCompiler</c> was constructed
141
        /// </summary>
142

    
143
        public Processor Processor
144
        {
145
            get { return processor; }
146
        }
147

    
148
        /// <summary>
149
        /// Import schema definitions for a specified namespace. That is, add the element and attribute declarations 
150
		/// and type definitions contained in a given namespace to the static context for the XPath expression.
151
        /// </summary>
152
        /// <remarks>
153
        /// <para>This method will not cause the schema to be loaded. That must be done separately, using the
154
        /// <c>SchemaManager</c>. This method will not fail if the schema has not been loaded (but in that case
155
        /// the set of declarations and definitions made available to the XPath expression is empty). The schema
156
        /// document for the specified namespace may be loaded before or after this method is called.
157
        /// </para>
158
        /// <para>
159
        /// This method does not bind a prefix to the namespace. That must be done separately, using the
160
        /// <c>DeclareNamespace</c> method.
161
        /// </para>
162
        /// </remarks>
163
        /// <param name="uri">The namespace URI whose declarations and type definitions are to
164
        /// be made available for use within the XPath expression.</param>
165

    
166
        public void ImportSchemaNamespace(String uri)
167
        {
168
            compiler.importSchemaNamespace(uri);
169
        }
170

    
171

    
172
        /// <summary>
173
        /// This property indicates whether the XPath expression may contain references to variables that have not been
174
        /// explicitly declared by calling <c>DeclareVariable</c>. The property is false by default (that is, variables
175
        /// must be declared).
176
        /// </summary>
177
        /// <remarks>
178
        /// If undeclared variables are permitted, then it is possible to determine after compiling the expression which
179
        /// variables it refers to by calling the method <c>EnumerateExternalVariables</c> on the <c>XPathExecutable</c> object.
180
        /// </remarks>
181
        
182
		public Boolean AllowUndeclaredVariables
183
        {
184
            get
185
            {
186
                return compiler.isAllowUndeclaredVariables();
187
            }
188
            set
189
            {
190
                compiler.setAllowUndeclaredVariables(value);
191
            }
192
        }
193

    
194
        /// <summary>
195
		/// Say whether XPath expressions compiled using this <c>XPathCompiler</c> are
196
        /// schema-aware. They will automatically be schema-aware if the method
197
        /// <see cref="ImportSchemaNamespace"/> is called. An XPath expression
198
        /// must be marked as schema-aware if it is to handle typed (validated)
199
        /// input documents.
200
        /// </summary>
201

    
202
        public Boolean SchemaAware
203
        {
204
            get {
205
				return compiler.isSchemaAware();
206
            }
207
            set 
208
            {
209
                compiler.setSchemaAware(value);
210
            }
211
        }
212

    
213

    
214
        private int checkSingleChar(String s)
215
        {
216
            int[] e = net.sf.saxon.value.StringValue.expand(s);
217
            if (e.Length != 1)
218
            {
219
                throw new ArgumentException("Attribute \"" + s + "\" should be a single character");
220
            }
221
            return e[0];
222

    
223
        }
224

    
225
        /// <summary>
226
		/// Sets a property of a selected decimal format, for use by the <c>format-number()</c> function.
227
        /// </summary>
228
        /// <remarks>
229
        /// This method checks that the value is valid for the particular property, but it does not
230
        /// check that all the values for the decimal format are consistent (for example, that the
231
        /// decimal separator and grouping separator have different values). This consistency
232
        /// check is performed only when the decimal format is used.
233
        /// </remarks>
234
        /// <param name="format">The name of the decimal format whose property is to be set.
235
        ///  Supply null to set a property of the default (unnamed) decimal format.
236
		///  This correponds to a name used in the third argument of <c>format-number()</c>.</param>
237
        /// <param name="property">The name of the property to set: one of
238
        ///   "decimal-separator", "grouping-separator", "infinity", "NaN",
239
        ///   "minus-sign", "percent", "per-mille", "zero-digit", "digit",
240
        ///   or "pattern-separator".</param>
241
        /// <param name="value">The new value for the property.</param>
242

    
243
        public void SetDecimalFormatProperty(QName format, String property, String value) {
244
            try
245
            {
246
                compiler.setDecimalFormatProperty(format.UnderlyingQName(), property, value);
247
            }
248
            catch (JSaxonApiException e) {
249
                throw new StaticError(e);
250
            }
251

    
252
        }
253

    
254
        /// <summary>
255
        /// Declare a variable for use by the XPath expression. If the expression
256
        /// refers to any variables, then they must be declared here, unless the
257
        /// <c>AllowUndeclaredVariables</c> property has been set to true.
258
        /// </summary>
259
        /// <param name="name">The name of the variable, as a <c>QName</c></param>
260

    
261

    
262
        public void DeclareVariable(QName name)
263
        {
264
            compiler.declareVariable(name.UnderlyingQName());
265
        }
266

    
267
        /// <summary>
268
        /// This property indicates which version of XPath language syntax is accepted. The accepted values
269
        /// are "2.0", "3.0", and "3.1". The default is "3.1".
270
        /// </summary>
271
        /// <remarks>
272
        /// <para>Requesting a value other than 3.1 restricts the XPath grammar to constructs defined
273
        /// in the appropriate version, and uses the appropriate subsets of the functions in the standard
274
        /// function library. However, the semantics of remaining constructs will generally follow the XPath 3.1
275
        /// rules. For example, there is a rule in XPath 2.0 that casting to
276
		/// <c>xs:QName</c> is only permitted if the operand is a string literal, but this is not enforced when
277
        /// this property is set to "2.0".</para>
278
        /// <para>There is no support for XPath 1.0.</para>
279
        /// </remarks>
280

    
281

    
282
        public string XPathLanguageVersion
283
        {
284
			get {
285
                try
286
                {
287
                    return compiler.getLanguageVersion();
288
                }
289
                catch (Exception) {
290
                    throw new StaticError(new net.sf.saxon.trans.XPathException("Unknown XPath version " + compiler.getLanguageVersion()));
291
                }
292
            }
293
            set { 
294
                try
295
                {
296
                    compiler.setLanguageVersion(value);
297
                }
298
                catch (Exception)
299
                {
300
                    throw new StaticError(new net.sf.saxon.trans.XPathException("Unknown XPath version " + value));
301
                }
302
                
303
            }
304
        }
305

    
306

    
307
        /// <summary>
308
        /// The required context item type for the expression. This is used for
309
        /// optimizing the expression at compile time, and to check at run-time
310
        /// that the value supplied for the context item is the correct type.
311
        /// </summary>
312

    
313
        public XdmItemType ContextItemType
314
        {
315
            get { return XdmItemType.MakeXdmItemType( compiler.getRequiredContextItemType().getUnderlyingItemType()); }
316
            set { compiler.setRequiredContextItemType(value.Unwrap()); }
317
        }
318

    
319

    
320
        /// <summary>
321
        /// The base URI of the expression, which forms part of the static context
322
        /// of the expression. This is used for resolving any relative URIs appearing
323
        /// within the expression, for example in the argument to the <c>doc()</c> function.
324
        /// </summary>
325

    
326
        public String BaseUri
327
        {
328
			get { return compiler.getBaseURI().getRawPath(); }
329
            set {
330
               compiler.setBaseURI(new java.net.URI(value)); 
331
            }
332
        }
333

    
334
        /// <summary>
335
        /// XPath 1.0 Backwards Compatibility Mode (that is, XPath 1.0 compatibility mode). 
336
        /// If true, backwards compatibility mode
337
        /// is set. In backwards compatibility mode, more implicit type conversions are
338
        /// allowed in XPath expressions, for example it is possible to compare a number
339
        /// with a string. The default is false (backwards compatibility mode is off).
340
        /// </summary>
341
        /// <remarks>
342
        /// <para>Setting XPath 1.0 compatibility mode does not prevent the use of constructs
343
        /// defined in a later XPath version; rather, it modifies the semantics of some
344
        /// constructs.</para></remarks>
345

    
346
        public Boolean BackwardsCompatible
347
        {
348
            get { return compiler.isBackwardsCompatible(); }
349
            set {
350
                compiler.setBackwardsCompatible(value);
351
            }
352
        }
353

    
354
        /// <summary>
355
        /// This property controls caching of compiled XPath expressions. If caching is enabled,
356
        /// then compiled expressions are saved in a cache and reused if the same expression is compiled
357
        /// again. The cache is cleared (invalidated) if any change is made to the properties of the
358
        /// <c>XPathCompiler</c> that would affect the validity of cached expressions. Caching is disabled
359
        /// by default.
360
        /// </summary>
361

    
362
        public Boolean Caching
363
        {
364
            get { 
365
                return compiler.isCaching(); 
366
            }
367
            set
368
            {
369
                compiler.setCaching(value);
370
            }
371
        }
372

    
373

    
374
		/// <summary>
375
		/// Request fast compilation. Fast compilation will generally be achieved at the expense of run-time performance
376
		/// and quality of diagnostics. Fast compilation is a good trade-off if (a) the expression is known to be correct,
377
		/// and (b) once compiled, it is only executed once against a document of modest size.
378
		/// </summary>
379
		/// <remarks>
380
		/// Set to true to request fast compilation; set to false to revert to the optimization options
381
		/// defined in the Configuration.
382
		/// </remarks>
383
        
384
		public bool FastCompliation
385
        {
386

    
387
            set { compiler.setFastCompilation(value); }
388
            get { return compiler.isFastCompilation(); }
389
        }
390

    
391
        /// <summary>
392
		/// Compile an expression supplied as a <c>String</c>.
393
        /// </summary>
394
        /// <example>
395
        /// <code>
396
        /// XPathExecutable q = compiler.Compile("distinct-values(//*/node-name()");
397
        /// </code>
398
        /// </example>
399
        /// <param name="source">A string containing the source text of the XPath expression</param>
400
        /// <returns>An <c>XPathExecutable</c> which represents the compiled XPath expression object.
401
		/// The <c>XPathExecutable</c> may be run as many times as required, in the same or a different
402
        /// thread. The <c>XPathExecutable</c> is not affected by any changes made to the <c>XPathCompiler</c>
403
        /// once it has been compiled.</returns>
404
        /// <exception cref="StaticError">
405
        /// Throws a <c>Saxon.Api.StaticError</c> if there is any static error in the XPath expression.
406
        /// This includes both syntax errors, semantic errors such as references to undeclared functions or
407
        /// variables, and statically-detected type errors.
408
        /// </exception>
409

    
410
        public XPathExecutable Compile(String source)
411
        {
412
            try { 
413
                JXPathExecutable executable = compiler.compile(source);
414
                return new XPathExecutable(executable);
415
           
416
            }
417
            catch (JSaxonApiException err)
418
            {
419
                throw new StaticError(err);
420
            }
421
        }
422

    
423
        /// <summary>
424
		/// Compile and execute an expression supplied as a <c>String</c>, with a given context item.
425
        /// </summary>
426
        /// <param name="expression">A string containing the source text of the XPath expression</param>
427
        /// <param name="contextItem">The context item to be used for evaluation of the XPath expression.
428
        /// May be null, in which case the expression is evaluated without any context item.</param>
429
        /// <returns>An <c>XdmValue</c> which is the result of evaluating the XPath expression.</returns>
430
        /// <exception cref="StaticError">
431
        /// Throws a <c>Saxon.Api.StaticError</c> if there is any static error in the XPath expression.
432
        /// This includes both syntax errors, semantic errors such as references to undeclared functions or
433
        /// variables, and statically-detected type errors.
434
        /// </exception>
435
        /// <exception cref="DynamicError">
436
        /// Throws a <c>Saxon.Api.DynamicError</c> if there is any dynamic error during evaluation of the XPath expression.
437
        /// This includes, for example, referring to the context item if no context item was supplied.
438
        /// </exception>
439

    
440
        public XdmValue Evaluate(String expression, XdmItem contextItem)
441
        {
442
            try
443
            {
444
                net.sf.saxon.s9api.XdmValue value = compiler.evaluate(expression, contextItem == null ? null : XdmItem.FromXdmItemItemToJXdmItem(contextItem));
445
                return XdmValue.Wrap(value.getUnderlyingValue());
446
            }
447
            catch (JSaxonApiException err)
448
            {
449
                if (err.getCause() is JXPathException)
450
                {
451
                    JXPathException xpathException = (JXPathException)err.getCause();
452
                    if (xpathException.isStaticError())
453
                    {
454
                        throw new StaticError(err);
455
                    }
456
                    else
457
                    {
458
                        throw new DynamicError(err.getMessage());
459
                    }
460
                }
461
                else
462
                {
463
                    throw new StaticError(err);
464
                }
465
            }
466
        }
467

    
468
        /// <summary>
469
		/// Compile and execute an expression supplied as a <c>String</c>, with a given context item, where
470
        /// the expression is expected to return a single item as its result.
471
        /// </summary>
472
        /// <param name="expression">A string containing the source text of the XPath expression</param>
473
        /// <param name="contextItem">The context item to be used for evaluation of the XPath expression.
474
        /// May be null, in which case the expression is evaluated without any context item.</param>
475
        /// <returns>If the XPath expression returns a singleton, then the the <c>XdmItem</c> 
476
        /// which is the result of evaluating the XPath expression. If the expression returns an empty sequence,
477
        /// then null. If the expression returns a sequence containing more than one item, then the first
478
        /// item in the result.</returns>
479
        /// <exception cref="StaticError">
480
        /// Throws a <c>Saxon.Api.StaticError</c> if there is any static error in the XPath expression.
481
        /// This includes both syntax errors, semantic errors such as references to undeclared functions or
482
        /// variables, and statically-detected type errors.
483
        /// </exception>
484
        /// <exception cref="DynamicError">
485
        /// Throws a <c>Saxon.Api.DynamicError</c> if there is any dynamic error during evaluation of the XPath expression.
486
        /// This includes, for example, referring to the context item if no context item was supplied.
487
        /// </exception>
488

    
489
        public XdmItem EvaluateSingle(String expression, XdmItem contextItem)
490
        {
491

    
492
            try
493
            {
494
                JXdmItem value = compiler.evaluateSingle(expression, contextItem == null ? null : XdmItem.FromXdmItemItemToJXdmItem(contextItem));
495
                return (value == null ? null : (XdmItem)XdmValue.Wrap(value.getUnderlyingValue()));
496
            }
497
            catch (JSaxonApiException err)
498
            {
499
                if (err.getCause() is JXPathException)
500
                {
501
                    JXPathException xpathException = (JXPathException)err.getCause();
502
                    if (xpathException.isStaticError())
503
                    {
504
                        throw new StaticError(err);
505
                    }
506
                    else {
507
                        throw new DynamicError(err.getMessage());
508
                    }
509
                }
510
                else
511
                {
512
                    throw new StaticError(err);
513
                }
514
            }
515
            
516
        }
517

    
518
    }
519

    
520
    /// <summary>
521
    /// An <c>XPathExecutable</c> represents the compiled form of an XPath expression. 
522
    /// To evaluate the expression, it must first be loaded to form an <c>XPathSelector</c>.
523
    /// </summary>
524
    /// <remarks>
525
    /// <para>An <c>XPathExecutable</c> is immutable, and therefore thread-safe. It is simplest to
526
    /// load a new <c>XPathSelector</c> each time the expression is to be evaluated. However, the 
527
    /// <c>XPathSelector</c> is serially reusable within a single thread.</para>
528
    /// <para>An <c>XPathExecutable</c> is created by using the <c>Compile</c>
529
    /// method on the <c>XPathCompiler</c> class.</para>
530
    /// </remarks>    
531

    
532
    [Serializable]
533
    public class XPathExecutable
534
    {
535

    
536
        private JXPathExecutable executable;
537

    
538
        // internal constructor
539

    
540
        internal XPathExecutable(JXPathExecutable executable)
541
        {
542
            this.executable = executable;
543
        }
544

    
545
        /// <summary>
546
        /// Get a list of external variables used by the expression. This will include both variables that were explicitly
547
        /// declared to the <c>XPathCompiler</c>, and (if the <c>AllowUndeclaredVariables</c> option was set) variables that
548
        /// are referenced within the expression but not explicitly declared.
549
        /// </summary>
550
        /// <returns>
551
		/// An <c>IEnumerator</c> over the names of the external variables, as instances of <c>QName</c>.
552
		/// </returns>
553
        
554
        public IEnumerator EnumerateExternalVariables()
555
        {
556
            ArrayList list = new ArrayList();
557
            JIterator iter = executable.iterateExternalVariables();
558
            while (iter.hasNext())
559
            {
560
                net.sf.saxon.s9api.QName var = (net.sf.saxon.s9api.QName)iter.next();
561
                list.Add(new QName(var));
562
            }
563
            return list.GetEnumerator();
564
        }
565

    
566
        /// <summary>
567
        /// Get a list of external variables used by the expression. This will include both variables that were explicitly
568
        /// declared to the <c>XPathCompiler</c>, and (if the <c>AllowUndeclaredVariables</c> option was set) variables that
569
        /// are referenced within the expression but not explicitly declared.
570
        /// </summary>
571
        /// <returns>
572
        /// An <c>IEnumerator</c> over the names of the external variables, as instances of <c>QName</c>.
573
        /// </returns>
574

    
575
        public IEnumerator<QName> EnumerateExternalVariables2()
576
        {
577
            IList<QName> list = new List<QName>();
578
            JIterator iter = executable.iterateExternalVariables();
579
            while (iter.hasNext())
580
            {
581
                net.sf.saxon.s9api.QName var = (net.sf.saxon.s9api.QName)iter.next();
582
                list.Add(new QName(var));
583
            }
584
            return list.GetEnumerator();
585
        }
586

    
587

    
588
        /// <summary>
589
        /// Get the required cardinality of a declared variable in the static context of the expression.
590
        /// </summary>
591
        /// <remarks>
592
        /// <para>The result is given as an occurrence indicator, one of:</para>
593
        /// <list>
594
        /// <item>'?' (zero-or-one)</item> 
595
        /// <item>'*' (zero-or-more)</item>
596
        /// <item>'+' (one-or-more)</item>
597
        /// <item>' ' (a single space) (exactly one)</item> 
598
        /// <item>'º' (masculine ordinal indicator, xBA) (exactly zero)</item>
599
        /// </list>
600
        /// <para>The type <c>empty-sequence()</c> can be represented by an occurrence indicator of 'º' with 
601
        /// any item type.</para>
602
        /// <para>If the variable was explicitly declared, this will be the occurrence indicator that was set when the
603
        /// variable was declared. If no item type was set, it will be 
604
        /// <see cref="net.sf.saxon.s9api.OccurrenceIndicator#ZERO_OR_MORE"/>.</para>
605
        /// <para>If the variable was implicitly declared by reference (which can happen only when the
606
        /// <c>allowUndeclaredVariables</c> option is set), the returned type will be
607
        /// <see cref="net.sf.saxon.s9api.OccurrenceIndicator#ZERO_OR_MORE"/>.</para>
608
        /// <para>If no variable with the specified <c>QName</c> has been declared either explicitly or implicitly,
609
        /// the method returns '0'.</para>
610
        /// </remarks>
611
        /// <param name="variableName">the name of a declared variable</param>
612
        /// <returns>The required cardinality, in the form of an occurrence indicator.</returns>
613

    
614

    
615
        public char GetRequiredCardinalityForVariable(QName variableName)
616
        {
617
            JXPathVariable var = ((JIndependentContext)executable.getUnderlyingStaticContext()).getExternalVariable(variableName.ToStructuredQName());
618
            if (var == null)
619
            {
620
                return '0';
621
            }
622
            else
623
            {
624
                return GetOccurrenceIndicator(var.getRequiredType().getCardinality());
625
            }
626
        }
627

    
628

    
629
        //internal method
630

    
631
        internal char GetOccurrenceIndicator(int occurrenceIndicator)
632
        {
633
                switch (occurrenceIndicator)
634
                {
635
                    case JStaticProperty.ALLOWS_ZERO_OR_MORE:
636

    
637
                        return XdmSequenceType.ZERO_OR_MORE;
638

    
639
                    case JStaticProperty.ALLOWS_ONE_OR_MORE:
640

    
641
                        return XdmSequenceType.ONE_OR_MORE;
642

    
643
                    case JStaticProperty.ALLOWS_ZERO_OR_ONE:
644

    
645
                        return XdmSequenceType.ZERO_OR_ONE;
646

    
647
                    case JStaticProperty.EXACTLY_ONE:
648

    
649
                        return XdmSequenceType.ONE;
650

    
651
                    case JStaticProperty.ALLOWS_ZERO:
652

    
653
                        return XdmSequenceType.ZERO;
654

    
655
                    default:
656
                        throw new ArgumentException("Unknown occurrence indicator");
657
                }
658
            
659
        }
660

    
661

    
662
        /// <summary>
663
        /// Load the compiled XPath expression to prepare it for execution.
664
        /// </summary>
665
        /// <returns>
666
        /// An <c>XPathSelector</c>. The returned <c>XPathSelector</c> can be used to
667
        /// set up the dynamic context, and then to evaluate the expression.
668
        /// </returns>
669

    
670
        public XPathSelector Load()
671
        {
672
            JXPathSelector selector = executable.load();
673
            return new XPathSelector(selector);
674
        }
675
    }
676

    
677
    /// <summary inherits="IEnumerable">
678
    /// An <c>XPathSelector</c> represents a compiled and loaded XPath expression ready for execution.
679
    /// The <c>XPathSelector</c> holds details of the dynamic evaluation context for the XPath expression.
680
    /// </summary>
681
    /// <remarks>
682
    /// <para>An <c>XPathSelector</c> should not be used concurrently in multiple threads. It is safe,
683
    /// however, to reuse the object within a single thread to evaluate the same XPath expression several times.
684
    /// Evaluating the expression does not change the context that has been established.</para>
685
    /// <para>An <c>XPathSelector</c> is always constructed by running the <c>Load</c> method of
686
    /// an <c>XPathExecutable</c>.</para>
687
    /// <para>The class <c>XPathSelector</c> implements <c>IEnumerable</c>, so it is possible to
688
	/// enumerate the results in a <c>for</c> expression.</para>
689
    /// </remarks>     
690

    
691
    [Serializable]
692
    public class XPathSelector : IEnumerable
693
    {
694
        JXPathSelector selector;
695

    
696
        // internal constructor
697

    
698
        internal XPathSelector(JXPathSelector selector)
699
        {
700
            this.selector = selector;
701
        }
702

    
703
        /// <summary>
704
        /// The context item for the XPath expression evaluation.
705
        /// </summary>
706
        /// <remarks> This may be either a node or an atomic
707
        /// value. Most commonly it will be a document node, which might be constructed
708
        /// using the <c>Build</c> method of the <c>DocumentBuilder</c> object.
709
        /// </remarks>
710

    
711
        public XdmItem ContextItem
712
        {
713
            get { return (XdmItem)XdmValue.Wrap(selector.getContextItem().getUnderlyingValue()); }
714
            set
715
            {
716
                if (value == null)
717
                {
718
                    throw new ArgumentException("contextItem is null");
719
                }
720
                try
721
                {
722
                    selector.setContextItem(XdmItem.FromXdmItemItemToJXdmItem(value));
723
                }
724
                catch (net.sf.saxon.trans.XPathException err)
725
                {
726
                    throw new StaticError(err);
727
                }
728
            }
729
        }
730

    
731
        /// <summary>
732
        /// Set the value of a variable
733
        /// </summary>
734
        /// <param name="name">The name of the variable. This must match the name of a variable
735
		/// that was declared to the <c>XPathCompiler</c>. No error occurs if the expression does not
736
        /// actually reference a variable with this name.</param>
737
        /// <param name="value">The value to be given to the variable.</param>
738

    
739
        public void SetVariable(QName name, XdmValue value)
740
        {
741
            try
742
            {
743
                selector.setVariable(name.UnderlyingQName(), value== null ? null : XdmValue.FromGroundedValueToJXdmValue(value.value));
744

    
745
            }
746
            catch (JXPathException err)
747
            {
748
                throw new StaticError(err);
749
            }
750
        }
751

    
752
        /// <summary>
753
        /// The <code>XmlResolver</code> to be used at run-time to resolve and dereference URIs
754
        /// supplied to the <c>doc()</c> function.
755
        /// </summary>
756

    
757
        public XmlResolver InputXmlResolver
758
        {
759
            get
760
            {
761
                return ((JDotNetURIResolver)selector.getURIResolver()).getXmlResolver();
762
            }
763
            set
764
            {
765
                selector.setURIResolver(new JDotNetURIResolver(value));
766
            }
767
        }
768

    
769
        /// <summary>
770
        /// Evaluate the expression, returning the result as an <c>XdmValue</c> (that is,
771
        /// a sequence of nodes, atomic values, and possibly function items such as maps and arrays).
772
        /// </summary>
773
        /// <remarks>
774
        /// Although a singleton result <i>may</i> be represented as an <c>XdmItem</c>, there is
775
        /// no guarantee that this will always be the case. If you know that the expression will return at
776
        /// most one node or atomic value, it is best to use the <c>EvaluateSingle</c> method, which 
777
        /// does guarantee that an <c>XdmItem</c> (or null) will be returned.
778
        /// </remarks>
779
        /// <returns>
780
        /// An <c>XdmValue</c> representing the results of the expression. 
781
        /// </returns>
782
        /// <exception cref="DynamicError">
783
        /// Throws <c>Saxon.Api.DynamicError</c> if the evaluation of the XPath expression fails
784
        /// with a dynamic error.
785
        /// </exception>
786

    
787
        public XdmValue Evaluate()
788
        {
789
            try {
790
                net.sf.saxon.s9api.XdmValue value = selector.evaluate();
791
                return value == null ? null : XdmValue.Wrap(value.getUnderlyingValue());
792
            } catch(JSaxonApiException err) {
793
                throw new DynamicError(err);
794
            }
795
        }
796

    
797
        /// <summary>
798
        /// Evaluate the XPath expression, returning the result as an <c>XdmItem</c> (that is,
799
        /// a single node or atomic value).
800
        /// </summary>
801
        /// <returns>
802
        /// An <c>XdmItem</c> representing the result of the expression, or null if the expression
803
        /// returns an empty sequence. If the expression returns a sequence of more than one item,
804
        /// any items after the first are ignored.
805
        /// </returns>
806
        /// <exception cref="DynamicError">
807
        /// Throws <c>Saxon.Api.DynamicError</c> if the evaluation of the XPath expression fails
808
        /// with a dynamic error.
809
        /// </exception>
810

    
811

    
812
        public XdmItem EvaluateSingle()
813
        {
814

    
815
            try
816
            {
817
                JXdmItem item = selector.evaluateSingle();
818
                return item == null ? null : (XdmItem)XdmValue.Wrap(item.getUnderlyingValue().materialize());
819
            }
820
            catch (JSaxonApiException err)
821
            {
822
                throw new DynamicError(err);
823
            }
824

    
825
        }
826
        
827
        /// <summary>
828
        /// Evaluate the effective boolean value of the XPath expression, returning the result as a <c>Boolean</c>
829
        /// </summary>
830
        /// <returns>
831
        /// A <c>Boolean</c> representing the result of the expression, converted to its
832
		/// effective boolean value as if by applying the XPath <c>boolean()</c> function
833
        /// </returns>
834
        /// <exception cref="DynamicError">
835
        /// Throws <c>Saxon.Api.DynamicError</c> if the evaluation of the XPath expression fails
836
        /// with a dynamic error.
837
        /// </exception>
838

    
839

    
840
        public Boolean EffectiveBooleanValue()
841
        {
842
            try
843
            {
844
                return selector.effectiveBooleanValue();
845
            } catch(JSaxonApiException err) {
846
                throw new DynamicError(err);
847
            }
848
        }
849
        
850

    
851
        /// <summary>
852
        /// Evaluate the expression, returning the result as an <c>IEnumerator</c> (that is,
853
        /// an enumerator over a sequence of nodes and/or atomic values).
854
        /// </summary>
855
        /// <returns>
856
        /// An enumerator over the sequence that represents the results of the expression.
857
        /// Each object in this sequence will be an instance of <c>XdmItem</c>. Note
858
        /// that the expression may be evaluated lazily, which means that a successful response
859
        /// from this method does not imply that the expression has executed successfully: failures
860
        /// may be reported later while retrieving items from the iterator. 
861
        /// </returns>
862
        /// <exception cref="DynamicError">
863
        /// May throw a <c>Saxon.Api.DynamicError</c> if the evaluation of the XPath expression fails
864
        /// with a dynamic error. However, some errors will not be detected during the invocation of this
865
		/// method, but only when stepping through the returned <c>SequenceEnumerator</c>.
866
        /// </exception>
867

    
868
        public IEnumerator GetEnumerator()
869
        {
870
            try {
871
                return new SequenceEnumerator<XdmItem>(selector.iterator());
872
            } catch (net.sf.saxon.s9api.SaxonApiUncheckedException err) {
873
                throw new DynamicError(JXPathException.makeXPathException(err));
874
            }
875
        }
876

    
877
    }
878

    
879
}
880

    
881
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
882
// Copyright (c) 2020 Saxonica Limited.
883
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
884
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
885
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
886
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
(9-9/11)