Project

Profile

Help

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

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

1
using System;
2
using System.IO;
3
using System.Xml;
4
using System.Collections;
5
using JConfiguration = net.sf.saxon.Configuration;
6
using JNamePool = net.sf.saxon.om.NamePool;
7
using JAtomicValue = net.sf.saxon.value.AtomicValue;
8
using JFunctionItem = net.sf.saxon.om.FunctionItem;
9
using JItem = net.sf.saxon.om.Item;
10
using JSingletonItem = net.sf.saxon.value.SingletonItem;
11
using JEmptySequence = net.sf.saxon.value.EmptySequence;
12
using JSequenceExtent = net.sf.saxon.value.SequenceExtent;
13
using JConversionResult = net.sf.saxon.type.ConversionResult;
14
using JValidationFailure = net.sf.saxon.type.ValidationFailure;
15
using JSequenceIterator = net.sf.saxon.om.SequenceIterator;
16
using JStandardNames = net.sf.saxon.om.StandardNames;
17
using JStructuredQName = net.sf.saxon.om.StructuredQName;
18
using JXPathContext = net.sf.saxon.expr.XPathContext;
19
using DotNetReceiver = net.sf.saxon.dotnet.DotNetReceiver;
20
using DotNetObjectValue = net.sf.saxon.dotnet.DotNetObjectValue;
21
using JBigDecimal = java.math.BigDecimal;
22
using JArrayList = java.util.ArrayList;
23
using JCharSequence = java.lang.CharSequence;
24
using net.sf.saxon.lib;
25
using net.sf.saxon.om;
26
using net.sf.saxon.tree.iter;
27
using net.sf.saxon.value;
28
using net.sf.saxon.pattern;
29
using JAtomicType = net.sf.saxon.type.AtomicType;
30
using JSchemaType = net.sf.saxon.type.SchemaType;
31
using JType = net.sf.saxon.type.Type;
32
using JStringToDouble = net.sf.saxon.type.StringToDouble;
33

    
34

    
35
namespace Saxon.Api
36
{
37

    
38
    /// <summary>
39
    /// An value in the XDM data model. A value is a sequence of zero or more
40
    /// items, each item being either an atomic value or a node.
41
    /// </summary>
42
    /// <remarks>
43
    /// <para>An <c>XdmValue</c> is immutable.</para>
44
    /// <para>A sequence consisting of a single item <i>may</i> be represented
45
    /// as an instance of <c>XdmItem</c>, which is a subtype of <c>XdmValue</c>. However,
46
    /// there is no guarantee that all single-item sequences will be instances of
47
    /// <c>XdmItem</c>: if you want to ensure this, use the <c>Simplify</c> property.</para>
48
    /// <para>There are various ways of creating an <c>XdmValue</c>. To create an atomic
49
    /// value, use one of the constructors on <c>XdmAtomicValue</c> (which is a subtype of <c>XdmValue</c>).
50
    /// To construct an <c>XdmNode</c> (another subtype) by parsing an XML document, or by wrapping a DOM document,
51
    /// use a <c>DocumentBuilder</c>. To create a sequence of values, use the <c>Append</c>
52
    /// method on this class to form a list from individual items or sublists.</para>
53
    /// <para>An <c>dmValue</c> is also returned as the result of evaluating a query
54
    /// using the XQuery and XPath interfaces.</para>
55
    /// <para>The subtype <c>XdmEmptySequence</c> represents an empty sequence: an
56
    /// <c>XdmValue</c> of length zero. Again, there is no guarantee that every empty sequence
57
    /// will be represented as an instance of <c>XdmEmptySequence</c>, unless you use
58
    /// the <c>Simplify</c> property.</para>
59
    /// </remarks>
60

    
61
    [Serializable]
62
    public class XdmValue : IEnumerable
63
    {
64

    
65
        internal ValueRepresentation value;
66

    
67
        /// <summary>
68
        /// Internal constructor
69
        /// </summary>
70

    
71
        internal XdmValue() { }
72

    
73
        /// <summary>
74
        /// Create a value from a collection of items
75
        /// </summary>
76
        /// <param name="items">An enumerable collection providing the items to make up the sequence. Every
77
        /// member of this collection must be an instance of <c>XdmItem</c>
78
        /// </param>
79

    
80
        public XdmValue(IEnumerable items)
81
        {
82
            JArrayList list = new JArrayList();
83
            foreach (XdmItem c in items)
84
            {
85
                list.add((Item)c.Unwrap());
86
            }
87
            value = new SequenceExtent(list);
88
        }
89

    
90
        /// <summary>
91
        /// Create a new XdmValue by concatenating the sequences of items in this XdmValue and another XdmValue
92
        /// </summary>
93
        /// <remarks>
94
        /// Neither of the input XdmValue objects is modified by this operation
95
        /// </remarks>
96
        /// <param name="otherValue">
97
        /// The other XdmValue, whose items are to be appended to the items from this XdmValue
98
        /// </param>
99
        
100
        public XdmValue Append(XdmValue otherValue) {
101
            JArrayList list = new JArrayList();
102
            foreach (XdmItem item in this) {
103
                list.add(item.Unwrap());
104
            }
105
            foreach (XdmItem item in otherValue) {
106
                list.add(item.Unwrap());
107
            }
108
            return XdmValue.Wrap(new SequenceExtent(list));
109
        }
110

    
111

    
112
        /// <summary>
113
        /// Create an XdmValue from an underlying Saxon ValueRepresentation object.
114
        /// This method is provided for the benefit of applications that need to mix
115
        /// use of the Saxon .NET API with direct use of the underlying objects
116
        /// and methods offered by the Java implementation.
117
        /// </summary>
118
        /// <param name="value">An object representing an XDM value in the
119
        /// underlying Saxon implementation. If the parameter is null,
120
        /// the method returns null.</param>
121
        /// <returns>An XdmValue that wraps the underlying Saxon value
122
        /// representation.</returns>
123

    
124
        public static XdmValue Wrap(ValueRepresentation value)
125
        {
126
            XdmValue result;
127
            if (value == null)
128
            {
129
                return null;
130
            }
131
            if (value is JEmptySequence)
132
            {
133
                return XdmEmptySequence.INSTANCE;
134
            }
135
            else if (value is JAtomicValue)
136
            {
137
                result = new XdmAtomicValue();
138
            }
139
            else if (value is NodeInfo)
140
            {
141
                result = new XdmNode();
142
            }
143
            else if (value is JSingletonItem)
144
            {
145
                value = ((JSingletonItem)value).asItem();
146
                result = new XdmNode();
147
            }
148
            else
149
            {
150
                result = new XdmValue();
151
            }
152
            result.value = value;
153
            return result;
154
        }
155

    
156
        /// <summary>
157
        /// Extract the underlying Saxon ValueRepresentation object from an XdmValue.
158
        /// This method is provided for the benefit of applications that need to mix
159
        /// use of the Saxon .NET API with direct use of the underlying objects
160
        /// and methods offered by the Java implementation.
161
        /// </summary>
162
        /// <returns>An object representing the XDM value in the
163
        /// underlying Saxon implementation.</returns>
164

    
165

    
166
        public ValueRepresentation Unwrap()
167
        {
168
            return value;
169
        }
170

    
171
        /// <summary>
172
        /// Get the sequence of items in the form of an <c>IList</c>
173
        /// </summary>
174
        /// <returns>
175
        /// The list of items making up this value. Each item in the list
176
        /// will be an object of type <c>XdmItem</c>
177
        /// </returns>        
178

    
179
        public IList GetList()
180
        {
181
            if (value == null)
182
            {
183
                return new ArrayList();
184
            }
185
            else if (value is Item)
186
            {
187
                ArrayList list = new ArrayList(1);
188
                list.Add((NodeInfo)value);
189
                return list;
190
            }
191
            else
192
            {
193
                ArrayList list = new ArrayList();
194
                SequenceIterator iter = ((Value)value).iterate();
195
                while (true)
196
                {
197
                    JItem jitem = iter.next();
198
                    if (jitem == null)
199
                    {
200
                        break;
201
                    }
202
                    list.Add((XdmItem)XdmValue.Wrap(jitem));
203
                }
204
                return list;
205
            }
206
        }
207

    
208
        /// <summary>
209
        /// Get the sequence of items in the form of an <c>IXdmEnumerator</c>
210
        /// </summary>
211
        /// <returns>
212
        /// An enumeration over the list of items making up this value. Each item in the list
213
        /// will be an object of type <c>XdmItem</c>
214
        /// </returns>    
215

    
216
        public IEnumerator GetEnumerator()
217
        {
218
            if (value == null)
219
            {
220
                return EmptyEnumerator.INSTANCE;
221
            }
222
            else if (value is Item)
223
            {
224
                return new SequenceEnumerator(SingletonIterator.makeIterator((Item)value));
225
            }
226
            else
227
            {
228
                return new SequenceEnumerator(((Value)value).iterate());
229
            }
230
        }
231

    
232
        /// <summary>
233
        /// Get the number of items in the sequence
234
        /// </summary>
235
        /// <returns>
236
        /// The number of items in the sequence
237
        /// </returns> 
238

    
239
        public int Count
240
        {
241
            get
242
            {
243
                if (value == null)
244
                {
245
                    return 0;
246
                }
247
                else if (value is Item)
248
                {
249
                    return 1;
250
                }
251
                else
252
                {
253
                    return ((Value)value).getLength();
254
                }
255
            }
256
        }
257

    
258
        /// <summary>
259
        /// Simplify a value: that is, reduce it to the simplest possible form.
260
        /// If the sequence is empty, the result will be an instance of <c>XdmEmptySequence</c>.
261
        /// If the sequence is a single node, the result will be an instance of <c>XdmNode</c>;
262
        /// if it is a single atomic value, it will be an instance of <c>XdmAtomicValue</c>.
263
        /// </summary>
264

    
265
        public XdmValue Simplify
266
        {
267
            get
268
            {
269
                switch (((Value)value).getLength())
270
                {
271
                    case 0:
272
                        if (this is XdmEmptySequence)
273
                        {
274
                            return this;
275
                        }
276
                        return XdmEmptySequence.INSTANCE;
277

    
278
                    case 1:
279
                        if (this is XdmItem)
280
                        {
281
                            return this;
282
                        }
283
                        return Wrap(Value.asItem(value));
284

    
285
                    default:
286
                        return this;
287
                }
288
            }
289
        }
290

    
291
    }
292

    
293
    /// <summary inherits="XdmValue">
294
    /// The class <c>XdmItem</c> represents an item in a sequence, as defined
295
    /// by the XDM data model. An item is either an atomic value or a node.
296
    /// </summary>
297
    /// <remarks>
298
    /// <para>An item is a member of a sequence, but it can also be considered as
299
    /// a sequence (of length one) in its own right. <c>XdmItem</c> is a subtype
300
    /// of <c>XdmValue</c> because every Item in the XDM data model is also a
301
    /// value.</para>
302
    /// <para>It cannot be assumed that every sequence of length one will be 
303
    /// represented by an <c>XdmItem</c>. It is quite possible for an <c>XdmValue</c>
304
    /// that is not an <c>XdmItem</c> to hold a singleton sequence.</para>
305
    /// </remarks> 
306

    
307
    [Serializable]
308
    public abstract class XdmItem : XdmValue
309
    {
310

    
311
        /// <summary>
312
        /// Determine whether the item is an atomic value
313
        /// </summary>
314
        /// <returns>
315
        /// true if the item is an atomic value, false if it is a Node
316
        /// </returns>
317

    
318
        public abstract bool IsAtomic();
319

    
320
    }
321

    
322
    /// <summary inherits="XdmItem">
323
    /// The class <c>XdmAtomicValue</c> represents an item in an XPath 2.0 sequence
324
    /// that is an atomic value. The value may belong to any of the 19 primitive types
325
    /// defined in XML Schema, or to a type derived from these primitive types, or to 
326
    /// the XPath 2.0 type <c>xs:untypedAtomic</c>
327
    /// </summary>
328
    /// <remarks>
329
    /// Note that there is no guarantee that every <c>XdmValue</c> comprising a single
330
    /// atomic value will be an instance of this class. To force this, use the <c>Simplify</c>
331
    /// property of the <c>XdmValue</c>.
332
    /// </remarks>
333

    
334
    [Serializable]
335
    public class XdmAtomicValue : XdmItem
336
    {
337

    
338
        //internal JAtomicValue atomicValue;
339

    
340
        internal XdmAtomicValue() { }
341

    
342
        /// <summary>
343
        /// Determine whether the item is an atomic value
344
        /// </summary>
345
        /// <returns>
346
        /// true (the item is an atomic value)
347
        /// </returns>
348

    
349
        public override bool IsAtomic()
350
        {
351
            return true;
352
        }
353

    
354
        /// <summary>
355
        /// Construct an atomic value of type <c>xs:string</c>
356
        /// </summary>
357
        /// <param name="str">The string value</param>
358

    
359
        public XdmAtomicValue(String str)
360
        {
361
            this.value = new StringValue(str);
362
        }
363

    
364
        /// <summary>
365
        /// Construct an atomic value of type <c>xs:integer</c>
366
        /// </summary>
367
        /// <param name="i">The integer value</param>
368

    
369
        public XdmAtomicValue(long i)
370
        {
371
            this.value = new Int64Value(i);
372
        }
373

    
374
        /// <summary>
375
        /// Construct an atomic value of type <c>xs:decimal</c>
376
        /// </summary>
377
        /// <param name="d">The decimal value</param>
378

    
379
        public XdmAtomicValue(decimal d)
380
        {
381
            this.value = new DecimalValue(new JBigDecimal(d.ToString()));
382
        }
383

    
384
        /// <summary>
385
        /// Construct an atomic value of type <c>xs:float</c>
386
        /// </summary>
387
        /// <param name="f">The float value</param>        
388

    
389
        public XdmAtomicValue(float f)
390
        {
391
            this.value = new FloatValue(f);
392
        }
393

    
394
        /// <summary>
395
        /// Construct an atomic value of type <c>xs:double</c>
396
        /// </summary>
397
        /// <param name="d">The double value</param>
398

    
399
        public XdmAtomicValue(double d)
400
        {
401
            this.value = new DoubleValue(d);
402
        }
403

    
404
        /// <summary>
405
        /// Construct an atomic value of type <c>xs:boolean</c>
406
        /// </summary>
407
        /// <param name="b">The boolean value</param>
408

    
409
        public XdmAtomicValue(bool b)
410
        {
411
            this.value = BooleanValue.get(b);
412
        }
413

    
414
        /// <summary>
415
        /// Construct an atomic value of type <c>xs:anyURI</c>
416
        /// </summary>
417
        /// <param name="u">The uri value</param>
418

    
419
        public XdmAtomicValue(Uri u)
420
        {
421
            this.value = new AnyURIValue(u.ToString());
422
        }
423

    
424
        /// <summary>
425
        /// Construct an atomic value of type <c>xs:QName</c>
426
        /// </summary>
427
        /// <param name="q">The QName value</param>                
428

    
429
        public XdmAtomicValue(QName q)
430
        {
431
            this.value = new QNameValue(
432
                q.Prefix, q.Uri, q.LocalName);
433
        }
434

    
435
        /// <summary>
436
        /// Construct an atomic value of a given built-in or user-defined type
437
        /// </summary>
438
        /// <example>
439
        ///   <code>AtomicValue("abcd", QName.XDT_UNTYPED_ATOMIC)</code>
440
        ///   <para>creates an untyped atomic value containing the string "abcd"</para>
441
        /// </example>
442
        /// <param name="lexicalForm">The string representation of the value (any value that is acceptable
443
        /// in the lexical space, as defined by XML Schema Part 2). Whitespace normalization as defined by
444
        /// the target type will be applied to the value.</param>
445
        /// <param name="type">The QName giving the name of the target type. This must be an atomic
446
        /// type, and it must not be a type that is namespace-sensitive (QName, NOTATION, or types derived
447
        /// from these). If the type is a user-defined type then its definition must be present
448
        /// in the schema cache maintained by the <c>SchemaManager</c>.</param> 
449
        /// <param name="processor">The <c>Processor</c> object. This is needed for looking up user-defined
450
        /// types, and also because some conversions are context-sensitive, for example they depend on the
451
        /// implicit timezone or the choice of XML 1.0 versus XML 1.1 for validating names.</param>
452
        /// <exception name="ArgumentException">Thrown if the type is unknown or unsuitable, or if the supplied string is not
453
        /// a valid lexical representation of a value of the given type.</exception>
454

    
455
        public XdmAtomicValue(String lexicalForm, QName type, Processor processor)
456
        {
457
            JConfiguration jconfig = processor.config;
458
            int fp = jconfig.getNamePool().getFingerprint(type.Uri, type.LocalName);
459
            if (fp == -1)
460
            {
461
                throw new ArgumentException("Unknown name " + type);
462
            }
463
            JSchemaType st = jconfig.getSchemaType(fp);
464
            if (st == null)
465
            {
466
                throw new ArgumentException("Unknown type " + type);
467
            }
468
            if (!(st is JAtomicType))
469
            {
470
                throw new ArgumentException("Specified type " + type + " is not atomic");
471
            }
472
            if (((JAtomicType)st).isNamespaceSensitive())
473
            {
474
                throw new ArgumentException("Specified type " + type + " is namespace-sensitive");
475
            }
476
            JConversionResult result = jconfig.getConversionRules()
477
                .getStringConverter((JAtomicType)st)
478
                .convertString((JCharSequence)lexicalForm);
479
 
480
            if (result is JValidationFailure)
481
            {
482
                throw new ArgumentException(((JValidationFailure)result).getMessage());
483
            }
484
            this.value = (JAtomicValue)result;
485
        }
486

    
487
        /// <summary>
488
        /// Create an atomic value that wraps an external object. Such values can be used
489
        /// in conjunction with extension functions.
490
        /// </summary>
491
        /// <remarks>
492
        /// <para>This method should not be used to create simple atomic values representing strings,
493
        /// numbers, booleans, and so on. For that purpose, use the relevant constructor.
494
        /// Wrapped external objects are used only when calling .NET native code external
495
        /// to a query or stylesheet.</para>
496
        /// <para>In releases prior to 9.2, this method also existed with the alternative spelling
497
        /// <code>wrapExternalObject</code> (lower-case "w"). This was retained for backwards compatibility,
498
        /// but caused problems for Visual Basic users, where it is not permitted to have two methods whose
499
        /// names differ only in case. Any applications using <code>wrapExternalObject</code> must
500
        /// therefore be changed to use <code>WrapExternalObject</code>. Apologies for the inconvenience.</para>
501
        /// </remarks>
502
        /// <param name="external">The object to be wrapped.</param>
503
        /// <returns>The wrapped object</returns>
504

    
505
        public static XdmAtomicValue WrapExternalObject(object external)
506
        {
507
            return (XdmAtomicValue)XdmValue.Wrap(new DotNetObjectValue(external));
508
        }
509

    
510

    
511
        /// <summary>
512
        /// Get the value converted to a boolean using the XPath casting rules
513
        /// </summary>
514
        /// <returns>the result of converting to a boolean (Note: this is not the same as the
515
        /// effective boolean value).</returns> 
516

    
517
        public bool GetBooleanValue()
518
        {
519
            JAtomicValue av = (JAtomicValue)this.value;
520
            if (av is BooleanValue) {
521
                return ((BooleanValue)av).getBooleanValue();
522
            } else if (av is NumericValue) {
523
                return !av.isNaN() && ((NumericValue)av).signum() != 0;
524
            } else if (av is StringValue) {
525
                String s = av.getStringValue().Trim();
526
                return "1".Equals(s) || "true".Equals(s);
527
            } else {
528
                throw new ArgumentException("Cannot cast item to a boolean");
529
            }
530
        }
531

    
532

    
533
        /// <summary>
534
        /// Get the value converted to a boolean using the XPath casting rules
535
        /// </summary>
536
        /// <returns>the result of converting to an integer</returns>
537

    
538
        public long GetLongValue()
539
        {
540
            AtomicValue av = (AtomicValue)this.Value;
541
            if (av is BooleanValue) {
542
                return ((BooleanValue)av).getBooleanValue() ? 0L : 1L;
543
            } else if (av is NumericValue) {
544
            try {
545
                return ((NumericValue)av).longValue();
546
            } catch (Exception) {
547
                throw new ArgumentException("Cannot cast item to an integer");
548
            }
549
            } else if (av is StringValue) {
550
                JStringToDouble converter = JStringToDouble.getInstance();
551
                return (long)converter.stringToNumber(av.getStringValueCS());
552
            } else {
553
                throw new ArgumentException("Cannot cast item to an integer");
554
            }
555
        }
556

    
557

    
558
        /// <summary>
559
        /// Get the value converted to a double using the XPath casting rules.
560
        /// <p>If the value is a string, the XSD 1.1 rules are used, which means that the string
561
        /// "+INF" is recognised.</p>
562
        /// </summary>
563
        /// <returns>the result of converting to a double</returns>
564

    
565
        public double GetDoubleValue()
566
        {
567
            AtomicValue av = (AtomicValue)this.value;
568
            if (av is BooleanValue) {
569
                return ((BooleanValue)av).getBooleanValue() ? 0.0 : 1.0;
570
            } else if (av is NumericValue) {
571
                return ((NumericValue)av).getDoubleValue();
572
            } else if (av is StringValue) {
573
            try {
574
                JStringToDouble converter = StringToDouble11.getInstance();
575
                return converter.stringToNumber(av.getStringValueCS());
576
            } catch (Exception e) {
577
                throw new ArgumentException(e.Message);
578
            }
579
            } else {
580
                throw new ArgumentException("Cannot cast item to a double");
581
            }
582
        }
583

    
584

    
585
         /// <summary>
586
        /// Get the value converted to a decimal using the XPath casting rules
587
        /// </summary>
588
        /// <returns>return the result of converting to a decimal</returns>
589

    
590
        public Decimal GetDecimalValue() 
591
        {
592
            AtomicValue av = (AtomicValue)this.value;
593
            if (av is BooleanValue) {
594
                return ((BooleanValue)av).getBooleanValue() ? 0  : 1;
595
            } else if (av is NumericValue) {
596
                try {
597
                    return Convert.ToDecimal(((NumericValue)av).getDecimalValue().toString());
598
                } catch (Exception) {
599
                    throw new ArgumentException("Cannot cast item to a decimal");
600
                }   
601
            } else if (av is StringValue) {
602
                return Convert.ToDecimal(av.getStringValueCS().toString());
603
            } else {
604
                throw new ArgumentException("Cannot cast item to a decimal");
605
            }
606
        }
607

    
608

    
609

    
610
        /// <summary>
611
        /// Convert the atomic value to a string
612
        /// </summary>
613
        /// <returns>The value converted to a string, according to the rules
614
        /// of the XPath 2.0 cast operator</returns>        
615

    
616
        public override String ToString()
617
        {
618
            return ((JAtomicValue)value).getStringValue();
619
        }
620

    
621
        /// <summary>
622
        /// Get the name of the value's XDM type
623
        /// </summary>
624
        /// <param name="processor">The <c>Processor</c> object. 
625
        /// This is needed for access to the NamePool,
626
        /// which maps the internal form of type names to their external form.</param>
627
        /// <returns>The type of the value, as a QName. This may be a built-in type or a user-defined
628
        /// atomic type.
629
        /// </returns>
630

    
631

    
632
        public QName GetTypeName(Processor processor)
633
        {
634
            int fp = ((JAtomicType)((JAtomicValue)value).getItemType(null)).getFingerprint();
635
            NamePool pool = processor.config.getNamePool();
636
            return new QName(pool.getPrefix(fp),
637
                             pool.getURI(fp),
638
                             pool.getLocalName(fp));
639
        }
640

    
641
        /// <summary>
642
        /// Get the name of the primitive type of the value
643
        /// </summary>
644
        /// <returns>The primitive type of the value, as a QName. This will be the name of
645
        /// one of the primitive types defined in XML Schema Part 2, or the XPath-defined
646
        /// type <c>xs:untypedAtomic</c>. For the purposes of this method, <c>xs:integer</c> is considered
647
        /// to be a primitive type.
648
        /// </returns>
649

    
650

    
651
        public QName GetPrimitiveTypeName()
652
        {
653
            int fp = ((JAtomicValue)value).getItemType(null).getPrimitiveType();
654
            return new QName(JStandardNames.getPrefix(fp),
655
                             JStandardNames.getURI(fp),
656
                             JStandardNames.getLocalName(fp));
657
        }
658

    
659
        /// <summary>Get the value as a CLI object of the nearest equivalent type.</summary>
660
        /// <remarks>
661
        /// <para>The return type is as follows:</para>
662
        /// <para>xs:string - String</para>
663
        /// <para>xs:integer - Long</para>
664
        /// <para>xs:decimal - Decimal</para>
665
        /// <para>xs:double - Double</para>
666
        /// <para>xs:float - Float</para>
667
        /// <para>xs:boolean - Bool</para>
668
        /// <para>xs:QName - QName</para>
669
        /// <para>xs:anyURI - Uri</para>
670
        /// <para>xs:untypedAtomic - String</para>
671
        /// <para>wrapped external object - the original external object</para>
672
        /// <para>Other types - currently String, but this may change in the future</para>
673
        /// </remarks>
674
        /// <returns>The value converted to the most appropriate CLI type</returns>
675

    
676
        public Object Value
677
        {
678
            get
679
            {
680
                if (value is IntegerValue)
681
                {
682
                    return ((IntegerValue)value).longValue();
683
                }
684
                else if (value is DoubleValue)
685
                {
686
                    return ((DoubleValue)value).getDoubleValue();
687
                }
688
                else if (value is FloatValue)
689
                {
690
                    return ((FloatValue)value).getFloatValue();
691
                }
692
                else if (value is DecimalValue)
693
                {
694
                    return Decimal.Parse(((DecimalValue)value).getStringValue());
695
                }
696
                else if (value is BooleanValue)
697
                {
698
                    return ((BooleanValue)value).getBooleanValue();
699
                }
700
                else if (value is AnyURIValue)
701
                {
702
                    return new Uri(((AnyURIValue)value).getStringValue());
703
                }
704
                else if (value is QNameValue)
705
                {
706
                    return new QName((QNameValue)value);
707
                }
708
                else if (value is DotNetObjectValue)
709
                {
710
                    return ((DotNetObjectValue)value).getObject();
711
                }
712
                else
713
                {
714
                    return ((JAtomicValue)value).getStringValue();
715
                }
716
            }
717
        }
718

    
719

    
720
    }
721

    
722
    /// <summary inherits="XdmItem">
723
    /// The class <c>XdmFunctionItem</c> represents an item in an XPath 3.0 sequence
724
    /// that represents a function.
725
    /// </summary>
726
    /// <remarks>
727
    /// <para>Note that there is no guarantee that every <c>XdmValue</c> comprising a single
728
    /// function item will be an instance of this class. To force this, use the <c>Simplify</c>
729
    /// property of the <c>XdmValue</c>.</para>
730
    /// <para>At present the only way of creating an instance of this class is as the result of
731
    /// an XPath or XQuery expression that returns a function item. Note that this feature requires
732
    /// XPath 3.0 or XQuery 3.0 to be enabled, which in turn requires use of Saxon-EE.</para>
733
    /// </remarks>
734

    
735
    [Serializable]
736
    public class XdmFunctionItem : XdmItem
737
    {
738
        /// <summary>
739
        /// The name of the function, as a QName. The result will be null if the function is anonymous.
740
        /// </summary>
741
        
742
        public QName FunctionName
743
        {
744
            get
745
            {
746
                return QName.FromStructuredQName(((JFunctionItem)value).getFunctionName());
747
            }
748
        }
749

    
750
        /// <summary>
751
        /// The arity of the function, that is, the number of arguments it expects
752
        /// </summary>
753

    
754
        public int Arity
755
        {
756
            get
757
            {
758
                return ((JFunctionItem)value).getArity();
759
            }
760
        }
761

    
762
        /// <summary>
763
        /// Determine whether the item is an atomic value
764
        /// </summary>
765
        /// <returns>
766
        /// false (a function item is not an atomic value)
767
        /// </returns>
768

    
769
        public override bool IsAtomic()
770
        {
771
            return false;
772
        }
773

    
774

    
775
        /// <summary>
776
        /// Invoke the function
777
        /// </summary>
778
        /// <param name="arguments">The arguments to the function</param>
779
        /// <param name="processor">The Saxon processor, used to provide context information</param>
780
        /// <returns>The result of calling the function</returns>
781
        /// 
782
        public XdmValue invoke(XdmValue[] arguments, Processor processor)
783
        {
784
            SequenceIterator[] args = new SequenceIterator[arguments.Length];
785
            for (int i = 0; i < arguments.Length; i++)
786
            {
787
                args[i] = Value.asIterator(arguments[i].Unwrap());
788
            }
789
            JXPathContext context = processor.config.getConversionContext();
790
            SequenceIterator result = ((JFunctionItem)value).invoke(args, context);
791
            return XdmValue.Wrap(JSequenceExtent.makeSequenceExtent(result));
792
        }
793
    }
794

    
795

    
796
    /// <summary inherits="XdmItem">
797
    /// The class <c>XdmNode</c> represents a Node in the XDM Data Model. A Node
798
    /// is an <c>XdmItem</c>, and is therefore an <c>XdmValue</c> in its own right, and may also participate
799
    /// as one item within a sequence value.
800
    /// </summary>
801
    /// <remarks>
802
    /// <para>An <c>XdmNode</c> is implemented as a wrapper around an object
803
    /// of type <c>net.sf.saxon.NodeInfo</c>. Because this is a key interface
804
    /// within Saxon, it is exposed via this API, even though it is a Java
805
    /// interface that is not part of the API proper.</para>
806
    /// <para>The <c>XdmNode</c> interface exposes basic properties of the node, such
807
    /// as its name, its string value, and its typed value. Navigation to other nodes
808
    /// is supported through a single method, <c>EnumerateAxis</c>, which allows
809
    /// other nodes to be retrieved by following any of the XPath axes.</para>
810
    /// </remarks>
811

    
812
    [Serializable]
813
    public class XdmNode : XdmItem
814
    {
815

    
816
        /// <summary>
817
        /// Determine whether the item is an atomic value
818
        /// </summary>
819
        /// <returns>
820
        /// false (the item is not an atomic value)
821
        /// </returns>
822

    
823
        public override bool IsAtomic()
824
        {
825
            return false;
826
        }
827

    
828
        /// <summary>
829
        /// The name of the node, as a <c>QName</c>. Returns null in the case of unnamed nodes.
830
        /// </summary>
831

    
832
        public QName NodeName
833
        {
834
            get
835
            {
836
                NodeInfo node = (NodeInfo)value;
837
                String local = node.getLocalPart();
838
                if (local == "")
839
                {
840
                    return null;
841
                }
842
                String prefix = node.getPrefix();
843
                String uri = node.getURI();
844
                return new QName(prefix, uri, local);
845
            }
846
        }
847

    
848
        /// <summary>
849
        /// The kind of node, as an instance of <c>System.Xml.XmlNodeType</c>.
850
        /// </summary>
851
        /// <remarks>For a namespace node in the XDM model, the value XmlNodeType.None 
852
        /// is returned.
853
        /// </remarks>
854

    
855
        public XmlNodeType NodeKind
856
        {
857
            get
858
            {
859
                NodeInfo node = (NodeInfo)value;
860
                int kind = node.getNodeKind();
861
                switch (kind)
862
                {
863
                    case JType.DOCUMENT:
864
                        return XmlNodeType.Document;
865
                    case JType.ELEMENT:
866
                        return XmlNodeType.Element;
867
                    case JType.ATTRIBUTE:
868
                        return XmlNodeType.Attribute;
869
                    case JType.TEXT:
870
                        return XmlNodeType.Text;
871
                    case JType.COMMENT:
872
                        return XmlNodeType.Comment;
873
                    case JType.PROCESSING_INSTRUCTION:
874
                        return XmlNodeType.ProcessingInstruction;
875
                    case JType.NAMESPACE:
876
                        return XmlNodeType.None;
877
                    default:
878
                        throw new ArgumentException("Unknown node kind");
879
                }
880
            }
881
        }
882

    
883
        /// <summary>
884
        /// The typed value of the node, as an instance of <c>XdmValue</c>.
885
        /// </summary>
886
        /// <exception>
887
        /// A DynamicError is thrown if the node has no typed value, as will be the case for
888
        /// an element with element-only content.
889
        /// </exception>
890

    
891
        public XdmValue TypedValue
892
        {
893
            get { return XdmValue.Wrap(((NodeInfo)value).atomize()); }
894
        }
895

    
896
        /// <summary>
897
        /// The string value of the node.
898
        /// </summary>
899

    
900
        public String StringValue
901
        {
902
            get { return ((NodeInfo)value).getStringValue(); }
903
        }
904

    
905
        /// <summary>
906
        /// Get the parent of this node.
907
        /// </summary>
908
        /// <remarks>
909
        /// Returns either a document node, and element node, or null in the case where
910
        /// this node has no parent. 
911
        /// </remarks>
912

    
913
        public XdmNode Parent
914
        {
915
            get {
916
                NodeInfo parent = ((NodeInfo)value).getParent();
917
                return (parent == null ? null : (XdmNode)XdmValue.Wrap(parent)); 
918
            }
919
        }
920

    
921
        /// <summary>
922
        /// Get the root of the tree containing this node.
923
        /// </summary>
924
        /// <remarks>
925
        /// Returns the root of the tree containing this node (which might be this node itself).
926
        /// </remarks>
927

    
928
        public XdmNode Root
929
        {
930
            get
931
            {
932
                XdmNode parent = Parent;
933
                if (parent == null)
934
                {
935
                    return this;
936
                }
937
                else
938
                {
939
                    return parent.Root;
940
                }
941
            }
942
        }
943

    
944
        /// <summary>
945
        /// Get a the string value of a named attribute of this element. 
946
        /// </summary>
947
        /// <remarks>
948
        /// Returns null if this node is not an element, or if this element has no
949
        /// attribute with the specified name.
950
        /// </remarks>
951
        /// <param name="name">The name of the attribute whose value is required</param>
952

    
953
        public String GetAttributeValue(QName name)
954
        {
955
            int fp = ((NodeInfo)value).getConfiguration().getNamePool().allocate(
956
                "", name.Uri, name.LocalName);
957
            return ((NodeInfo)value).getAttributeValue(fp);
958
        }
959

    
960
        /// <summary>
961
        /// Get an enumerator that supplies all the nodes on one of the XPath
962
        /// axes, starting with this node.
963
        /// </summary>
964
        /// <param name="axis">
965
        /// The axis to be navigated, for example <c>XdmAxis.Child</c> for the child axis.
966
        /// </param>
967
        /// <remarks>
968
        /// The nodes are returned in axis order: that is, document order for a forwards
969
        /// axis, reverse document order for a reverse axis.
970
        /// </remarks>
971

    
972
        public IEnumerator EnumerateAxis(XdmAxis axis)
973
        {
974
            return new SequenceEnumerator(((NodeInfo)value).iterateAxis(GetAxisNumber(axis)));
975
        }
976

    
977
        /// <summary>
978
        /// Get an enumerator that selects all the nodes on one of the XPath
979
        /// axes, provided they have a given name. The nodes selected are those of the principal
980
        /// node kind (elements for most axes, attributes for the attribute axis, namespace nodes
981
        /// for the namespace axis) whose name matches the name given in the second argument.
982
        /// </summary>
983
        /// <param name="axis">
984
        /// The axis to be navigated, for example <c>XdmAxis.Child</c> for the child axis.
985
        /// </param>
986
        /// <param name="nodeName">
987
        /// The name of the required nodes, for example <c>new QName("", "item")</c> to select
988
        /// nodes with local name "item", in no namespace.
989
        /// </param>
990
        /// <remarks>
991
        /// The nodes are returned in axis order: that is, document order for a forwards
992
        /// axis, reverse document order for a reverse axis.
993
        /// </remarks>
994

    
995
        public IEnumerator EnumerateAxis(XdmAxis axis, QName nodeName)
996
        {
997
            int kind;
998
            switch (axis)
999
            {
1000
                case XdmAxis.Attribute:
1001
                    kind = net.sf.saxon.type.Type.ATTRIBUTE;
1002
                    break;
1003
                case XdmAxis.Namespace:
1004
                    kind = net.sf.saxon.type.Type.NAMESPACE;
1005
                    break;
1006
                default:
1007
                    kind = net.sf.saxon.type.Type.ELEMENT;
1008
                    break;
1009
            }
1010
            NamePool pool = ((NodeInfo)value).getConfiguration().getNamePool();
1011
            int nameCode = pool.allocate("", nodeName.Uri, nodeName.LocalName);
1012
            NameTest test = new NameTest(kind, nameCode, pool);
1013
            return new SequenceEnumerator(((NodeInfo)value).iterateAxis(GetAxisNumber(axis), test));
1014
        }
1015

    
1016
        private static byte GetAxisNumber(XdmAxis axis)
1017
        {
1018
            switch (axis)
1019
            {
1020
                case XdmAxis.Ancestor: return Axis.ANCESTOR;
1021
                case XdmAxis.AncestorOrSelf: return Axis.ANCESTOR_OR_SELF;
1022
                case XdmAxis.Attribute: return Axis.ATTRIBUTE;
1023
                case XdmAxis.Child: return Axis.CHILD;
1024
                case XdmAxis.Descendant: return Axis.DESCENDANT;
1025
                case XdmAxis.DescendantOrSelf: return Axis.DESCENDANT_OR_SELF;
1026
                case XdmAxis.Following: return Axis.FOLLOWING;
1027
                case XdmAxis.FollowingSibling: return Axis.FOLLOWING_SIBLING;
1028
                case XdmAxis.Namespace: return Axis.NAMESPACE;
1029
                case XdmAxis.Parent: return Axis.PARENT;
1030
                case XdmAxis.Preceding: return Axis.PRECEDING;
1031
                case XdmAxis.PrecedingSibling: return Axis.PRECEDING_SIBLING;
1032
                case XdmAxis.Self: return Axis.SELF;
1033
            }
1034
            return 0;
1035
        }
1036

    
1037
        /// <summary>
1038
        /// The Base URI of the node.
1039
        /// </summary>
1040

    
1041
        public Uri BaseUri
1042
        {
1043
            get { return new Uri(((NodeInfo)value).getBaseURI()); }
1044
        }
1045

    
1046
        /// <summary>
1047
        /// The Document URI of the node.
1048
        /// </summary>
1049

    
1050
        public Uri DocumentUri
1051
        {
1052
            get
1053
            {
1054
                String s = ((NodeInfo)value).getSystemId();
1055
                if (s == null || s.Length == 0)
1056
                {
1057
                    return null;
1058
                }
1059
                return new Uri(s);
1060
            }
1061
        }
1062

    
1063
        /// <summary>
1064
        /// Send the node (that is, the subtree rooted at this node) to an <c>XmlWriter</c>
1065
        /// </summary>
1066
        /// <remarks>
1067
        /// Note that a <c>XmlWriter</c> can only handle a well-formed XML document. This method
1068
        /// will therefore signal an exception if the node is a document node with no children, or with
1069
        /// more than one element child.
1070
        /// </remarks>
1071
        /// <param name="writer">
1072
        /// The <c>XmlWriter</c> to which the node is to be written
1073
        /// </param>
1074

    
1075
        public void WriteTo(XmlWriter writer)
1076
        {
1077
            NodeInfo node = ((NodeInfo)value);
1078
            DotNetReceiver receiver = new DotNetReceiver(writer);
1079
            receiver.setPipelineConfiguration(node.getConfiguration().makePipelineConfiguration());
1080
            receiver.open();
1081
            node.copy(receiver, net.sf.saxon.om.CopyOptions.ALL_NAMESPACES, 0);
1082
            receiver.close();
1083
        }
1084

    
1085
        /// <summary>
1086
        /// Return a serialization of this node as lexical XML
1087
        /// </summary>
1088
        /// <remarks>
1089
        /// <para>In the case of an element node, the result will be a well-formed
1090
        /// XML document serialized as defined in the W3C XSLT/XQuery serialization specification,
1091
        /// using options method="xml", indent="yes", omit-xml-declaration="yes".</para>
1092
        /// <para>In the case of a document node, the result will be a well-formed
1093
        /// XML document provided that the document node contains exactly one element child,
1094
        /// and no text node children. In other cases it will be a well-formed external
1095
        /// general parsed entity.</para>
1096
        /// <para>In the case of an attribute node, the output is a string in the form
1097
        /// <c>name="value"</c>. The name will use the original namespace prefix.</para>
1098
        /// <para>Other nodes, such as text nodes, comments, and processing instructions, are
1099
        /// represented as they would appear in lexical XML.</para>
1100
        /// </remarks>
1101

    
1102
        public String OuterXml
1103
        {
1104
            get
1105
            {
1106
                NodeInfo node = ((NodeInfo)value);
1107

    
1108
                if (node.getNodeKind() == JType.ATTRIBUTE)
1109
                {
1110
                    String val = node.getStringValue().Replace("\"", "&quot;");
1111
                    val = val.Replace("<", "&lt;");
1112
                    val = val.Replace("&", "&amp;");
1113
                    return node.getDisplayName() + "=\"" + val + '"';
1114
                }
1115

    
1116
                Serializer serializer = new Serializer();
1117
                serializer.SetOutputProperty(Serializer.METHOD, "xml");
1118
                serializer.SetOutputProperty(Serializer.INDENT, "yes");
1119
                serializer.SetOutputProperty(Serializer.OMIT_XML_DECLARATION, "yes");
1120

    
1121
                StringWriter sw = new StringWriter();
1122
                serializer.SetOutputWriter(sw);
1123
                node.copy(serializer.GetReceiver(node.getConfiguration()), net.sf.saxon.om.CopyOptions.ALL_NAMESPACES, 0);
1124
                return sw.ToString();
1125
            }
1126
        }
1127

    
1128
        /// <summary>
1129
        /// Two instances of XdmNode are equal if they represent the same node. That is, the Equals()
1130
        /// method returns the same result as the XPath "is" operator.
1131
        /// </summary>
1132
        /// <param name="obj">The object node to be compared</param>
1133
         
1134
        public override bool Equals(object obj)
1135
        {
1136
            return obj is XdmNode && ((NodeInfo)value).equals((NodeInfo)((XdmNode)obj).value);
1137
        }
1138

    
1139
        /// <summary>
1140
        /// The hashCode of a node reflects the equality relationship: if two XdmNode instances
1141
        /// represent the same node, then they have the same hashCode
1142
        /// </summary>
1143

    
1144
        public override int GetHashCode()
1145
        {
1146
            return ((NodeInfo)value).hashCode();
1147
        }
1148

    
1149
        /// <summary>
1150
        /// Return a string representation of the node.
1151
        /// </summary>
1152
        /// <remarks>
1153
        /// This currently returns the same as the <c>OuterXml</c> property.
1154
        /// To get the string value as defined in XPath, use the <c>StringValue</c> property.
1155
        /// </remarks>
1156

    
1157
        public override String ToString()
1158
        {
1159
            return OuterXml;
1160
        }
1161

    
1162
        /// <summary>
1163
        /// Escape hatch to the underlying class in the Java implementation
1164
        /// </summary>
1165

    
1166
        public NodeInfo Implementation
1167
        {
1168
            get { return ((NodeInfo)value); }
1169
        }
1170

    
1171

    
1172
    }
1173

    
1174

    
1175
    /// <summary inherits="XdmValue">
1176
    /// The class <c>XdmEmptySequence</c> represents an empty sequence in the XDM Data Model.
1177
    /// </summary>
1178
    /// <remarks>
1179
    /// <para>An empty sequence <i>may</i> also be represented by an <c>XdmValue</c> whose length
1180
    /// happens to be zero. Applications should therefore not test to see whether an object
1181
    /// is an instance of this class in order to decide whether it is empty.</para>
1182
    /// <para>In interfaces that expect an <c>XdmItem</c>, an empty sequence is represented
1183
    /// by a CLI <c>null</c> value.</para> 
1184
    /// </remarks>
1185

    
1186
    [Serializable]
1187
    public sealed class XdmEmptySequence : XdmValue
1188
    {
1189

    
1190
        ///<summary>The singular instance of this class</summary>
1191

    
1192
        public static XdmEmptySequence INSTANCE = new XdmEmptySequence();
1193

    
1194
        private XdmEmptySequence()
1195
        {
1196
            this.value = JEmptySequence.getInstance();
1197
        }
1198
    }
1199

    
1200

    
1201
    /// <summary>
1202
    /// The QName class represents an instance of xs:QName, as defined in the XPath 2.0
1203
    /// data model. Internally, it has three components, a namespace URI, a local name, and
1204
    /// a prefix. The prefix is intended to be used only when converting the value back to 
1205
    /// a string.
1206
    /// </summary>
1207
    /// <remarks>
1208
    /// Note that a QName is not itself an <c>XdmItem</c> in this model; however it can
1209
    /// be wrapped in an XdmItem.
1210
    /// </remarks>    
1211

    
1212
    [Serializable]
1213
    public sealed class QName
1214
    {
1215

    
1216
        private String prefix;
1217
        private String uri;
1218
        private String local;
1219
        int hashcode = -1;      // evaluated lazily
1220
        int fingerprint = -1;   // evaluated only if the QName is registered with the Processor
1221
        private NamePool pool = null;
1222

    
1223
        private static String XS = NamespaceConstant.SCHEMA;
1224

    
1225
        /// <summary>QName constant for the name xs:string</summary>
1226
        public static readonly QName XS_STRING = new QName(XS, "xs:string");
1227

    
1228
        /// <summary>QName constant for the name xs:integer</summary>
1229
        public static readonly QName XS_INTEGER = new QName(XS, "xs:integer");
1230

    
1231
        /// <summary>QName constant for the name xs:double</summary>
1232
        public static readonly QName XS_DOUBLE = new QName(XS, "xs:double");
1233

    
1234
        /// <summary>QName constant for the name xs:float</summary>
1235
        public static readonly QName XS_FLOAT = new QName(XS, "xs:float");
1236

    
1237
        /// <summary>QName constant for the name xs:decimal</summary>
1238
        public static readonly QName XS_DECIMAL = new QName(XS, "xs:decimal");
1239

    
1240
        /// <summary>QName constant for the name xs:boolean</summary>
1241
        public static readonly QName XS_BOOLEAN = new QName(XS, "xs:boolean");
1242

    
1243
        /// <summary>QName constant for the name xs:anyURI</summary>
1244
        public static readonly QName XS_ANYURI = new QName(XS, "xs:anyURI");
1245

    
1246
        /// <summary>QName constant for the name xs:QName</summary>
1247
        public static readonly QName XS_QNAME = new QName(XS, "xs:QName");
1248

    
1249
        /// <summary>QName constant for the name xs:untypedAtomic</summary>
1250
        public static readonly QName XS_UNTYPED_ATOMIC = new QName(XS, "xs:untypedAtomic");
1251

    
1252
        /// <summary>QName constant for the name xs:untypedAtomic (for backwards compatibility)</summary>
1253
        public static readonly QName XDT_UNTYPED_ATOMIC = new QName(XS, "xs:untypedAtomic");
1254

    
1255
        /// <summary>
1256
        /// Construct a QName representing a name in no namespace
1257
        /// </summary>
1258
        /// <remarks>
1259
        /// This constructor does not check that the components of the QName are
1260
        /// lexically valid.
1261
        /// </remarks>
1262
        /// <param name="local">The local part of the name
1263
        /// </param>
1264

    
1265
        public QName(String local)
1266
        {
1267
            // TODO: check for validity
1268
            this.prefix = String.Empty;
1269
            this.uri = String.Empty;
1270
            this.local = local;
1271
        }
1272

    
1273
        /// <summary>
1274
        /// Construct a QName using a namespace URI and a lexical representation.
1275
        /// The lexical representation may be a local name on its own, or it may 
1276
        /// be in the form <c>prefix:local-name</c>
1277
        /// </summary>
1278
        /// <remarks>
1279
        /// This constructor does not check that the components of the QName are
1280
        /// lexically valid.
1281
        /// </remarks>
1282
        /// <param name="uri">The namespace URI. Use either the string "" or null
1283
        /// for names that are not in any namespace.
1284
        /// </param>
1285
        /// <param name="lexical">Either the local part of the name, or the prefix
1286
        /// and local part in the format <c>prefix:local</c>
1287
        /// </param>
1288

    
1289
        public QName(String uri, String lexical)
1290
        {
1291
            // TODO: check for validity
1292
            this.uri = (uri == null ? "" : uri);
1293
            int colon = lexical.IndexOf(':');
1294
            if (colon < 0)
1295
            {
1296
                this.prefix = String.Empty;
1297
                this.local = lexical;
1298
            }
1299
            else
1300
            {
1301
                this.prefix = lexical.Substring(0, colon);
1302
                this.local = lexical.Substring(colon + 1);
1303
            }
1304
        }
1305

    
1306
        /// <summary>
1307
        /// Construct a QName using a namespace prefix, a namespace URI, and a local name
1308
        /// (in that order).
1309
        /// </summary>
1310
        /// <remarks>
1311
        /// This constructor does not check that the components of the QName are
1312
        /// lexically valid.
1313
        /// </remarks>
1314
        /// <param name="prefix">The prefix of the name. Use either the string ""
1315
        /// or null for names that have no prefix (that is, they are in the default
1316
        /// namespace)</param>
1317
        /// <param name="uri">The namespace URI. Use either the string "" or null
1318
        /// for names that are not in any namespace.
1319
        /// </param>
1320
        /// <param name="local">The local part of the name</param>
1321

    
1322
        public QName(String prefix, String uri, String local)
1323
        {
1324
            this.uri = (uri == null ? String.Empty : uri);
1325
            this.local = local;
1326
            this.prefix = (prefix == null ? String.Empty : prefix);
1327
        }
1328

    
1329
        /// <summary>
1330
        /// Construct a QName from a lexical QName, supplying an element node whose
1331
        /// in-scope namespaces are to be used to resolve any prefix contained in the QName.
1332
        /// </summary>
1333
        /// <remarks>
1334
        /// <para>This constructor checks that the components of the QName are
1335
        /// lexically valid.</para>
1336
        /// <para>If the lexical QName has no prefix, the name is considered to be in the
1337
        /// default namespace, as defined by <c>xmlns="..."</c>.</para>
1338
        /// <para>If the prefix of the lexical QName is not in scope, returns null.</para>
1339
        /// </remarks>
1340
        /// <param name="lexicalQName">The lexical QName, in the form <code>prefix:local</code>
1341
        /// or simply <c>local</c>.</param>
1342
        /// <param name="element">The element node whose in-scope namespaces are to be used
1343
        /// to resolve the prefix part of the lexical QName.</param>
1344
        /// <exception cref="ArgumentException">If the prefix of the lexical QName is not in scope</exception>
1345
        /// <exception cref="ArgumentException">If the lexical QName is invalid 
1346
        /// (for example, if it contains invalid characters)</exception>
1347
        /// 
1348

    
1349
        public QName(String lexicalQName, XdmNode element)
1350
        {
1351
            try
1352
            {
1353
                NodeInfo node = (NodeInfo)element.value;
1354
                NamePool pool = node.getConfiguration().getNamePool();
1355
                int nc = pool.allocateLexicalQName(lexicalQName, true, new InscopeNamespaceResolver(node),
1356
                    node.getConfiguration().getNameChecker());
1357
                this.uri = pool.getURI(nc);
1358
                this.local = pool.getLocalName(nc);
1359
                this.prefix = pool.getPrefix(nc);
1360
            }
1361
            catch (net.sf.saxon.trans.XPathException err)
1362
            {
1363
                throw new ArgumentException(err.getMessage());
1364
            }
1365
        }
1366

    
1367
        /// <summary>
1368
        /// Construct a <c>QName</c> from an <c>XmlQualifiedName</c> (as defined in the
1369
        /// <c>System.Xml</c> package).
1370
        /// </summary>
1371
        /// <remarks>
1372
        /// Note that an <c>XmlQualifiedName</c> does not contain any prefix, so the result
1373
        /// will always have a prefix of ""
1374
        /// </remarks>
1375
        /// <param name="qualifiedName">The XmlQualifiedName</param>
1376

    
1377
        public QName(XmlQualifiedName qualifiedName)
1378
        {
1379
            this.uri = qualifiedName.Namespace;
1380
            this.local = qualifiedName.Name;
1381
            this.prefix = String.Empty;
1382
        }
1383

    
1384
        //  internal constructor from a QNameValue
1385

    
1386
        internal QName(QNameValue q)
1387
        {
1388
            this.uri = q.getNamespaceURI();
1389
            this.prefix = q.getPrefix();
1390
            this.local = q.getLocalName();
1391
        }
1392

    
1393
        /// <summary>
1394
        /// Factory method to construct a QName from a string containing the expanded
1395
        /// QName in Clark notation, that is, <c>{uri}local</c>
1396
        /// </summary>
1397
        /// <remarks>
1398
        /// The prefix part of the <c>QName</c> will be set to an empty string.
1399
        /// </remarks>
1400
        /// <param name="expandedName">The URI in Clark notation: <c>{uri}local</c> if the
1401
        /// name is in a namespace, or simply <c>local</c> if not.</param> 
1402

    
1403
        public static QName FromClarkName(String expandedName)
1404
        {
1405
            String namespaceURI;
1406
            String localName;
1407
            if (expandedName[0] == '{')
1408
            {
1409
                int closeBrace = expandedName.IndexOf('}');
1410
                if (closeBrace < 0)
1411
                {
1412
                    throw new ArgumentException("No closing '}' in Clark name");
1413
                }
1414
                namespaceURI = expandedName.Substring(1, closeBrace - 1);
1415
                if (closeBrace == expandedName.Length)
1416
                {
1417
                    throw new ArgumentException("Missing local part in Clark name");
1418
                }
1419
                localName = expandedName.Substring(closeBrace + 1);
1420
            }
1421
            else
1422
            {
1423
                namespaceURI = "";
1424
                localName = expandedName;
1425
            }
1426

    
1427
            return new QName("", namespaceURI, localName);
1428
        }
1429

    
1430
        /// <summary>
1431
        /// Factory method to construct a QName from Saxon's internal <c>StructuredQName</c>
1432
        /// representation.
1433
        /// </summary>
1434

    
1435
        internal static QName FromStructuredQName(StructuredQName sqn) {
1436
            return new QName(sqn.getPrefix(), sqn.getURI(), sqn.getLocalPart());
1437
        }
1438

    
1439
        /// <summary>
1440
        /// Register a QName with the <c>Processor</c>. This makes comparison faster
1441
        /// when the QName is compared with others that are also registered with the <c>Processor</c>.
1442
        /// </summary>
1443
        /// <remarks>
1444
        /// A given <c>QName</c> object can only be registered with one <c>Processor</c>.
1445
        /// </remarks>
1446
        /// <param name="processor">The Processor in which the name is to be registered.</param>
1447

    
1448
        public void Register(Processor processor)
1449
        {
1450
            if (pool != null && pool != processor.config.getNamePool())
1451
            {
1452
                throw new InvalidOperationException("A QName cannot be registered with more than one Processor");
1453
            }
1454
            pool = processor.config.getNamePool();
1455
            fingerprint = pool.allocate(prefix, uri, local) & 0xfffff;
1456
        }
1457

    
1458
        /// <summary>
1459
        /// Validate the QName against the XML 1.0 or XML 1.1 rules for valid names.
1460
        /// </summary>
1461
        /// <param name="processor">The Processor in which the name is to be validated.
1462
        /// This determines whether the XML 1.0 or XML 1.1 rules for forming names are used.</param>
1463
        /// <returns>true if the name is valid, false if not</returns>
1464

    
1465
        public bool IsValid(Processor processor)
1466
        {
1467
            NameChecker nc = processor.config.getNameChecker();
1468
            if (prefix != String.Empty)
1469
            {
1470
                if (!nc.isValidNCName(prefix))
1471
                {
1472
                    return false;
1473
                }
1474
            }
1475
            if (!nc.isValidNCName(local))
1476
            {
1477
                return false;
1478
            }
1479
            return true;
1480
        }
1481

    
1482
        /// <summary>The prefix of the QName. This plays no role in operations such as comparison
1483
        /// of QNames for equality, but is retained (as specified in XPath) so that a string representation
1484
        /// can be reconstructed.
1485
        /// </summary>
1486
        /// <remarks>
1487
        /// Returns the zero-length string in the case of a QName that has no prefix.
1488
        /// </remarks>
1489

    
1490
        public String Prefix
1491
        {
1492
            get { return prefix; }
1493
        }
1494

    
1495
        /// <summary>The namespace URI of the QName. Returns "" (the zero-length string) if the
1496
        /// QName is not in a namespace.
1497
        /// </summary>
1498

    
1499
        public String Uri
1500
        {
1501
            get { return uri; }
1502
        }
1503

    
1504
        /// <summary>The local part of the QName</summary>
1505

    
1506
        public String LocalName
1507
        {
1508
            get { return local; }
1509
        }
1510

    
1511
        /// <summary>The expanded name, as a string using the notation devised by James Clark.
1512
        /// If the name is in a namespace, the resulting string takes the form <c>{uri}local</c>.
1513
        /// Otherwise, the value is the local part of the name.
1514
        /// </summary>
1515

    
1516
        public String ClarkName
1517
        {
1518
            get
1519
            {
1520
                if (uri == "")
1521
                {
1522
                    return local;
1523
                }
1524
                else
1525
                {
1526
                    return "{" + uri + "}" + local;
1527
                }
1528
            }
1529
        }
1530

    
1531
        /// <summary>
1532
        /// Convert the value to a string. The resulting string is the lexical form of the QName,
1533
        /// using the original prefix if there was one.
1534
        /// </summary>
1535

    
1536
        public override String ToString()
1537
        {
1538
            if (prefix == "")
1539
            {
1540
                return local;
1541
            }
1542
            else
1543
            {
1544
                return prefix + ":" + uri;
1545
            }
1546
        }
1547

    
1548
        /// <summary>
1549
        /// Get a hash code for the QName, to support equality matching. This supports the
1550
        /// semantics of equality, which considers only the namespace URI and local name, and
1551
        /// not the prefix.
1552
        /// </summary>
1553
        /// <remarks>
1554
        /// The algorithm for allocating a hash code does not depend on registering the QName 
1555
        /// with the <c>Processor</c>.
1556
        /// </remarks>
1557

    
1558
        public override int GetHashCode()
1559
        {
1560
            if (hashcode == -1)
1561
            {
1562
                hashcode = ClarkName.GetHashCode();
1563
            }
1564
            return hashcode;
1565
        }
1566

    
1567
        /// <summary>
1568
        /// Test whether two QNames are equal. This supports the
1569
        /// semantics of equality, which considers only the namespace URI and local name, and
1570
        /// not the prefix.
1571
        /// </summary>
1572
        /// <remarks>
1573
        /// The result of the function does not depend on registering the QName 
1574
        /// with the <c>Processor</c>, but is computed more quickly if the QNames have
1575
        /// both been registered
1576
        /// </remarks>
1577
        /// <param name="other">The value to be compared with this QName. If this value is not a QName, the
1578
        /// result is always false. Otherwise, it is true if the namespace URI and local name both match.</param>
1579

    
1580
        public override bool Equals(Object other)
1581
        {
1582
            if (!(other is QName))
1583
            {
1584
                return false;
1585
            }
1586
            if (pool != null && pool == ((QName)other).pool)
1587
            {
1588
                return fingerprint == ((QName)other).fingerprint;
1589
            }
1590
            if (GetHashCode() != ((QName)other).GetHashCode())
1591
            {
1592
                return false;
1593
            }
1594
            return ClarkName == ((QName)other).ClarkName;
1595
            //TODO: avoid computing ClarkName more than once
1596
        }
1597

    
1598
        /// <summary>
1599
        /// Convert the value to an <c>XmlQualifiedName</c> (as defined in the
1600
        /// <c>System.Xml</c> package)
1601
        /// </summary>
1602
        /// <remarks>
1603
        /// Note that this loses the prefix.
1604
        /// </remarks>
1605

    
1606
        public XmlQualifiedName ToXmlQualifiedName()
1607
        {
1608
            return new XmlQualifiedName(local, uri);
1609
        }
1610

    
1611
        /// <summary>
1612
        /// Convert to a net.sf.saxon.value.QNameValue
1613
        /// </summary>
1614

    
1615
        internal QNameValue ToQNameValue()
1616
        {
1617
            return new QNameValue(prefix, uri, local, null);
1618
        }
1619

    
1620
        internal JStructuredQName ToStructuredQName()
1621
        {
1622
            return new JStructuredQName(Prefix, Uri, LocalName);
1623
        }
1624

    
1625
        /// <summary>
1626
        /// Get the internal Saxon fingerprint of this name
1627
        /// </summary>
1628
        /// <param name="config">The Saxon configuration (the fingerprint for a QName is different in different configurations)</param>
1629
        /// <returns>
1630
        /// The integer fingerprint of the name
1631
        /// </returns>
1632

    
1633
        internal int GetFingerprint(JConfiguration config)
1634
        {
1635
            JNamePool namePool = config.getNamePool();
1636
            if (fingerprint != -1 && pool == namePool)
1637
            {
1638
                return fingerprint;
1639
            }
1640
            return namePool.allocate(prefix, uri, local) & 0xfffff;
1641
        }
1642

    
1643

    
1644

    
1645
    }
1646

    
1647
    /// <summary>
1648
    /// This class represents an enumeration of the values in an XPath
1649
    /// sequence. It implements the IEnumerator interface, and the objects
1650
    /// returned are always instances of <c>XdmItem</c>. In addition to the
1651
    /// methods defined by <c>IEnumerator</c>, an additional method <c>GetAnother</c>
1652
    /// must be implemented: this provides a new iterator over the same sequence
1653
    /// of items, positioned at the start of the sequence.
1654
    /// </summary>
1655
    /// <remarks>
1656
    /// Because the underlying value can be evaluated lazily, it is possible
1657
    /// for exceptions to occur as the sequence is being read.
1658
    /// </remarks>
1659

    
1660
    public interface IXdmEnumerator : IEnumerator
1661
    {
1662

    
1663
        /// <summary>
1664
        /// Create another <c>XdmEnumerator</c> over the same sequence of values, positioned at the start
1665
        /// of the sequence, with no change to this <c>XdmEnumerator</c>.
1666
        /// </summary>
1667
        /// <returns>
1668
        /// A new XdmEnumerator over the same sequence of XDM items, positioned at the start of the sequence.
1669
        /// </returns>
1670

    
1671
        IXdmEnumerator GetAnother();
1672
    }
1673

    
1674
    /// <summary>
1675
    /// This class is an implementation of <c>IXdmEnumerator</c> that wraps
1676
    /// a (Java) SequenceIterator.
1677
    /// </summary>
1678
    /// <remarks>
1679
    /// Because the underlying value can be evaluated lazily, it is possible
1680
    /// for exceptions to occur as the sequence is being read.
1681
    /// </remarks>
1682

    
1683
    [Serializable]
1684
    internal class SequenceEnumerator : IXdmEnumerator
1685
    {
1686

    
1687
        private SequenceIterator iter;
1688

    
1689
        internal SequenceEnumerator(SequenceIterator iter)
1690
        {
1691
            this.iter = iter;
1692
        }
1693

    
1694
        /// <summary>Return the current item in the sequence</summary>
1695
        /// <returns>An object which will always be an instance of <c>XdmItem</c></returns>
1696

    
1697
        public object Current
1698
        {
1699
            get { return XdmValue.Wrap(iter.current()); }
1700
        }
1701

    
1702
        /// <summary>Move to the next item in the sequence</summary>
1703
        /// <returns>true if there are more items in the sequence</returns>
1704

    
1705
        public bool MoveNext()
1706
        {
1707
            return (iter.next() != null);
1708
        }
1709

    
1710
        /// <summary>Reset the enumeration so that the next call of
1711
        /// <c>MoveNext</c> will position the enumeration at the
1712
        /// first item in the sequence</summary>
1713

    
1714
        public void Reset()
1715
        {
1716
            iter = iter.getAnother();
1717
        }
1718

    
1719
        /// <summary>
1720
        /// Create another XdmEnumerator over the same sequence of values, positioned at the start
1721
        /// of the sequence, with no change to this XdmEnumerator.
1722
        /// </summary>
1723
        /// <returns>
1724
        /// A new XdmEnumerator over the same sequence of XDM items, positioned at the start of the sequence.
1725
        /// </returns>
1726

    
1727
        public IXdmEnumerator GetAnother()
1728
        {
1729
            return new SequenceEnumerator(iter.getAnother());
1730
        }
1731
    }
1732

    
1733
    /// <summary>
1734
    /// Implementation of the (Java) interface SequenceIterator that wraps
1735
    /// a (.NET) IXdmEnumerator
1736
    /// </summary>
1737

    
1738
    internal class DotNetSequenceIterator : JSequenceIterator
1739
    {
1740
        // TODO: catch errors and throw an XPathException if necessary.
1741

    
1742
        IXdmEnumerator iter;
1743
        int pos = 0;
1744

    
1745
        public DotNetSequenceIterator(IXdmEnumerator iter)
1746
        {
1747
            this.iter = iter;
1748
        }
1749

    
1750
        public JItem next()
1751
        {
1752
            if (pos < 0)
1753
            {
1754
                return null;
1755
            }
1756
            bool more = iter.MoveNext();
1757
            if (more)
1758
            {
1759
                pos++;
1760
                XdmItem i = (XdmItem)iter.Current;
1761
                return (JItem)i.Unwrap();
1762
            }
1763
            else
1764
            {
1765
                pos = -1;
1766
                return null;
1767
            }
1768

    
1769
        }
1770

    
1771
        public JItem current()
1772
        {
1773
            if (pos < 0)
1774
            {
1775
                return null;
1776
            }
1777
            XdmItem i = (XdmItem)iter.Current;
1778
            return (JItem)i.Unwrap();
1779
        }
1780

    
1781
        public int position()
1782
        {
1783
            return pos;
1784
        }
1785

    
1786
        public void close()
1787
        {
1788
        }
1789

    
1790
        public SequenceIterator getAnother()
1791
        {
1792
            return new DotNetSequenceIterator(iter.GetAnother());
1793
        }
1794

    
1795
        public int getProperties()
1796
        {
1797
            return 0;
1798
        }
1799
    }
1800

    
1801
    /// <summary>
1802
    /// Enumeration identifying the thirteen XPath axes
1803
    /// </summary>
1804

    
1805
    public enum XdmAxis
1806
    {
1807
        /// <summary>The XPath ancestor axis</summary> 
1808
        Ancestor,
1809
        /// <summary>The XPath ancestor-or-self axis</summary> 
1810
        AncestorOrSelf,
1811
        /// <summary>The XPath attribute axis</summary> 
1812
        Attribute,
1813
        /// <summary>The XPath child axis</summary> 
1814
        Child,
1815
        /// <summary>The XPath descendant axis</summary> 
1816
        Descendant,
1817
        /// <summary>The XPath descandant-or-self axis</summary> 
1818
        DescendantOrSelf,
1819
        /// <summary>The XPath following axis</summary> 
1820
        Following,
1821
        /// <summary>The XPath following-sibling axis</summary> 
1822
        FollowingSibling,
1823
        /// <summary>The XPath namespace axis</summary> 
1824
        Namespace,
1825
        /// <summary>The XPath parent axis</summary> 
1826
        Parent,
1827
        /// <summary>The XPath preceding axis</summary> 
1828
        Preceding,
1829
        /// <summary>The XPath preceding-sibling axis</summary> 
1830
        PrecedingSibling,
1831
        /// <summary>The XPath self axis</summary> 
1832
        Self
1833
    }
1834

    
1835
    /// <summary>
1836
    /// An implementation of <code>IXdmEnumerator</code> that iterates over an empty sequence.
1837
    /// </summary>
1838

    
1839
    public class EmptyEnumerator : IXdmEnumerator
1840
    {
1841

    
1842
        public static EmptyEnumerator INSTANCE = new EmptyEnumerator();
1843

    
1844
        private EmptyEnumerator() { }
1845

    
1846
        public void Reset() { }
1847

    
1848
        public object Current
1849
        {
1850
            get { throw new InvalidOperationException("Collection is empty."); }
1851
        }
1852

    
1853
        public bool MoveNext()
1854
        {
1855
            return false;
1856
        }
1857

    
1858
        public IXdmEnumerator GetAnother()
1859
        {
1860
            return this;
1861
        }
1862
    }
1863

    
1864

    
1865
}
1866

    
1867
//
1868
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
1869
// you may not use this file except in compliance with the License. You may obtain a copy of the
1870
// License at http://www.mozilla.org/MPL/
1871
//
1872
// Software distributed under the License is distributed on an "AS IS" basis,
1873
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
1874
// See the License for the specific language governing rights and limitations under the License.
1875
//
1876
// The Original Code is: all this file.
1877
//
1878
// The Initial Developer of the Original Code is Michael H. Kay.
1879
//
1880
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
1881
//
1882
// Contributor(s): none.
1883
//
(5-5/12)