Project

Profile

Help

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

he / tags / 9.4.0.7 / hen / csource / api / Saxon.Api / XPath.cs @ b0b00ab2

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 JValueRepresentation = net.sf.saxon.om.ValueRepresentation;
11
using JStructuredQName = net.sf.saxon.om.StructuredQName;
12
using JValue = net.sf.saxon.value.Value;
13
using JIndependentContext = net.sf.saxon.sxpath.IndependentContext;
14
using JDedicatedStaticContext = net.sf.saxon.sxpath.DedicatedStaticContext;
15
using JXPathExpression = net.sf.saxon.sxpath.XPathExpression;
16
using JExpression = net.sf.saxon.expr.Expression;
17
using JXPathContext = net.sf.saxon.expr.XPathContext;
18
using JXPathDynamicContext = net.sf.saxon.sxpath.XPathDynamicContext;
19
using JXPathVariable = net.sf.saxon.sxpath.XPathVariable;
20
using JDecimalValue = net.sf.saxon.value.DecimalValue;
21
using net.sf.saxon.dotnet;
22

    
23

    
24
namespace Saxon.Api
25
{
26

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

    
49
    [Serializable]
50
    public class XPathCompiler
51
    {
52

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

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

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

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

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

    
84

    
85

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

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

    
104
        public string GetNamespaceURI(string lexicalName, bool useDefault) 
105
        {
106
            net.sf.saxon.om.NameChecker checker = net.sf.saxon.om.Name11Checker.getInstance();
107
            String[] parts = checker.getQNameParts(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.decimalSeparator = checkSingleChar(value);
246
            }
247
            else if (property.Equals("grouping-separator"))
248
            {
249
                symbols.groupingSeparator = checkSingleChar(value);
250
            }
251
            else if (property.Equals("infinity"))
252
            {
253
                symbols.infinity = value;
254
            }
255
            else if (property.Equals("NaN"))
256
            {
257
                symbols.NaN = value;
258
            }
259
            else if (property.Equals("minus-sign"))
260
            {
261
                symbols.minusSign = checkSingleChar(value);
262
            }
263
            else if (property.Equals("percent"))
264
            {
265
                symbols.percent = checkSingleChar(value);
266
            }
267
            else if (property.Equals("per-mille"))
268
            {
269
                symbols.permill = checkSingleChar(value);
270
            }
271
            else if (property.Equals("zero-digit"))
272
            {
273
                symbols.zeroDigit = checkSingleChar(value);
274
                if (!symbols.isValidZeroDigit()) {
275
                    throw new ArgumentException("Value supplied for zero-digit is not a Unicode digit representing zero");
276
                } 
277
            }
278
            else if (property.Equals("digit"))
279
            {
280
                symbols.digit = checkSingleChar(value);
281
            }
282
            else if (property.Equals("pattern-separator"))
283
            {
284
                symbols.patternSeparator = checkSingleChar(value);
285
            }
286
            else
287
            {
288
                throw new ArgumentException("Unknown decimal format property " + property);
289
            }
290

    
291
            try
292
            {
293
                if (format == null)
294
                {
295
                    dfm.setDefaultDecimalFormat(symbols, 0);
296
                }
297
                else
298
                {
299
                    dfm.setNamedDecimalFormat(net.sf.saxon.om.StructuredQName.fromClarkName(format.ClarkName), symbols, 0);
300
                }
301
            }
302
            catch (net.sf.saxon.trans.XPathException e)
303
            {
304
                throw new DynamicError(e);
305
            }
306
        }
307

    
308
        /// <summary>
309
        /// Declare a variable for use by the XPath expression. If the expression
310
        /// refers to any variables, then they must be declared here, unless the
311
        /// <c>AllowUndeclaredVariables</c> property has been set to true.
312
        /// </summary>
313
        /// <param name="name">The name of the variable, as a <c>QName</c></param>
314

    
315

    
316
        public void DeclareVariable(QName name)
317
        {
318
            if (cache != null)
319
            {
320
                cache.Clear();
321
            }
322
            JXPathVariable var = env.declareVariable(name.ToQNameValue());
323
            //declaredVariables.Add(var);
324
        }
325

    
326
        /// <summary>
327
        /// This property indicates which version of XPath language syntax is accepted. The default
328
        /// value is "1.0". This property must be set to "3.0" before compiling a query that
329
        /// uses XPath 3.0 (formerly known as XPath 2.1) syntax.
330
        /// </summary>
331
        /// <remarks>
332
        /// <para>Support for XPath 3.0 is currently limited: for details see the Saxon documentation.</para>
333
        /// <para><i>Property added in Saxon 9.4</i></para></remarks>
334

    
335

    
336
        public string XPathLanguageVersion
337
        {
338
            get { return env.getXPathLanguageLevel().toString(); }
339
            set { env.setXPathLanguageLevel((JDecimalValue)JDecimalValue.makeDecimalValue(value.ToString(), true)); }
340
        }
341

    
342

    
343
        /// <summary>
344
        /// The required context item type for the expression. This is used for
345
        /// optimizing the expression at compile time, and to check at run-time
346
        /// that the value supplied for the context item is the correct type.
347
        /// </summary>
348

    
349
        public XdmItemType ContextItemType
350
        {
351
            get { return XdmItemType.MakeXdmItemType(env.getRequiredContextItemType()); }
352
            set { env.setRequiredContextItemType(value.Unwrap()); }
353
        }
354

    
355

    
356
        /// <summary>
357
        /// The base URI of the expression, which forms part of the static context
358
        /// of the expression. This is used for resolving any relative URIs appearing
359
        /// within the expression, for example in references to library modules, schema
360
        /// locations, or as an argument to the <c>doc()</c> function.
361
        /// </summary>
362

    
363
        public String BaseUri
364
        {
365
            get { return env.getBaseURI(); }
366
            set {
367
                if (cache != null)
368
                {
369
                    cache.Clear();
370
                }
371
                env.setBaseURI(value); 
372
            }
373
        }
374

    
375
        /// <summary>
376
        /// XPath 1.0 Backwards Compatibility Mode. If true, backwards compatibility mode
377
        /// is set. In backwards compatibility mode, more implicit type conversions are
378
        /// allowed in XPath expressions, for example it is possible to compare a number
379
        /// with a string. The default is false (backwards compatibility mode is off).
380
        /// </summary>
381

    
382

    
383
        public Boolean BackwardsCompatible
384
        {
385
            get { return env.isInBackwardsCompatibleMode(); }
386
            set {
387
                if (cache != null)
388
                {
389
                    cache.Clear();
390
                }
391
                env.setBackwardsCompatibilityMode(value);
392
            }
393
        }
394

    
395
        /// <summary>
396
        /// XPath 1.0 Backwards Compatibility Mode. If true, backwards compatibility mode
397
        /// is set. In backwards compatibility mode, more implicit type conversions are
398
        /// allowed in XPath expressions, for example it is possible to compare a number
399
        /// with a string. The default is false (backwards compatibility mode is off).
400
        /// </summary>
401

    
402

    
403
        public Boolean Caching
404
        {
405
            get { 
406
                return cache != null; 
407
            }
408
            set
409
            {
410
                cache = (value ? new Hashtable() : null);
411
            }
412
        }
413

    
414

    
415

    
416
        /// <summary>
417
        /// Compile an expression supplied as a String.
418
        /// </summary>
419
        /// <example>
420
        /// <code>
421
        /// XPathExecutable q = compiler.Compile("distinct-values(//*/node-name()");
422
        /// </code>
423
        /// </example>
424
        /// <param name="source">A string containing the source text of the XPath expression</param>
425
        /// <returns>An <c>XPathExecutable</c> which represents the compiled xpath expression object.
426
        /// The XPathExecutable may be run as many times as required, in the same or a different
427
        /// thread. The <c>XPathExecutable</c> is not affected by any changes made to the <c>XPathCompiler</c>
428
        /// once it has been compiled.</returns>
429
        /// <exception cref="StaticError">
430
        /// Throws a <c>Saxon.Api.StaticError</c> if there is any static error in the XPath expression.
431
        /// This includes both syntax errors, semantic errors such as references to undeclared functions or
432
        /// variables, and statically-detected type errors.
433
        /// </exception>
434

    
435
        public XPathExecutable Compile(String source)
436
        {
437
            if (cache != null)
438
            {
439
                XPathExecutable exec = (XPathExecutable)cache[source];
440
                if (exec != null)
441
                {
442
                    return exec;
443
                }
444
            }
445
            try
446
            {
447
                JIndependentContext ic = env;
448
                if (ic.isAllowUndeclaredVariables())
449
                {
450
                    // self-declaring variables modify the static context. The XPathCompiler must not change state
451
                    // as the result of compiling an expression, so we need to copy the static context.
452
                    ic = new JDedicatedStaticContext(env);
453
                    for (JIterator iter = env.iterateExternalVariables(); iter.hasNext(); )
454
                    {
455
                        JXPathVariable var = (JXPathVariable)iter.next();
456
                        JXPathVariable var2 = ic.declareVariable(var.getVariableQName());
457
                    }
458
                }
459
                JXPathEvaluator eval = new JXPathEvaluator(config);
460
                eval.setStaticContext(ic);
461
                JXPathExpression cexp = eval.createExpression(source);
462
                XPathExecutable exec = new XPathExecutable(cexp, config, ic);
463
                if (cache != null)
464
                {
465
                    cache[source] = exec;
466
                }
467
                return exec;
468
            }
469
            catch (net.sf.saxon.trans.XPathException err)
470
            {
471
                throw new StaticError(err);
472
            }
473
        }
474

    
475
        /// <summary>
476
        /// Compile and execute an expression supplied as a String, with a given context item.
477
        /// </summary>
478
        /// <param name="expression">A string containing the source text of the XPath expression</param>
479
        /// <param name="contextItem">The context item to be used for evaluation of the XPath expression.
480
        /// May be null, in which case the expression is evaluated without any context item.</param>
481
        /// <returns>An <c>XdmValue</c> which is the result of evaluating the XPath expression.</returns>
482
        /// <exception cref="StaticError">
483
        /// Throws a <c>Saxon.Api.StaticError</c> if there is any static error in the XPath expression.
484
        /// This includes both syntax errors, semantic errors such as references to undeclared functions or
485
        /// variables, and statically-detected type errors.
486
        /// </exception>
487
        /// <exception cref="DynamicError">
488
        /// Throws a <c>Saxon.Api.DynamicError</c> if there is any dynamic error during evaluation of the XPath expression.
489
        /// This includes, for example, referring to the context item if no context item was supplied.
490
        /// </exception>
491

    
492
        public XdmValue Evaluate(String expression, XdmItem contextItem)
493
        {
494
            XPathSelector xs = Compile(expression).Load();
495
            if (contextItem != null)
496
            {
497
                xs.ContextItem = contextItem;
498
            }
499
            return xs.Evaluate();
500
        }
501

    
502
        /// <summary>
503
        /// Compile and execute an expression supplied as a String, with a given context item, where
504
        /// the expression is expected to return a single item as its result
505
        /// </summary>
506
        /// <param name="expression">A string containing the source text of the XPath expression</param>
507
        /// <param name="contextItem">The context item to be used for evaluation of the XPath expression.
508
        /// May be null, in which case the expression is evaluated without any context item.</param>
509
        /// <returns>If the XPath expression returns a singleton, then the the <c>XdmItem</c> 
510
        /// which is the result of evaluating the XPath expression. If the expression returns an empty sequence,
511
        /// then null. If the expression returns a sequence containing more than one item, then the first
512
        /// item in the result.</returns>
513
        /// <exception cref="StaticError">
514
        /// Throws a <c>Saxon.Api.StaticError</c> if there is any static error in the XPath expression.
515
        /// This includes both syntax errors, semantic errors such as references to undeclared functions or
516
        /// variables, and statically-detected type errors.
517
        /// </exception>
518
        /// <exception cref="DynamicError">
519
        /// Throws a <c>Saxon.Api.DynamicError</c> if there is any dynamic error during evaluation of the XPath expression.
520
        /// This includes, for example, referring to the context item if no context item was supplied.
521
        /// </exception>
522

    
523
        public XdmItem EvaluateSingle(String expression, XdmItem contextItem)
524
        {
525
            XPathSelector xs = Compile(expression).Load();
526
            if (contextItem != null)
527
            {
528
                xs.ContextItem = contextItem;
529
            }
530
            return xs.EvaluateSingle();
531
        }
532

    
533
    }
534

    
535
    /// <summary>
536
    /// An <c>XPathExecutable</c> represents the compiled form of an XPath expression. 
537
    /// To evaluate the expression,
538
    /// it must first be loaded to form an <c>XPathSelector</c>.
539
    /// </summary>
540
    /// <remarks>
541
    /// <para>An <c>XPathExecutable</c> is immutable, and therefore thread-safe. It is simplest to
542
    /// load a new <c>XPathSelector</c> each time the expression is to be evaluated. However, the 
543
    /// <c>XPathSelector</c> is serially reusable within a single thread.</para>
544
    /// <para>An <c>XPathExecutable</c> is created by using one of the <c>Compile</c>
545
    /// methods on the <c>XPathCompiler</c> class.</para>
546
    /// </remarks>    
547

    
548
    [Serializable]
549
    public class XPathExecutable
550
    {
551

    
552
        private JXPathExpression exp;
553
        private JConfiguration config;
554
        private JIndependentContext env;
555
        //private ArrayList declaredVariables;
556

    
557
        // internal constructor
558

    
559
        internal XPathExecutable(JXPathExpression exp, JConfiguration config,
560
            JIndependentContext env /*, ArrayList declaredVariables*/)
561
        {
562
            this.exp = exp;
563
            this.config = config;
564
            this.env = env;
565
            //this.declaredVariables = declaredVariables;
566
        }
567

    
568
        /// <summary>
569
        /// Get a list of external variables used by the expression. This will include both variables that were explicitly
570
        /// declared to the <c>XPathCompiler</c>, and (if the <c>AllowUndeclaredVariables</c> option was set) variables that
571
        /// are referenced within the expression but not explicitly declared.
572
        /// </summary>
573
        /// <returns>
574
        /// An IEnumerator over the names of the external variables, as instances of <c>QName</c>.</returns>
575
        
576
        public IEnumerator EnumerateExternalVariables()
577
        {
578
            ArrayList list = new ArrayList();
579
            JIterator iter = env.iterateExternalVariables();
580
            while (iter.hasNext())
581
            {
582
                JXPathVariable var = (JXPathVariable)iter.next();
583
                JStructuredQName q = var.getVariableQName();
584
                list.Add(new QName(q.getPrefix(), q.getURI(), q.getLocalPart()));
585
            }
586
            return list.GetEnumerator();
587
        }
588

    
589

    
590
        /// <summary>
591
        /// Load the compiled XPath expression to prepare it for execution.
592
        /// </summary>
593
        /// <returns>
594
        /// An <c>XPathSelector</c>. The returned <c>XPathSelector</c> can be used to
595
        /// set up the dynamic context, and then to evaluate the expression.
596
        /// </returns>
597

    
598
        public XPathSelector Load()
599
        {
600
            ArrayList declaredVariables = new ArrayList();
601
            JIterator iter = env.iterateExternalVariables();
602
            while (iter.hasNext())
603
            {
604
                JXPathVariable var = (JXPathVariable)iter.next();
605
                declaredVariables.Add(var);
606
            }
607
            return new XPathSelector(exp, config, declaredVariables);
608
        }
609
    }
610

    
611
    /// <summary inherits="IEnumerable">
612
    /// An <c>XPathSelector</c> represents a compiled and loaded XPath expression ready for execution.
613
    /// The <c>XPathSelector</c> holds details of the dynamic evaluation context for the XPath expression.
614
    /// </summary>
615
    /// <remarks>
616
    /// <para>An <c>XPathSelector</c> should not be used concurrently in multiple threads. It is safe,
617
    /// however, to reuse the object within a single thread to evaluate the same XPath expression several times.
618
    /// Evaluating the expression does not change the context that has been established.</para>
619
    /// <para>An <c>XPathSelector</c> is always constructed by running the <c>Load</c> method of
620
    /// an <c>XPathExecutable</c>.</para>
621
    /// </remarks>     
622

    
623
    [Serializable]
624
    public class XPathSelector : IEnumerable
625
    {
626

    
627
        private JXPathExpression exp;
628
        private JConfiguration config;
629
        private JXPathDynamicContext dynamicContext;
630
        //private JIndependentContext env;
631
        private ArrayList declaredVariables; // a list of XPathVariable objects
632

    
633
        // internal constructor
634

    
635
        internal XPathSelector(JXPathExpression exp, JConfiguration config,
636
            ArrayList declaredVariables)
637
        {
638
            this.exp = exp;
639
            this.config = config;
640
            //this.env = env;
641
            this.declaredVariables = declaredVariables;
642
            this.dynamicContext = exp.createDynamicContext(null);
643
        }
644

    
645
        /// <summary>
646
        /// The context item for the XPath expression evaluation.
647
        /// </summary>
648
        /// <remarks> This may be either a node or an atomic
649
        /// value. Most commonly it will be a document node, which might be constructed
650
        /// using the <c>Build</c> method of the <c>DocumentBuilder</c> object.
651
        /// </remarks>
652

    
653
        public XdmItem ContextItem
654
        {
655
            get { return (XdmItem)XdmValue.Wrap(dynamicContext.getContextItem()); }
656
            set { dynamicContext.setContextItem((JItem)value.Unwrap()); }
657
        }
658

    
659
        /// <summary>
660
        /// Set the value of a variable
661
        /// </summary>
662
        /// <param name="name">The name of the variable. This must match the name of a variable
663
        /// that was declared to the XPathCompiler. No error occurs if the expression does not
664
        /// actually reference a variable with this name.</param>
665
        /// <param name="value">The value to be given to the variable.</param>
666
        
667

    
668
        public void SetVariable(QName name, XdmValue value)
669
        {
670
            JXPathVariable var = null;
671
            String uri = (name.Uri == null ? "" : name.Uri);
672
            String local = name.LocalName;
673
            foreach (JXPathVariable v in declaredVariables)
674
            {
675
                String vuri = v.getVariableQName().getURI();
676
                if (vuri == null)
677
                {
678
                    vuri = "";
679
                }
680
                if (vuri == uri && v.getVariableQName().getLocalPart() == local)
681
                {
682
                    var = v;
683
                    break;
684
                }
685
            }
686
            if (var == null)
687
            {
688
                // TODO: this seems to conflict with the documentation of the method
689
                throw new ArgumentException("Variable has not been declared: " + name);
690
            }
691
            dynamicContext.setVariable(var, value.Unwrap());
692
        }
693

    
694
        /// <summary>
695
        /// The <code>XmlResolver</code> to be used at run-time to resolve and dereference URIs
696
        /// supplied to the <c>doc()</c> function.
697
        /// </summary>
698

    
699
        public XmlResolver InputXmlResolver
700
        {
701
            get
702
            {
703
                return ((DotNetURIResolver)dynamicContext.getURIResolver()).getXmlResolver();
704
            }
705
            set
706
            {
707
                dynamicContext.setURIResolver(new DotNetURIResolver(value));
708
            }
709
        }
710

    
711
        /// <summary>
712
        /// Evaluate the expression, returning the result as an <c>XdmValue</c> (that is,
713
        /// a sequence of nodes and/or atomic values).
714
        /// </summary>
715
        /// <remarks>
716
        /// Although a singleton result <i>may</i> be represented as an <c>XdmItem</c>, there is
717
        /// no guarantee that this will always be the case. If you know that the expression will return at
718
        /// most one node or atomic value, it is best to use the <c>EvaluateSingle</c> method, which 
719
        /// does guarantee that an <c>XdmItem</c> (or null) will be returned.
720
        /// </remarks>
721
        /// <returns>
722
        /// An <c>XdmValue</c> representing the results of the expression. 
723
        /// </returns>
724
        /// <exception cref="DynamicError">
725
        /// Throws <c>Saxon.Api.DynamicError</c> if the evaluation of the XPath expression fails
726
        /// with a dynamic error.
727
        /// </exception>
728

    
729
        public XdmValue Evaluate()
730
        {
731
            try {
732
                JValueRepresentation value = JSequenceExtent.makeSequenceExtent(
733
                    exp.iterate(dynamicContext));
734
                return XdmValue.Wrap(value);
735
            } catch (net.sf.saxon.trans.XPathException err) {
736
                throw new DynamicError(err);
737
            }
738
        }
739

    
740
        /// <summary>
741
        /// Evaluate the XPath expression, returning the result as an <c>XdmItem</c> (that is,
742
        /// a single node or atomic value).
743
        /// </summary>
744
        /// <returns>
745
        /// An <c>XdmItem</c> representing the result of the expression, or null if the expression
746
        /// returns an empty sequence. If the expression returns a sequence of more than one item,
747
        /// any items after the first are ignored.
748
        /// </returns>
749
        /// <exception cref="DynamicError">
750
        /// Throws <c>Saxon.Api.DynamicError</c> if the evaluation of the XPath expression fails
751
        /// with a dynamic error.
752
        /// </exception>
753

    
754

    
755
        public XdmItem EvaluateSingle()
756
        {
757
            try
758
            {
759
                net.sf.saxon.om.Item i = exp.evaluateSingle(dynamicContext);
760
                if (i == null)
761
                {
762
                    return null;
763
                }
764
                return (XdmItem)XdmValue.Wrap(i);
765
            } catch (net.sf.saxon.trans.XPathException err) {
766
                throw new DynamicError(err);
767
            }
768
        }
769

    
770
        /// <summary>
771
        /// Evaluate the expression, returning the result as an <c>IEnumerator</c> (that is,
772
        /// an enumerator over a sequence of nodes and/or atomic values).
773
        /// </summary>
774
        /// <returns>
775
        /// An enumerator over the sequence that represents the results of the expression.
776
        /// Each object in this sequence will be an instance of <c>XdmItem</c>. Note
777
        /// that the expression may be evaluated lazily, which means that a successful response
778
        /// from this method does not imply that the expression has executed successfully: failures
779
        /// may be reported later while retrieving items from the iterator. 
780
        /// </returns>
781
        /// <exception cref="DynamicError">
782
        /// May throw a <c>Saxon.Api.DynamicError</c> if the evaluation of the XPath expression fails
783
        /// with a dynamic error. However, some errors will not be detected during the invocation of this
784
        /// method, but only when stepping through the returned <c>SequenceEnumerator</c>.
785
        /// </exception>
786

    
787
        public IEnumerator GetEnumerator()
788
        {
789
            try {
790
                return new SequenceEnumerator(exp.iterate(dynamicContext));
791
            } catch (net.sf.saxon.trans.XPathException err) {
792
                throw new DynamicError(err);
793
            }
794
        }
795

    
796
    }
797

    
798
}
799

    
800
//
801
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
802
// you may not use this file except in compliance with the License. You may obtain a copy of the
803
// License at http://www.mozilla.org/MPL/
804
//
805
// Software distributed under the License is distributed on an "AS IS" basis,
806
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
807
// See the License for the specific language governing rights and limitations under the License.
808
//
809
// The Original Code is: all this file.
810
//
811
// The Initial Developer of the Original Code is Michael H. Kay.
812
//
813
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
814
//
815
// Contributor(s): none.
816
//
(10-10/12)