Project

Profile

Help

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

he / latest9.9 / hen / csource / api / Saxon.Api / XPath.cs @ a2e6e9df

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
        /// <summary>
63
        /// Create a collation based on a given <c>CompareInfo</c> and <c>CompareOptions</c>    
64
        /// </summary>
65
        /// <remarks>
66
        /// In the current and recent releases of Saxon, collations are always defined at the level of a <c>Configuration</c>.
67
        /// Declaring a collation here may therefore have wider effects than intended. It is recommended not to use
68
        /// this method, but to use <see cref="Processor.DeclareCollation(Uri, CompareInfo, CompareOptions)"/> instead.
69
        /// </remarks>
70
        /// <param name="uri">The collation URI to be used within the XPath expression to refer to this collation</param>
71
        /// <param name="compareInfo">The <c>CompareInfo</c>, which determines the language-specific
72
        /// collation rules to be used</param>
73
        /// <param name="options">Options to be used in performing comparisons, for example
74
        /// whether they are to be case-blind and/or accent-blind</param>
75
        /// <param name="isDefault">If true, this collation will be used as the default collation</param>
76
        [Obsolete("Since 9.6. All collations are now registered at the level of the Configuration. If this method" +
77
            "is called, the effect is (a) the supplied collation" +
78
            "is registered with the configuration, and (b) if isDefault=true, the collation" +
79
            "becomes the default collation for this static context.")]
80
        public void DeclareCollation(Uri uri, CompareInfo compareInfo, CompareOptions options, Boolean isDefault)
81
        {
82
			JDotNetComparator comparator = new JDotNetComparator(uri.ToString(), compareInfo, options);
83
            ((net.sf.saxon.sxpath.AbstractStaticContext)compiler.getUnderlyingStaticContext()).declareCollation(uri.ToString(), comparator, isDefault);
84
        }
85

    
86
        
87
        /// <summary>
88
        /// Declare the default collation
89
        /// </summary>
90
        /// <param name="uri">the absolute URI of the default collation. This URI must identify a known collation;
91
        /// either one that has been explicitly declared, or one that is recognized implicitly, such as a UCA collation</param>
92
        public void DeclareDefaultCollation(String uri)
93
        {
94
            compiler.declareDefaultCollation(uri);
95
        }
96

    
97
        /// <summary>
98
        /// Declare a namespace for use by the XPath expression.
99
        /// </summary>
100
        /// <param name="prefix">The namespace prefix to be declared. Use
101
        /// a zero-length string to declare the default namespace (that is, the
102
        /// default namespace for elements and types).</param>
103
        /// <param name="uri">The namespace URI. It is possible to specify
104
        /// a zero-length string to "undeclare" a namespace.</param>
105

    
106
        public void DeclareNamespace(String prefix, String uri)
107
        {
108
            compiler.declareNamespace(prefix, uri);
109
        }
110
        
111
        /// <summary>
112
        /// Get the namespace URI part of a QName provided in lexical form (<c>prefix:localname</c>)
113
        /// </summary>
114
        /// <param name="lexicalName">The lexical QName. This may either be a plain <c>NCName</c> (a local name
115
        /// with no prefix or colon) or a lexical name using a prefix that is bound to a namespace.</param>
116
        /// <param name="useDefault">Set to true if the default namespace for elements and types is to be used
117
        /// in the case where there is no prefix. If false, no prefix means no namespace.</param>
118
        /// <returns>The namespace URI associated with the prefix (or absence thereof) in the supplied
119
        /// lexical QName. The "null namespace" is represented by a zero length string. The method returns null
120
        /// if there is no known binding for the prefix used in the lexical QName.</returns>
121

    
122
        public string GetNamespaceURI(string lexicalName, Boolean useDefault) 
123
        {            
124
			String[] parts = net.sf.saxon.om.NameChecker.checkQNameParts(net.sf.saxon.value.Whitespace.trimWhitespace(lexicalName));
125
            
126
            return compiler.getUnderlyingStaticContext().getNamespaceResolver().getURIForPrefix(parts[0], useDefault);        
127
        }
128

    
129
        /// <summary>
130
		/// Get the <c>Processor</c> from which this <c>XPathCompiler</c> was constructed
131
        /// </summary>
132
        
133
		public Processor Processor
134
        {
135
            get { return processor; }
136
        }
137

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

    
156
        public void ImportSchemaNamespace(String uri)
157
        {
158
            compiler.importSchemaNamespace(uri);
159
        }
160

    
161

    
162
        /// <summary>
163
        /// This property indicates whether the XPath expression may contain references to variables that have not been
164
        /// explicitly declared by calling <c>DeclareVariable</c>. The property is false by default (that is, variables
165
        /// must be declared).
166
        /// </summary>
167
        /// <remarks>
168
        /// If undeclared variables are permitted, then it is possible to determine after compiling the expression which
169
        /// variables it refers to by calling the method <c>EnumerateExternalVariables</c> on the <c>XPathExecutable</c> object.
170
        /// </remarks>
171
        
172
		public Boolean AllowUndeclaredVariables
173
        {
174
            get
175
            {
176
                return compiler.isAllowUndeclaredVariables();
177
            }
178
            set
179
            {
180
                compiler.setAllowUndeclaredVariables(value);
181
            }
182
        }
183

    
184
        /// <summary>
185
		/// Say whether XPath expressions compiled using this <c>XPathCompiler</c> are
186
        /// schema-aware. They will automatically be schema-aware if the method
187
        /// <see cref="ImportSchemaNamespace"/> is called. An XPath expression
188
        /// must be marked as schema-aware if it is to handle typed (validated)
189
        /// input documents.
190
        /// </summary>
191

    
192
        public Boolean SchemaAware
193
        {
194
            get {
195
				return compiler.isSchemaAware();
196
            }
197
            set 
198
            {
199
                compiler.setSchemaAware(value);
200
            }
201
        }
202

    
203

    
204
        private int checkSingleChar(String s)
205
        {
206
            int[] e = net.sf.saxon.value.StringValue.expand(s);
207
            if (e.Length != 1)
208
            {
209
                throw new ArgumentException("Attribute \"" + s + "\" should be a single character");
210
            }
211
            return e[0];
212

    
213
        }
214

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

    
233
        public void SetDecimalFormatProperty(QName format, String property, String value) {
234
            try
235
            {
236
                compiler.setDecimalFormatProperty(format.UnderlyingQName(), property, value);
237
            }
238
            catch (JSaxonApiException e) {
239
                throw new StaticError(e);
240
            }
241

    
242
        }
243

    
244
        /// <summary>
245
        /// Declare a variable for use by the XPath expression. If the expression
246
        /// refers to any variables, then they must be declared here, unless the
247
        /// <c>AllowUndeclaredVariables</c> property has been set to true.
248
        /// </summary>
249
        /// <param name="name">The name of the variable, as a <c>QName</c></param>
250

    
251

    
252
        public void DeclareVariable(QName name)
253
        {
254
            compiler.declareVariable(name.UnderlyingQName());
255
        }
256

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

    
271

    
272
        public string XPathLanguageVersion
273
        {
274
			get {
275
                try
276
                {
277
                    return compiler.getLanguageVersion();
278
                }
279
                catch (Exception e) {
280
                    throw new StaticError(new net.sf.saxon.trans.XPathException("Unknown XPath version " + compiler.getLanguageVersion()));
281
                }
282
            }
283
            set { 
284
                try
285
                {
286
                    compiler.setLanguageVersion(value);
287
                }
288
                catch (Exception e)
289
                {
290
                    throw new StaticError(new net.sf.saxon.trans.XPathException("Unknown XPath version " + value));
291
                }
292
                
293
            }
294
        }
295

    
296

    
297
        /// <summary>
298
        /// The required context item type for the expression. This is used for
299
        /// optimizing the expression at compile time, and to check at run-time
300
        /// that the value supplied for the context item is the correct type.
301
        /// </summary>
302

    
303
        public XdmItemType ContextItemType
304
        {
305
            get { return XdmItemType.MakeXdmItemType( compiler.getRequiredContextItemType().getUnderlyingItemType()); }
306
            set { compiler.setRequiredContextItemType(value.Unwrap()); }
307
        }
308

    
309

    
310
        /// <summary>
311
        /// The base URI of the expression, which forms part of the static context
312
        /// of the expression. This is used for resolving any relative URIs appearing
313
        /// within the expression, for example in the argument to the <c>doc()</c> function.
314
        /// </summary>
315

    
316
        public String BaseUri
317
        {
318
			get { return compiler.getBaseURI().getRawPath(); }
319
            set {
320
               compiler.setBaseURI(new java.net.URI(value)); 
321
            }
322
        }
323

    
324
        /// <summary>
325
        /// XPath 1.0 Backwards Compatibility Mode (that is, XPath 1.0 compatibility mode). 
326
        /// If true, backwards compatibility mode
327
        /// is set. In backwards compatibility mode, more implicit type conversions are
328
        /// allowed in XPath expressions, for example it is possible to compare a number
329
        /// with a string. The default is false (backwards compatibility mode is off).
330
        /// </summary>
331
        /// <remarks>
332
        /// <para>Setting XPath 1.0 compatibility mode does not prevent the use of constructs
333
        /// defined in a later XPath version; rather, it modifies the semantics of some
334
        /// constructs.</para></remarks>
335

    
336
        public Boolean BackwardsCompatible
337
        {
338
            get { return compiler.isBackwardsCompatible(); }
339
            set {
340
                compiler.setBackwardsCompatible(value);
341
            }
342
        }
343

    
344
        /// <summary>
345
        /// This property controls caching of compiled XPath expressions. If caching is enabled,
346
        /// then compiled expressions are saved in a cache and reused if the same expression is compiled
347
        /// again. The cache is cleared (invalidated) if any change is made to the properties of the
348
        /// <c>XPathCompiler</c> that would affect the validity of cached expressions. Caching is disabled
349
        /// by default.
350
        /// </summary>
351

    
352
        public Boolean Caching
353
        {
354
            get { 
355
                return compiler.isCaching(); 
356
            }
357
            set
358
            {
359
                compiler.setCaching(value);
360
            }
361
        }
362

    
363

    
364
		/// <summary>
365
		/// Request fast compilation. Fast compilation will generally be achieved at the expense of run-time performance
366
		/// and quality of diagnostics. Fast compilation is a good trade-off if (a) the expression is known to be correct,
367
		/// and (b) once compiled, it is only executed once against a document of modest size.
368
		/// </summary>
369
		/// <remarks>
370
		/// Set to true to request fast compilation; set to false to revert to the optimization options
371
		/// defined in the Configuration.
372
		/// </remarks>
373
        
374
		public bool FastCompliation
375
        {
376

    
377
            set { compiler.setFastCompilation(value); }
378
            get { return compiler.isFastCompilation(); }
379
        }
380

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

    
400
        public XPathExecutable Compile(String source)
401
        {
402
            try { 
403
                JXPathExecutable executable = compiler.compile(source);
404
                return new XPathExecutable(executable);
405
           
406
            }
407
            catch (JSaxonApiException err)
408
            {
409
                throw new StaticError(err);
410
            }
411
        }
412

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

    
430
        public XdmValue Evaluate(String expression, XdmItem contextItem)
431
        {
432
            try
433
            {
434
                net.sf.saxon.s9api.XdmValue value = compiler.evaluate(expression, contextItem == null ? null : XdmItem.FromXdmItemItemToJXdmItem(contextItem));
435
                return XdmValue.Wrap(value.getUnderlyingValue());
436
            }
437
            catch (JSaxonApiException err)
438
            {
439
                if (err.getCause() is JXPathException)
440
                {
441
                    JXPathException xpathException = (JXPathException)err.getCause();
442
                    if (xpathException.isStaticError())
443
                    {
444
                        throw new StaticError(err);
445
                    }
446
                    else
447
                    {
448
                        throw new DynamicError(err.getMessage());
449
                    }
450
                }
451
                else
452
                {
453
                    throw new StaticError(err);
454
                }
455
            }
456
        }
457

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

    
479
        public XdmItem EvaluateSingle(String expression, XdmItem contextItem)
480
        {
481

    
482
            try
483
            {
484
                JXdmItem value = compiler.evaluateSingle(expression, contextItem == null ? null : XdmItem.FromXdmItemItemToJXdmItem(contextItem));
485
                return (value == null ? null : (XdmItem)XdmValue.Wrap(value.getUnderlyingValue()));
486
            }
487
            catch (JSaxonApiException err)
488
            {
489
                if (err.getCause() is JXPathException)
490
                {
491
                    JXPathException xpathException = (JXPathException)err.getCause();
492
                    if (xpathException.isStaticError())
493
                    {
494
                        throw new StaticError(err);
495
                    }
496
                    else {
497
                        throw new DynamicError(err.getMessage());
498
                    }
499
                }
500
                else
501
                {
502
                    throw new StaticError(err);
503
                }
504
            }
505
            
506
        }
507

    
508
    }
509

    
510
    /// <summary>
511
    /// An <c>XPathExecutable</c> represents the compiled form of an XPath expression. 
512
    /// To evaluate the expression, it must first be loaded to form an <c>XPathSelector</c>.
513
    /// </summary>
514
    /// <remarks>
515
    /// <para>An <c>XPathExecutable</c> is immutable, and therefore thread-safe. It is simplest to
516
    /// load a new <c>XPathSelector</c> each time the expression is to be evaluated. However, the 
517
    /// <c>XPathSelector</c> is serially reusable within a single thread.</para>
518
    /// <para>An <c>XPathExecutable</c> is created by using the <c>Compile</c>
519
    /// method on the <c>XPathCompiler</c> class.</para>
520
    /// </remarks>    
521

    
522
    [Serializable]
523
    public class XPathExecutable
524
    {
525

    
526
        private JXPathExecutable executable;
527

    
528
        // internal constructor
529

    
530
        internal XPathExecutable(JXPathExecutable executable)
531
        {
532
            this.executable = executable;
533
        }
534

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

    
556
        /// <summary>
557
        /// Get a list of external variables used by the expression. This will include both variables that were explicitly
558
        /// declared to the <c>XPathCompiler</c>, and (if the <c>AllowUndeclaredVariables</c> option was set) variables that
559
        /// are referenced within the expression but not explicitly declared.
560
        /// </summary>
561
        /// <returns>
562
        /// An <c>IEnumerator</c> over the names of the external variables, as instances of <c>QName</c>.
563
        /// </returns>
564

    
565
        public IEnumerator<QName> EnumerateExternalVariables2()
566
        {
567
            IList<QName> list = new List<QName>();
568
            JIterator iter = executable.iterateExternalVariables();
569
            while (iter.hasNext())
570
            {
571
                net.sf.saxon.s9api.QName var = (net.sf.saxon.s9api.QName)iter.next();
572
                list.Add(new QName(var));
573
            }
574
            return list.GetEnumerator();
575
        }
576

    
577

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

    
604

    
605
        public char GetRequiredCardinalityForVariable(QName variableName)
606
        {
607
            JXPathVariable var = ((JIndependentContext)executable.getUnderlyingStaticContext()).getExternalVariable(variableName.ToStructuredQName());
608
            if (var == null)
609
            {
610
                return '0';
611
            }
612
            else
613
            {
614
                return GetOccurrenceIndicator(var.getRequiredType().getCardinality());
615
            }
616
        }
617

    
618

    
619
        //internal method
620

    
621
        internal char GetOccurrenceIndicator(int occurrenceIndicator)
622
        {
623
                switch (occurrenceIndicator)
624
                {
625
                    case JStaticProperty.ALLOWS_ZERO_OR_MORE:
626

    
627
                        return XdmSequenceType.ZERO_OR_MORE;
628

    
629
                    case JStaticProperty.ALLOWS_ONE_OR_MORE:
630

    
631
                        return XdmSequenceType.ONE_OR_MORE;
632

    
633
                    case JStaticProperty.ALLOWS_ZERO_OR_ONE:
634

    
635
                        return XdmSequenceType.ZERO_OR_ONE;
636

    
637
                    case JStaticProperty.EXACTLY_ONE:
638

    
639
                        return XdmSequenceType.ONE;
640

    
641
                    case JStaticProperty.ALLOWS_ZERO:
642

    
643
                        return XdmSequenceType.ZERO;
644

    
645
                    default:
646
                        throw new ArgumentException("Unknown occurrence indicator");
647
                }
648
            
649
        }
650

    
651

    
652
        /// <summary>
653
        /// Load the compiled XPath expression to prepare it for execution.
654
        /// </summary>
655
        /// <returns>
656
        /// An <c>XPathSelector</c>. The returned <c>XPathSelector</c> can be used to
657
        /// set up the dynamic context, and then to evaluate the expression.
658
        /// </returns>
659

    
660
        public XPathSelector Load()
661
        {
662
            JXPathSelector selector = executable.load();
663
            return new XPathSelector(selector);
664
        }
665
    }
666

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

    
681
    [Serializable]
682
    public class XPathSelector : IEnumerable
683
    {
684
        JXPathSelector selector;
685

    
686
        // internal constructor
687

    
688
        internal XPathSelector(JXPathSelector selector)
689
        {
690
            this.selector = selector;
691
        }
692

    
693
        /// <summary>
694
        /// The context item for the XPath expression evaluation.
695
        /// </summary>
696
        /// <remarks> This may be either a node or an atomic
697
        /// value. Most commonly it will be a document node, which might be constructed
698
        /// using the <c>Build</c> method of the <c>DocumentBuilder</c> object.
699
        /// </remarks>
700

    
701
        public XdmItem ContextItem
702
        {
703
            get { return (XdmItem)XdmValue.Wrap(selector.getContextItem().getUnderlyingValue()); }
704
            set
705
            {
706
                if (value == null)
707
                {
708
                    throw new ArgumentException("contextItem is null");
709
                }
710
                try
711
                {
712
                    selector.setContextItem(XdmItem.FromXdmItemItemToJXdmItem(value));
713
                }
714
                catch (net.sf.saxon.trans.XPathException err)
715
                {
716
                    throw new StaticError(err);
717
                }
718
            }
719
        }
720

    
721
        /// <summary>
722
        /// Set the value of a variable
723
        /// </summary>
724
        /// <param name="name">The name of the variable. This must match the name of a variable
725
		/// that was declared to the <c>XPathCompiler</c>. No error occurs if the expression does not
726
        /// actually reference a variable with this name.</param>
727
        /// <param name="value">The value to be given to the variable.</param>
728

    
729
        public void SetVariable(QName name, XdmValue value)
730
        {
731
            try
732
            {
733
                selector.setVariable(name.UnderlyingQName(), value== null ? null : XdmValue.FromGroundedValueToJXdmValue(value.value));
734

    
735
            }
736
            catch (JXPathException err)
737
            {
738
                throw new StaticError(err);
739
            }
740
        }
741

    
742
        /// <summary>
743
        /// The <code>XmlResolver</code> to be used at run-time to resolve and dereference URIs
744
        /// supplied to the <c>doc()</c> function.
745
        /// </summary>
746

    
747
        public XmlResolver InputXmlResolver
748
        {
749
            get
750
            {
751
                return ((JDotNetURIResolver)selector.getURIResolver()).getXmlResolver();
752
            }
753
            set
754
            {
755
                selector.setURIResolver(new JDotNetURIResolver(value));
756
            }
757
        }
758

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

    
777
        public XdmValue Evaluate()
778
        {
779
            try {
780
                net.sf.saxon.s9api.XdmValue value = selector.evaluate();
781
                return value == null ? null : XdmValue.Wrap(value.getUnderlyingValue());
782
            } catch(JSaxonApiException err) {
783
                throw new DynamicError(err);
784
            }
785
        }
786

    
787
        /// <summary>
788
        /// Evaluate the XPath expression, returning the result as an <c>XdmItem</c> (that is,
789
        /// a single node or atomic value).
790
        /// </summary>
791
        /// <returns>
792
        /// An <c>XdmItem</c> representing the result of the expression, or null if the expression
793
        /// returns an empty sequence. If the expression returns a sequence of more than one item,
794
        /// any items after the first are ignored.
795
        /// </returns>
796
        /// <exception cref="DynamicError">
797
        /// Throws <c>Saxon.Api.DynamicError</c> if the evaluation of the XPath expression fails
798
        /// with a dynamic error.
799
        /// </exception>
800

    
801

    
802
        public XdmItem EvaluateSingle()
803
        {
804

    
805
            try
806
            {
807
                JXdmItem item = selector.evaluateSingle();
808
                return item == null ? null : (XdmItem)XdmValue.Wrap(item.getUnderlyingValue().materialize());
809
            }
810
            catch (JSaxonApiException err)
811
            {
812
                throw new DynamicError(err);
813
            }
814

    
815
        }
816
        
817
        /// <summary>
818
        /// Evaluate the effective boolean value of the XPath expression, returning the result as a <c>Boolean</c>
819
        /// </summary>
820
        /// <returns>
821
        /// A <c>Boolean</c> representing the result of the expression, converted to its
822
		/// effective boolean value as if by applying the XPath <c>boolean()</c> function
823
        /// </returns>
824
        /// <exception cref="DynamicError">
825
        /// Throws <c>Saxon.Api.DynamicError</c> if the evaluation of the XPath expression fails
826
        /// with a dynamic error.
827
        /// </exception>
828

    
829

    
830
        public Boolean EffectiveBooleanValue()
831
        {
832
            try
833
            {
834
                return selector.effectiveBooleanValue();
835
            } catch(JSaxonApiException err) {
836
                throw new DynamicError(err);
837
            }
838
        }
839
        
840

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

    
858
        public IEnumerator GetEnumerator()
859
        {
860
            try {
861
                return new SequenceEnumerator<XdmItem>(selector.iterator());
862
            } catch (net.sf.saxon.s9api.SaxonApiUncheckedException err) {
863
                throw new DynamicError(JXPathException.makeXPathException(err));
864
            }
865
        }
866

    
867
    }
868

    
869
}
870

    
871
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
872
// Copyright (c) 2018 Saxonica Limited.
873
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
874
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
875
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
876
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
(11-11/13)