Project

Profile

Help

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

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

1
using System;
2
using System.Xml;
3
using System.Collections;
4
using System.Globalization;
5
using JIterator = java.util.Iterator;
6
using JConfiguration = net.sf.saxon.Configuration;
7
using JXPathEvaluator = net.sf.saxon.sxpath.XPathEvaluator;
8
using JItem = net.sf.saxon.om.Item;
9
using JSequenceExtent = net.sf.saxon.value.SequenceExtent;
10
using JSequence = net.sf.saxon.om.Sequence;
11
using JNodeInfo = net.sf.saxon.om.NodeInfo;
12
using JStructuredQName = net.sf.saxon.om.StructuredQName;
13
using JIndependentContext = net.sf.saxon.sxpath.IndependentContext;
14
using JXPathExpression = net.sf.saxon.sxpath.XPathExpression;
15
using JExpression = net.sf.saxon.expr.Expression;
16
using JXPathContext = net.sf.saxon.expr.XPathContext;
17
using JXPathDynamicContext = net.sf.saxon.sxpath.XPathDynamicContext;
18
using JXPathVariable = net.sf.saxon.sxpath.XPathVariable;
19
using JDecimalValue = net.sf.saxon.value.DecimalValue;
20
using JStaticProperty = net.sf.saxon.expr.StaticProperty;
21
using JSaxonApiException = net.sf.saxon.s9api.SaxonApiException;
22
using net.sf.saxon.dotnet;
23

    
24

    
25
namespace Saxon.Api
26
{
27

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

    
50
    [Serializable]
51
    public class XPathCompiler
52
    {
53

    
54
        private JConfiguration config;
55
        private Processor processor;
56
        private JIndependentContext env;
57
        private Hashtable cache = null;
58

    
59
        // internal constructor: the public interface is a factory method
60
        // on the Processor object
61

    
62
        internal XPathCompiler(Processor proc)
63
        {
64
            this.processor = proc;
65
			this.config = proc.Implementation;
66
            this.env = new JIndependentContext(config);
67
        }
68

    
69
        /// <summary>
70
        /// Create a collation based on a given <c>CompareInfo</c> and <c>CompareOptions</c>    
71
        /// </summary>
72
        /// <param name="uri">The collation URI to be used within the XPath expression to refer to this collation</param>
73
        /// <param name="compareInfo">The <c>CompareInfo</c>, which determines the language-specific
74
        /// collation rules to be used</param>
75
        /// <param name="options">Options to be used in performing comparisons, for example
76
        /// whether they are to be case-blind and/or accent-blind</param>
77
        /// <param name="isDefault">If true, this collation will be used as the default collation</param>
78

    
79
        public void DeclareCollation(Uri uri, CompareInfo compareInfo, CompareOptions options, Boolean isDefault)
80
        {
81
            DotNetComparator comparator = new DotNetComparator(compareInfo, options);
82
            env.declareCollation(uri.ToString(), comparator, isDefault);
83
        }
84

    
85

    
86

    
87
        /// <summary>
88
        /// Declare a namespace for use by the XPath expression.
89
        /// </summary>
90
        /// <param name="prefix">The namespace prefix to be declared. Use
91
        /// a zero-length string to declare the default namespace (that is, the
92
        /// default namespace for elements and types).</param>
93
        /// <param name="uri">The namespace URI. It is possible to specify
94
        /// a zero-length string to "undeclare" a namespace.</param>
95

    
96
        public void DeclareNamespace(String prefix, String uri)
97
        {
98
            if (cache != null)
99
            {
100
                cache.Clear();
101
            }
102
            env.declareNamespace(prefix, uri);
103
        }
104

    
105
        public string GetNamespaceURI(string lexicalName, Boolean useDefault) 
106
        {            
107
			String[] parts = net.sf.saxon.om.NameChecker.checkQNameParts(net.sf.saxon.value.Whitespace.trimWhitespace(lexicalName));
108
            return env.getNamespaceResolver().getURIForPrefix(parts[0], useDefault);
109
        
110
        }
111

    
112
        /// <summary>
113
        /// Get the Processor from which this XPathCompiler was constructed
114
        /// </summary>
115
        public Processor Processor
116
        {
117
            get { return processor; }
118
            set { processor = value; }
119
        }
120

    
121
        /// <summary>
122
        /// Import schema definitions for a specified namespace. That is, add the element and attribute declarations and type definitions
123
        /// contained in a given namespace to the static context for the XPath expression.
124
        /// </summary>
125
        /// <remarks>
126
        /// <para>This method will not cause the schema to be loaded. That must be done separately, using the
127
        /// <c>SchemaManager</c>}. This method will not fail if the schema has not been loaded (but in that case
128
        /// the set of declarations and definitions made available to the XPath expression is empty). The schema
129
        /// document for the specified namespace may be loaded before or after this method is called.
130
        /// </para>
131
        /// <para>
132
        /// This method does not bind a prefix to the namespace. That must be done separately, using the
133
        /// <c>declareNamespace</c> method.
134
        /// </para>
135
        /// </remarks>
136
        /// <param name="uri">The namespace URI whose declarations and type definitions are to
137
        /// be made available for use within the XPath expression.</param>
138
        /// 
139

    
140
        public void ImportSchemaNamespace(String uri)
141
        {
142
            if (cache != null)
143
            {
144
                cache.Clear();
145
            }
146
            env.getImportedSchemaNamespaces().add(uri);
147
        }
148

    
149

    
150
        /// <summary>
151
        /// This property indicates whether the XPath expression may contain references to variables that have not been
152
        /// explicitly declared by calling <c>DeclareVariable</c>. The property is false by default (that is, variables
153
        /// must be declared).
154
        /// </summary>
155
        /// <remarks>
156
        /// If undeclared variables are permitted, then it is possible to determine after compiling the expression which
157
        /// variables it refers to by calling the method <c>EnumerateExternalVariables</c> on the <c>XPathExecutable</c> object.
158
        /// </remarks>
159
        /// 
160
        public Boolean AllowUndeclaredVariables
161
        {
162
            get
163
            {
164
                return env.isAllowUndeclaredVariables();
165
            }
166
            set
167
            {
168
                if (cache != null)
169
                {
170
                    cache.Clear();
171
                }
172
                env.setAllowUndeclaredVariables(value);
173
            }
174
        }
175

    
176
        /// <summary>
177
        /// Say whether XPath expressions compiled using this XPathCompiler are
178
        /// schema-aware. They will automatically be schema-aware if the method
179
        /// {@link #importSchemaNamespace(String)} is called. An XPath expression
180
        /// must be marked as schema-aware if it is to handle typed (validated)
181
        /// input documents.
182
        /// </summary>
183

    
184
        public Boolean SchemaAware
185
        {
186
            get {
187
                return env.isSchemaAware();
188
            }
189
            set 
190
            {
191
                env.setSchemaAware(value);
192
            }
193
        }
194

    
195

    
196
        private int checkSingleChar(String s)
197
        {
198
            int[] e = net.sf.saxon.value.StringValue.expand(s);
199
            if (e.Length != 1)
200
            {
201
                throw new ArgumentException("Attribute \"" + s + "\" should be a single character");
202
            }
203
            return e[0];
204

    
205
        }
206

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

    
225
        public void SetDecimalFormatProperty(QName format, String property, String value){
226
            net.sf.saxon.trans.DecimalFormatManager dfm = env.getDecimalFormatManager();
227
            if (dfm == null)
228
            {
229
                dfm = new net.sf.saxon.trans.DecimalFormatManager();
230
                env.setDecimalFormatManager(dfm);
231
            }
232
            
233
            net.sf.saxon.om.StructuredQName sqname = null;
234
            net.sf.saxon.trans.DecimalSymbols symbols = null;
235
            
236
            if (format != null) {
237
                sqname = net.sf.saxon.om.StructuredQName.fromClarkName(format.ClarkName);
238
                symbols = dfm.obtainNamedDecimalFormat(sqname);
239
            } else {
240
                symbols = dfm.getDefaultDecimalFormat();
241
            }
242

    
243
            if (property.Equals("decimal-separator"))
244
            {
245
                symbols.setDecimalSeparator(value);
246
            }
247
            else if (property.Equals("grouping-separator"))
248
            {
249
                symbols.setGroupingSeparator(value);
250
            }
251
            else if (property.Equals("infinity"))
252
            {
253
                symbols.setInfinity(value);
254
            }
255
            else if (property.Equals("NaN"))
256
            {
257
                symbols.setNaN(value);
258
            }
259
            else if (property.Equals("minus-sign"))
260
            {
261
                symbols.setMinusSign(value);
262
            }
263
            else if (property.Equals("percent"))
264
            {
265
                symbols.setPercent(value);
266
            }
267
            else if (property.Equals("per-mille"))
268
            {
269
                symbols.setPerMille(value);
270
            }
271
            else if (property.Equals("zero-digit"))
272
            {
273
                symbols.setZeroDigit(value);
274
                if (!net.sf.saxon.trans.DecimalSymbols.isValidZeroDigit(symbols.getZeroDigit()))
275
                {
276
                    throw new ArgumentException("Value supplied for zero-digit is not a Unicode digit representing zero");
277
                } 
278
            }
279
            else if (property.Equals("digit"))
280
            {
281
                symbols.setDigit(value);
282
            }
283
            else if (property.Equals("pattern-separator"))
284
            {
285
                symbols.setPatternSeparator(value);
286
            }
287
            else
288
            {
289
                throw new ArgumentException("Unknown decimal format property " + property);
290
            }
291

    
292
        }
293

    
294
        /// <summary>
295
        /// Declare a variable for use by the XPath expression. If the expression
296
        /// refers to any variables, then they must be declared here, unless the
297
        /// <c>AllowUndeclaredVariables</c> property has been set to true.
298
        /// </summary>
299
        /// <param name="name">The name of the variable, as a <c>QName</c></param>
300

    
301

    
302
        public void DeclareVariable(QName name)
303
        {
304
            if (cache != null)
305
            {
306
                cache.Clear();
307
            }
308
            JXPathVariable var = env.declareVariable(name.ToQNameValue());
309
            //declaredVariables.Add(var);
310
        }
311

    
312
        /// <summary>
313
        /// This property indicates which version of XPath language syntax is accepted. The default
314
        /// value is "1.0". This property must be set to "3.0" before compiling a query that
315
        /// uses XPath 3.0 (formerly known as XPath 2.1) syntax.
316
        /// </summary>
317
        /// <remarks>
318
        /// <para>Support for XPath 3.0 is currently limited: for details see the Saxon documentation.</para>
319
        /// <para><i>Property added in Saxon 9.4</i></para></remarks>
320

    
321

    
322
        public string XPathLanguageVersion
323
        {
324
            get { return env.getXPathLanguageLevel().toString(); }
325
            set { env.setXPathLanguageLevel((JDecimalValue)JDecimalValue.makeDecimalValue(value.ToString(), true)); }
326
        }
327

    
328

    
329
        /// <summary>
330
        /// The required context item type for the expression. This is used for
331
        /// optimizing the expression at compile time, and to check at run-time
332
        /// that the value supplied for the context item is the correct type.
333
        /// </summary>
334

    
335
        public XdmItemType ContextItemType
336
        {
337
            get { return XdmItemType.MakeXdmItemType(env.getRequiredContextItemType()); }
338
            set { env.setRequiredContextItemType(value.Unwrap()); }
339
        }
340

    
341

    
342
        /// <summary>
343
        /// The base URI of the expression, which forms part of the static context
344
        /// of the expression. This is used for resolving any relative URIs appearing
345
        /// within the expression, for example in references to library modules, schema
346
        /// locations, or as an argument to the <c>doc()</c> function.
347
        /// </summary>
348

    
349
        public String BaseUri
350
        {
351
            get { return env.getBaseURI(); }
352
            set {
353
                if (cache != null)
354
                {
355
                    cache.Clear();
356
                }
357
                env.setBaseURI(value); 
358
            }
359
        }
360

    
361
        /// <summary>
362
        /// XPath 1.0 Backwards Compatibility Mode. If true, backwards compatibility mode
363
        /// is set. In backwards compatibility mode, more implicit type conversions are
364
        /// allowed in XPath expressions, for example it is possible to compare a number
365
        /// with a string. The default is false (backwards compatibility mode is off).
366
        /// </summary>
367

    
368

    
369
        public Boolean BackwardsCompatible
370
        {
371
            get { return env.isInBackwardsCompatibleMode(); }
372
            set {
373
                if (cache != null)
374
                {
375
                    cache.Clear();
376
                }
377
                env.setBackwardsCompatibilityMode(value);
378
            }
379
        }
380

    
381
        /// <summary>
382
        /// XPath 1.0 Backwards Compatibility Mode. If true, backwards compatibility mode
383
        /// is set. In backwards compatibility mode, more implicit type conversions are
384
        /// allowed in XPath expressions, for example it is possible to compare a number
385
        /// with a string. The default is false (backwards compatibility mode is off).
386
        /// </summary>
387

    
388

    
389
        public Boolean Caching
390
        {
391
            get { 
392
                return cache != null; 
393
            }
394
            set
395
            {
396
                cache = (value ? new Hashtable() : null);
397
            }
398
        }
399

    
400

    
401

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

    
421
        public XPathExecutable Compile(String source)
422
        {
423
            if (cache != null)
424
            {
425
                XPathExecutable exec = (XPathExecutable)cache[source];
426
                if (exec != null)
427
                {
428
                    return exec;
429
                }
430
            }
431
            try
432
            {
433
                JIndependentContext ic = env;
434
                if (ic.isAllowUndeclaredVariables())
435
                {
436
                    // self-declaring variables modify the static context. The XPathCompiler must not change state
437
                    // as the result of compiling an expression, so we need to copy the static context.
438
                    ic = new JIndependentContext(env);
439
                    for (JIterator iter = env.iterateExternalVariables(); iter.hasNext(); )
440
                    {
441
                        JXPathVariable var = (JXPathVariable)iter.next();
442
                        JXPathVariable var2 = ic.declareVariable(var.getVariableQName());
443
                    }
444
                }
445
                JXPathEvaluator eval = new JXPathEvaluator(config);
446
                eval.setStaticContext(ic);
447
                JXPathExpression cexp = eval.createExpression(source);
448
                XPathExecutable exec = new XPathExecutable(cexp, config, ic);
449
                if (cache != null)
450
                {
451
                    cache[source] = exec;
452
                }
453
                return exec;
454
            }
455
            catch (net.sf.saxon.trans.XPathException err)
456
            {
457
                throw new StaticError(err);
458
            }
459
        }
460

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

    
478
        public XdmValue Evaluate(String expression, XdmItem contextItem)
479
        {
480
            XPathSelector xs = Compile(expression).Load();
481
            if (contextItem != null)
482
            {
483
                xs.ContextItem = contextItem;
484
            }
485
            return xs.Evaluate();
486
        }
487

    
488
        /// <summary>
489
        /// Compile and execute an expression supplied as a String, with a given context item, where
490
        /// the expression is expected to return a single item as its result
491
        /// </summary>
492
        /// <param name="expression">A string containing the source text of the XPath expression</param>
493
        /// <param name="contextItem">The context item to be used for evaluation of the XPath expression.
494
        /// May be null, in which case the expression is evaluated without any context item.</param>
495
        /// <returns>If the XPath expression returns a singleton, then the the <c>XdmItem</c> 
496
        /// which is the result of evaluating the XPath expression. If the expression returns an empty sequence,
497
        /// then null. If the expression returns a sequence containing more than one item, then the first
498
        /// item in the result.</returns>
499
        /// <exception cref="StaticError">
500
        /// Throws a <c>Saxon.Api.StaticError</c> if there is any static error in the XPath expression.
501
        /// This includes both syntax errors, semantic errors such as references to undeclared functions or
502
        /// variables, and statically-detected type errors.
503
        /// </exception>
504
        /// <exception cref="DynamicError">
505
        /// Throws a <c>Saxon.Api.DynamicError</c> if there is any dynamic error during evaluation of the XPath expression.
506
        /// This includes, for example, referring to the context item if no context item was supplied.
507
        /// </exception>
508

    
509
        public XdmItem EvaluateSingle(String expression, XdmItem contextItem)
510
        {
511
            XPathSelector xs = Compile(expression).Load();
512
            if (contextItem != null)
513
            {
514
                xs.ContextItem = contextItem;
515
            }
516
            return xs.EvaluateSingle();
517
        }
518

    
519
    }
520

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

    
534
    [Serializable]
535
    public class XPathExecutable
536
    {
537

    
538
        private JXPathExpression exp;
539
        private JConfiguration config;
540
        private JIndependentContext env;
541
        //private ArrayList declaredVariables;
542

    
543
        // internal constructor
544

    
545
        internal XPathExecutable(JXPathExpression exp, JConfiguration config,
546
            JIndependentContext env /*, ArrayList declaredVariables*/)
547
        {
548
            this.exp = exp;
549
            this.config = config;
550
            this.env = env;
551
            //this.declaredVariables = declaredVariables;
552
        }
553

    
554
        /// <summary>
555
        /// Get a list of external variables used by the expression. This will include both variables that were explicitly
556
        /// declared to the <c>XPathCompiler</c>, and (if the <c>AllowUndeclaredVariables</c> option was set) variables that
557
        /// are referenced within the expression but not explicitly declared.
558
        /// </summary>
559
        /// <returns>
560
        /// An IEnumerator over the names of the external variables, as instances of <c>QName</c>.</returns>
561
        
562
        public IEnumerator EnumerateExternalVariables()
563
        {
564
            ArrayList list = new ArrayList();
565
            JIterator iter = env.iterateExternalVariables();
566
            while (iter.hasNext())
567
            {
568
                JXPathVariable var = (JXPathVariable)iter.next();
569
                JStructuredQName q = var.getVariableQName();
570
                list.Add(new QName(q.getPrefix(), q.getURI(), q.getLocalPart()));
571
            }
572
            return list.GetEnumerator();
573
        }
574

    
575
        
576
        /// <summary>
577
        /// Get the required cardinality of a declared variable in the static context of the expression.
578
        /// The occurrence indicator, one of '?' (zero-or-one), 
579
        /// '*' (zero-or-more), '+' (one-or-more), ' ' (a single space) (exactly one),
580
        /// or 'º' (masculine ordinal indicator, xBA) (exactly zero). The type empty-sequence()
581
        /// can be represented by an occurrence indicator of 'º' with any item type.
582
        /// If the variable was explicitly declared, this will be the occurrence indicator that was set when the
583
        /// variable was declared. If no item type was set, it will be {@link OccurrenceIndicator#ZERO_OR_MORE}.
584
        /// If the variable was implicitly declared by reference (which can happen only when the
585
        /// allowUndeclaredVariables option is set), the returned type will be
586
        /// {@link OccurrenceIndicator#ZERO_OR_MORE}.
587
        /// If no variable with the specified QName has been declared either explicitly or implicitly,
588
        /// the method returns 0.
589
        /// </summary>
590
        /// <param name="variableName">the name of a declared variable</param>
591
        /// <returns>the required cardinality.</returns>
592
 
593

    
594
        public char GetRequiredCardinalityForVariable(QName variableName)
595
        {
596
            JXPathVariable var = env.getExternalVariable(variableName.ToStructuredQName());
597
            if (var == null)
598
            {
599
                return '0';
600
            }
601
            else
602
            {
603
                return GetOccurrenceIndicator(var.getRequiredType().getCardinality());
604
            }
605
        }
606

    
607

    
608
        //internal method
609

    
610
        internal char GetOccurrenceIndicator(int occurrenceIndicator)
611
        {
612
          
613
            
614
                switch (occurrenceIndicator)
615
                {
616
                    case JStaticProperty.ALLOWS_ZERO_OR_MORE:
617

    
618
                        return XdmSequenceType.ZERO_OR_MORE;
619

    
620
                    case JStaticProperty.ALLOWS_ONE_OR_MORE:
621

    
622
                        return XdmSequenceType.ONE_OR_MORE;
623

    
624
                    case JStaticProperty.ALLOWS_ZERO_OR_ONE:
625

    
626
                        return XdmSequenceType.ZERO_OR_ONE;
627

    
628
                    case JStaticProperty.EXACTLY_ONE:
629

    
630
                        return XdmSequenceType.ONE;
631

    
632
                    case JStaticProperty.ALLOWS_ZERO:
633

    
634
                        return XdmSequenceType.ZERO;
635

    
636
                    default:
637
                        throw new ArgumentException("Unknown occurrence indicator");
638
                }
639
            
640
        }
641

    
642

    
643
        /// <summary>
644
        /// Load the compiled XPath expression to prepare it for execution.
645
        /// </summary>
646
        /// <returns>
647
        /// An <c>XPathSelector</c>. The returned <c>XPathSelector</c> can be used to
648
        /// set up the dynamic context, and then to evaluate the expression.
649
        /// </returns>
650

    
651
        public XPathSelector Load()
652
        {
653
            ArrayList declaredVariables = new ArrayList();
654
            JIterator iter = env.iterateExternalVariables();
655
            while (iter.hasNext())
656
            {
657
                JXPathVariable var = (JXPathVariable)iter.next();
658
                declaredVariables.Add(var);
659
            }
660
            return new XPathSelector(exp, config, declaredVariables);
661
        }
662
    }
663

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

    
676
    [Serializable]
677
    public class XPathSelector : IEnumerable
678
    {
679

    
680
        private JXPathExpression exp;
681
        private JConfiguration config;
682
        private JXPathDynamicContext dynamicContext;
683
        //private JIndependentContext env;
684
        private ArrayList declaredVariables; // a list of XPathVariable objects
685

    
686
        // internal constructor
687

    
688
        internal XPathSelector(JXPathExpression exp, JConfiguration config,
689
            ArrayList declaredVariables)
690
        {
691
            this.exp = exp;
692
            this.config = config;
693
            //this.env = env;
694
            this.declaredVariables = declaredVariables;
695
            this.dynamicContext = exp.createDynamicContext(null);
696
        }
697

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

    
706
        public XdmItem ContextItem
707
        {
708
            get { return (XdmItem)XdmValue.Wrap(dynamicContext.getContextItem()); }
709
			set {
710
				if (value == null) {
711
					throw new ArgumentException("contextItem is null");
712
				}
713
				if (exp.getInternalExpression ().getContainer ().getPackageData ().isSchemaAware ()) {
714
					JItem it = ((XdmItem)value).Unwrap().head();
715
					if (it is JNodeInfo && (((JNodeInfo)it).getDocumentRoot().isTyped())) {
716
						throw new ArgumentException(
717
							"The supplied node has been schema-validated, but the XPath expression was compiled without schema-awareness");	
718

    
719
					}
720

    
721
				}
722

    
723
				dynamicContext.setContextItem((JItem)value.Unwrap()); }
724
        }
725

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

    
735
        public void SetVariable(QName name, XdmValue value)
736
        {
737
            JXPathVariable var = null;
738
            String uri = (name.Uri == null ? "" : name.Uri);
739
            String local = name.LocalName;
740
            foreach (JXPathVariable v in declaredVariables)
741
            {
742
                String vuri = v.getVariableQName().getURI();
743
                if (vuri == null)
744
                {
745
                    vuri = "";
746
                }
747
                if (vuri == uri && v.getVariableQName().getLocalPart() == local)
748
                {
749
                    var = v;
750
                    break;
751
                }
752
            }
753
            if (var == null)
754
            {
755
                // TODO: this seems to conflict with the documentation of the method
756
                throw new ArgumentException("Variable has not been declared: " + name);
757
            }
758
            dynamicContext.setVariable(var, value.Unwrap());
759
        }
760

    
761
        /// <summary>
762
        /// The <code>XmlResolver</code> to be used at run-time to resolve and dereference URIs
763
        /// supplied to the <c>doc()</c> function.
764
        /// </summary>
765

    
766
        public XmlResolver InputXmlResolver
767
        {
768
            get
769
            {
770
                return ((DotNetURIResolver)dynamicContext.getURIResolver()).getXmlResolver();
771
            }
772
            set
773
            {
774
                dynamicContext.setURIResolver(new DotNetURIResolver(value));
775
            }
776
        }
777

    
778
        /// <summary>
779
        /// Evaluate the expression, returning the result as an <c>XdmValue</c> (that is,
780
        /// a sequence of nodes and/or atomic values).
781
        /// </summary>
782
        /// <remarks>
783
        /// Although a singleton result <i>may</i> be represented as an <c>XdmItem</c>, there is
784
        /// no guarantee that this will always be the case. If you know that the expression will return at
785
        /// most one node or atomic value, it is best to use the <c>EvaluateSingle</c> method, which 
786
        /// does guarantee that an <c>XdmItem</c> (or null) will be returned.
787
        /// </remarks>
788
        /// <returns>
789
        /// An <c>XdmValue</c> representing the results of the expression. 
790
        /// </returns>
791
        /// <exception cref="DynamicError">
792
        /// Throws <c>Saxon.Api.DynamicError</c> if the evaluation of the XPath expression fails
793
        /// with a dynamic error.
794
        /// </exception>
795

    
796
        public XdmValue Evaluate()
797
        {
798
            try {
799
                JSequence value = (JSequence)JSequenceExtent.makeSequenceExtent(
800
                    exp.iterate(dynamicContext));
801
                return XdmValue.Wrap(value);
802
            } catch (net.sf.saxon.trans.XPathException err) {
803
                throw new DynamicError(err);
804
            }
805
        }
806

    
807
        /// <summary>
808
        /// Evaluate the XPath expression, returning the result as an <c>XdmItem</c> (that is,
809
        /// a single node or atomic value).
810
        /// </summary>
811
        /// <returns>
812
        /// An <c>XdmItem</c> representing the result of the expression, or null if the expression
813
        /// returns an empty sequence. If the expression returns a sequence of more than one item,
814
        /// any items after the first are ignored.
815
        /// </returns>
816
        /// <exception cref="DynamicError">
817
        /// Throws <c>Saxon.Api.DynamicError</c> if the evaluation of the XPath expression fails
818
        /// with a dynamic error.
819
        /// </exception>
820

    
821

    
822
        public XdmItem EvaluateSingle()
823
        {
824
            try
825
            {
826
                net.sf.saxon.om.Item i = exp.evaluateSingle(dynamicContext);
827
                if (i == null)
828
                {
829
                    return null;
830
                }
831
                return (XdmItem)XdmValue.Wrap(i);
832
            } catch (net.sf.saxon.trans.XPathException err) {
833
                throw new DynamicError(err);
834
            }
835
        }
836
        
837
        /// <summary>
838
        /// Evaluate the effective boolean value of the XPath expression, returning the result as a <c>Boolean</c>
839
        /// </summary>
840
        /// <returns>
841
        /// A <c>Boolean</c> representing the result of the expression, converted to its
842
        /// effective boolean value as if by applying the XPath boolean() function
843
        /// </returns>
844
        /// <exception cref="DynamicError">
845
        /// Throws <c>Saxon.Api.DynamicError</c> if the evaluation of the XPath expression fails
846
        /// with a dynamic error.
847
        /// </exception>
848

    
849

    
850
        public Boolean EffectiveBooleanValue()
851
        {
852
            try
853
            {
854
                return exp.effectiveBooleanValue(dynamicContext);
855
            } catch (net.sf.saxon.trans.XPathException err) {
856
                throw new DynamicError(err);
857
            }
858
        }
859
        
860

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

    
878
        public IEnumerator GetEnumerator()
879
        {
880
            try {
881
                return new SequenceEnumerator(exp.iterate(dynamicContext));
882
            } catch (net.sf.saxon.trans.XPathException err) {
883
                throw new DynamicError(err);
884
            }
885
        }
886

    
887
    }
888

    
889
}
890

    
891
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
892
// Copyright (c) 2013 Saxonica Limited.
893
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
894
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
895
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
896
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
(11-11/13)