Project

Profile

Help

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

he / latest9.1 / bn / csource / api / Saxon.Api / Schema.cs @ e090dcb1

1
using System;
2
using System.IO;
3
using System.Xml;
4
using System.Collections;
5
using javax.xml.transform;
6
using javax.xml.transform.stream;
7
//using JConfiguration = com.saxonica.validate.SchemaAwareConfiguration;
8
using JConfiguration = net.sf.saxon.Configuration;
9
using JReceiver = net.sf.saxon.@event.Receiver;
10
using net.sf.saxon;
11
using net.sf.saxon.om;
12
using net.sf.saxon.pull;
13
using net.sf.saxon.@event;
14
using net.sf.saxon.dotnet;
15
using net.sf.saxon.type;
16

    
17

    
18
namespace Saxon.Api
19
{
20

    
21
    /// <summary>
22
    /// A <c>SchemaManager</c> is responsible for compiling schemas and
23
    /// maintaining a cache of compiled schemas that can be used for validating
24
    /// instance documents.
25
    /// </summary>
26
    /// <remarks>
27
    /// <para>To obtain a <c>SchemaManager</c>, use the 
28
    /// <c>SchemaManager</c> property of the <c>Processor</c> object.</para>
29
    /// <para>In a schema-aware Processor there is exactly one
30
    /// <c>SchemaManager</c> (in a non-schema-aware Processor there is none).</para>
31
    /// <para>The cache of compiled schema definitions can include only one schema
32
    /// component (for example a type, or an element declaration) with any given name.
33
    /// An attempt to compile two different schemas in the same namespace will usually
34
    /// therefore fail.</para>
35
    /// <para>As soon as a type definition or element declaration is used for the first
36
    /// time in a validation episode, it is marked as being "sealed": this prevents subsequent
37
    /// modifications to the component. Examples of modifications that are thereby disallowed
38
    /// include adding to the substitution group of an existing element declaration, adding subtypes
39
    /// to an existing type, or redefining components using &lt;xs:redefine&gt;</para>
40
    /// </remarks>
41

    
42
    [Serializable]
43
    public class SchemaManager
44
    {
45

    
46
        private JConfiguration config;
47
        private IList errorList = null;
48

    
49
        // internal constructor: the public interface is a factory method
50
        // on the Processor object
51

    
52
        internal SchemaManager(net.sf.saxon.Configuration config)
53
        {
54
            this.config = (JConfiguration)config;
55
        }
56

    
57
        /// <summary>
58
        /// The SchemaResolver is a user-supplied class used for resolving references to
59
        /// schema documents. It applies to references from one schema document to another
60
        /// appearing in <c>xs:import</c>, <c>xs:include</c>, and <c>xs:redefine</c>; to
61
        /// references from an instance document to a schema in <c>xsi:schemaLocation</c> and
62
        /// <c>xsi:noNamespaceSchemaLocation</c>, to <c>xsl:import-schema</c> in XSLT, and to
63
        /// the <c>import schema</c> declaration in XQuery.
64
        /// </summary>
65

    
66
        public SchemaResolver SchemaResolver
67
        {
68
            get
69
            {
70
                SchemaURIResolver r = config.getSchemaURIResolver();
71
                if (r is DotNetSchemaURIResolver)
72
                {
73
                    return ((DotNetSchemaURIResolver)r).resolver;
74
                }
75
                else
76
                {
77
                    return null;
78
                }
79
            }
80
            set
81
            {
82
                config.setSchemaURIResolver(new DotNetSchemaURIResolver(value));
83
            }
84
        }
85

    
86
        /// <summary>
87
        /// List of errors. The caller may supply an empty list before calling Compile;
88
        /// the processor will then populate the list with error information obtained during
89
        /// the schema compilation. Each error will be included as an object of type StaticError.
90
        /// If no error list is supplied by the caller, error information will be written to
91
        /// the standard error stream.
92
        /// </summary>
93
        /// <remarks>
94
        /// <para>By supplying a custom List with a user-written add() method, it is possible to
95
        /// intercept error conditions as they occur.</para>
96
        /// <para>Note that this error list is used only for errors detected during the compilation
97
        /// of the schema. It is not used for errors detected when using the schema to validate
98
        /// a source document.</para>
99
        /// </remarks>
100

    
101
        public IList ErrorList
102
        {
103
            set
104
            {
105
                errorList = value;
106
            }
107
            get
108
            {
109
                return errorList;
110
            }
111
        }
112

    
113
        /// <summary>
114
        /// Compile a schema supplied as a Stream. The resulting schema components are added
115
        /// to the cache.
116
        /// </summary>
117
        /// <param name="input">A stream containing the source text of the schema. This method
118
        /// will consume the supplied stream. It is the caller's responsibility to close the stream
119
        /// after use.</param>
120
        /// <param name="baseUri">The base URI of the schema document, for resolving any references to other
121
        /// schema documents</param>        
122

    
123
        public void Compile(Stream input, Uri baseUri)
124
        {
125
            StreamSource ss = new StreamSource(new DotNetInputStream(input));
126
            ss.setSystemId(baseUri.ToString());
127
            if (errorList == null)
128
            {
129
                config.addSchemaSource(ss);
130
            }
131
            else
132
            {
133
                config.addSchemaSource(ss, new ErrorGatherer(errorList));
134
            }
135
        }
136

    
137
        /// <summary>
138
        /// Compile a schema, retrieving the source using a URI. The resulting schema components are added
139
        /// to the cache.
140
        /// </summary>
141
        /// <remarks>
142
        /// The document located via the URI is parsed using the <c>System.Xml</c> parser.
143
        /// </remarks>
144
        /// <param name="uri">The URI identifying the location where the schema document can be
145
        /// found</param>
146

    
147
        public void Compile(Uri uri)
148
        {
149
            StreamSource ss = new StreamSource(uri.ToString());
150
            AugmentedSource aug = AugmentedSource.makeAugmentedSource(ss);
151
            aug.setPleaseCloseAfterUse(true);
152
            if (errorList == null)
153
            {
154
                config.addSchemaSource(aug);
155
            }
156
            else
157
            {
158
                config.addSchemaSource(aug, new ErrorGatherer(errorList));
159
            }
160
        }
161

    
162
        /// <summary>
163
        /// Compile a schema, delivered using an XmlReader. The resulting schema components are added
164
        /// to the cache.
165
        /// </summary>
166
        /// <remarks>
167
        /// The <c>XmlReader</c> is responsible for parsing the document; this method builds a tree
168
        /// representation of the document (in an internal Saxon format) and compiles it.
169
        /// If the <c>XmlReader</c> is an <c>XmlTextReader</c>, Saxon will set its <c>Normalization</c>
170
        /// property to true, and will wrap it in a (non-validating) <c>XmlValidatingReader</c> to ensure
171
        /// that entity references are expanded.
172
        /// </remarks>
173
        /// <param name="reader">The XmlReader (that is, the XML parser) used to supply the source schema document</param>
174

    
175
        public void Compile(XmlReader reader)
176
        {
177
            if (reader is XmlTextReader)
178
            {
179
                ((XmlTextReader)reader).Normalization = true;
180
                reader = new XmlValidatingReader(reader);
181
                ((XmlValidatingReader)reader).ValidationType = ValidationType.None;
182
            }
183
            PullProvider pp = new DotNetPullProvider(reader);
184
            pp.setPipelineConfiguration(config.makePipelineConfiguration());
185
            // pp = new PullTracer(pp);  /* diagnostics */
186
            PullSource ss = new PullSource(pp);
187
            ss.setSystemId(reader.BaseURI);
188
            if (errorList == null)
189
            {
190
                config.addSchemaSource(ss);
191
            }
192
            else
193
            {
194
                config.addSchemaSource(ss, new ErrorGatherer(errorList));
195
            }
196
        }
197

    
198
        /// <summary>
199
        /// Compile a schema document, located at an XdmNode. This may be a document node whose
200
        /// child is an <c>xs:schema</c> element, or it may be
201
        /// the <c>xs:schema</c> element itself. The resulting schema components are added
202
        /// to the cache.
203
        /// </summary>
204
        /// <param name="node">The document node or the outermost element node of a schema document.</param>
205

    
206
        public void Compile(XdmNode node)
207
        {
208
            ErrorGatherer eg = null;
209
            if (errorList != null)
210
            {
211
                eg = new ErrorGatherer(errorList);
212
            }
213
            try
214
            {
215
                config.readInlineSchema((NodeInfo)node.value, null, eg);
216
            }
217
            catch (SchemaException e)
218
            {
219
                throw new StaticError(e);
220
            }
221
        }
222

    
223
        /// <summary>
224
        /// Create a new <c>SchemaValidator</c>, which may be used for validating instance
225
        /// documents.
226
        /// </summary>
227
        /// <remarks>
228
        /// <para>The <c>SchemaValidator</c> uses the cache of schema components held by the
229
        /// <c>SchemaManager</c>. It may also add new components to this cache (for example,
230
        /// when the instance document references a schema using <c>xsi:schemaLocation</c>).
231
        /// It is also affected by changes to the schema cache that occur after the 
232
        /// <c>SchemaValidator</c> is created.</para>
233
        /// <para>When schema components are used for validating instance documents (or for compiling
234
        /// schema-aware queries and stylesheets) they are <i>sealed</i> to prevent subsequent modification.
235
        /// The modifications disallowed once a component is sealed include adding to the substitution group
236
        /// of an element declaration, adding subtypes derived by extension to an existing complex type, and
237
        /// use of <c>&lt;xs:redefine&gt;</c></para>
238
        /// </remarks>
239

    
240
        public SchemaValidator NewSchemaValidator()
241
        {
242
            return new SchemaValidator(config);
243
        }
244

    
245
    }
246

    
247
    /// <summary>
248
    /// A <c>SchemaValidator</c> is an object that is used for validating instance documents
249
    /// against a schema. The schema consists of the collection of schema components that are
250
    /// available within the schema cache maintained by the <c>SchemaManager</c>, together with
251
    /// any additional schema components located during the course of validation by means of an
252
    /// <c>xsl:schemaLocation</c> or <c>xsi:noNamespaceSchemaLocation</c> attribute within the
253
    /// instance document.
254
    /// </summary>
255
    /// <remarks>
256
    /// If validation fails, an exception is thrown. If validation succeeds, the validated
257
    /// document can optionally be written to a specified destination. This will be a copy of
258
    /// the original document, augmented with default values for absent elements and attributes,
259
    /// and carrying type annotations derived from the schema processing. Saxon does not deliver
260
    /// the full PSVI as described in the XML schema specifications, only the subset of the
261
    /// PSVI properties featured in the XDM data model.
262
    /// </remarks>    
263

    
264
    [Serializable]
265
    public class SchemaValidator
266
    {
267

    
268
        private JConfiguration config;
269
        private bool lax = false;
270
        private Source source;
271
        private XmlDestination destination;
272
        private IList errorList = null;
273
        private bool useXsiSchemaLocation;
274

    
275
        // internal constructor
276

    
277
        internal SchemaValidator(JConfiguration config)
278
        {
279
            this.config = config;
280
            Object obj = config.getConfigurationProperty(FeatureKeys.USE_XSI_SCHEMA_LOCATION);
281
            useXsiSchemaLocation = ((java.lang.Boolean)obj).booleanValue();
282
        }
283

    
284
        /// <summary>
285
        /// The validation mode may be either strict or lax. The default is strict;
286
        /// this property is set to indicate that lax validation is required. With strict validation,
287
        /// validation fails if no element declaration can be located for the outermost element. With lax
288
        /// validation, the absence of an element declaration results in the content being considered valid.
289
        /// </summary>
290

    
291
        public bool IsLax
292
        {
293
            get { return lax; }
294
            set { lax = value; }
295
        }
296

    
297
        /// <summary>
298
        /// This property defines whether the schema processor will recognize, and attempt to
299
        /// dereference, any <c>xsi:schemaLocation</c> and <c>xsi:noNamespaceSchemaLocation</c>
300
        /// attributes encountered in the instance document. The default value is true.
301
        /// </summary>
302

    
303
        public Boolean UseXsiSchemaLocation
304
        {
305
            get
306
            {
307
                return useXsiSchemaLocation;
308
            }
309
            set
310
            {
311
                useXsiSchemaLocation = value;
312
            }
313
        }
314

    
315
        /// <summary>
316
        /// Supply the instance document to be validated in the form of a Stream
317
        /// </summary>
318
        /// <param name="source">A stream containing the XML document to be parsed
319
        /// and validated. This stream will be consumed by the validation process,
320
        /// but it will not be closed after use: that is the responsibility of the
321
        /// caller.</param>
322
        /// <param name="baseUri">The base URI to be used for resolving any relative
323
        /// references, for example a reference to an <c>xsi:schemaLocation</c></param>                  
324

    
325
        public void SetSource(Stream source, Uri baseUri)
326
        {
327
            StreamSource ss = new StreamSource(new DotNetInputStream(source));
328
            ss.setSystemId(baseUri.ToString());
329
            this.source = ss;
330
        }
331

    
332
        /// <summary>
333
        /// Supply the instance document to be validated in the form of a Uri reference
334
        /// </summary>
335
        /// <param name="uri">URI of the document to be validated</param>                  
336

    
337
        public void SetSource(Uri uri)
338
        {
339
            StreamSource ss = new StreamSource(uri.ToString());
340
            AugmentedSource aug = AugmentedSource.makeAugmentedSource(ss);
341
            aug.setPleaseCloseAfterUse(true);
342
            this.source = aug;
343
        }
344

    
345
        /// <summary>
346
        /// Supply the instance document to be validated, in the form of an XmlReader.
347
        /// </summary>
348
        /// <remarks>
349
        /// The XmlReader is responsible for parsing the document; this method validates it.
350
        /// </remarks>
351
        /// <param name="reader">The <c>XmlReader</c> used to read and parse the instance
352
        /// document being validated. This is used as supplied. For conformance, use of a
353
        /// plain <c>XmlTextReader</c> is discouraged, because it does not expand entity
354
        /// references. This may cause validation failures.
355
        /// </param>
356

    
357
        public void SetSource(XmlReader reader)
358
        {
359
            PullProvider pp = new DotNetPullProvider(reader);
360
            PipelineConfiguration pipe = config.makePipelineConfiguration();
361
            pipe.setUseXsiSchemaLocation(useXsiSchemaLocation);
362
            pp.setPipelineConfiguration(pipe);
363
            // pp = new PullTracer(pp);  /* diagnostics */
364
            PullSource psource = new PullSource(pp);
365
            psource.setSystemId(reader.BaseURI);
366
            this.source = psource;
367
        }
368

    
369
        /// <summary>
370
        /// Supply the instance document to be validated in the form of an XdmNode
371
        /// </summary>
372
        /// <remarks>
373
        /// <para>The supplied node must be either a document node or an element node.
374
        /// If an element node is supplied, then the subtree rooted at this element is
375
        /// validated as if it were a complete document: that is, it must not only conform
376
        /// to the structure required of that element, but any referential constraints
377
        /// (keyref, IDREF) must be satisfied within that subtree.
378
        /// </para>
379
        /// </remarks>
380
        /// <param name="source">The document or element node at the root of the tree
381
        /// to be validated</param>        
382

    
383
        public void SetSource(XdmNode source)
384
        {
385
            this.source = (NodeInfo)source.value;
386
        }
387

    
388
        /// <summary>
389
        /// Supply the destination to hold the validated document. If no destination
390
        /// is supplied, the validated document is discarded.
391
        /// </summary>
392
        /// <remarks>
393
        /// The destination differs from the source in that (a) default values of missing
394
        /// elements and attributes are supplied, and (b) the typed values of elements and
395
        /// attributes are available. However, typed values can only be accessed if the result
396
        /// is represented using the XDM data model, that is, if the destination is supplied
397
        /// as an XdmDestination.
398
        /// </remarks>
399

    
400
        public void SetDestination(XmlDestination destination)
401
        {
402
            this.destination = destination;
403
        }
404

    
405
        /// <summary>
406
        /// List of errors. The caller may supply an empty list before calling Compile;
407
        /// the processor will then populate the list with error information obtained during
408
        /// the schema compilation. Each error will be included as an object of type StaticError.
409
        /// If no error list is supplied by the caller, error information will be written to
410
        /// the standard error stream.
411
        /// </summary>
412
        /// <remarks>
413
        /// <para>By supplying a custom List with a user-written add() method, it is possible to
414
        /// intercept error conditions as they occur.</para>
415
        /// <para>Note that this error list is used only for errors detected while 
416
        /// using the schema to validate a source document. It is not used to report errors
417
        /// in the schema itself.</para>
418
        /// </remarks>
419

    
420
        public IList ErrorList
421
        {
422
            set
423
            {
424
                errorList = value;
425
            }
426
            get
427
            {
428
                return errorList;
429
            }
430
        }
431

    
432

    
433
        /// <summary>
434
        /// Run the validation of the supplied source document, optionally
435
        /// writing the validated document to the supplied destination.
436
        /// </summary>
437

    
438
        public void Run()
439
        {
440
            AugmentedSource aug = AugmentedSource.makeAugmentedSource(source);
441
            aug.setSchemaValidationMode(lax ? Validation.LAX : Validation.STRICT);
442
            JReceiver receiver;
443
            if (destination == null)
444
            {
445
                receiver = new Sink();
446
            }
447
            else if (destination is Serializer)
448
            {
449
                receiver = ((Serializer)destination).GetReceiver(config);
450
            }
451
            else
452
            {
453
                Result result = destination.GetResult();
454
                if (result is JReceiver)
455
                {
456
                    receiver = (JReceiver)result;
457
                }
458
                else
459
                {
460
                    throw new ArgumentException("Unknown type of destination");
461
                }
462
            }
463
            PipelineConfiguration pipe = config.makePipelineConfiguration();
464
            pipe.setUseXsiSchemaLocation(useXsiSchemaLocation);
465
            if (errorList != null)
466
            {
467
                pipe.setErrorListener(new ErrorGatherer(errorList));
468
            }
469
            new Sender(pipe).send(aug, receiver, true);
470
        }
471

    
472
    }
473

    
474

    
475
    /// <summary>
476
    /// The SchemaResolver is a user-supplied class used for resolving references to
477
    /// schema documents. It applies to references from one schema document to another
478
    /// appearing in <c>xs:import</c>, <c>xs:include</c>, and <c>xs:redefine</c>; to
479
    /// references from an instance document to a schema in <c>xsi:schemaLocation</c> and
480
    /// <c>xsi:noNamespaceSchemaLocation</c>, to <c>xsl:import-schema</c> in XSLT, and to
481
    /// the <c>import schema</c> declaration in XQuery.
482
    /// </summary>
483

    
484

    
485
    public interface SchemaResolver
486
    {
487

    
488
        /// <summary>
489
        /// Given a targetNamespace and a set of location hints, return a set of schema documents.
490
        /// </summary>
491
        /// <param name="targetNamespace">The target namespace of the required schema components</param>
492
        /// <param name="baseUri">The base URI of the module containing the reference to a schema document
493
        /// declaration</param>
494
        /// <param name="locationHints">The sequence of URIs (if any) listed as location hints.
495
        /// In most cases there will only be one; but the <c>import schema</c> declaration in
496
        /// XQuery permits several.</param>
497
        /// <returns>A set of absolute Uris identifying the query modules to be loaded. There is no requirement
498
        /// that these correspond one-to-one with the URIs defined in the <c>locationHints</c>. The 
499
        /// returned URIs will be dereferenced by calling the <c>GetEntity</c> method.
500
        /// </returns>
501

    
502
        Uri[] GetSchemaDocuments(String targetNamespace, Uri baseUri, String[] locationHints);
503

    
504
        /// <summary>
505
        /// Dereference a URI returned by <c>GetModules</c> to retrieve a <c>Stream</c> containing
506
        /// the actual XML schema document.
507
        /// </summary>
508
        /// <param name="absoluteUri">A URI returned by the <code>GetSchemaDocuments</code> method.</param>
509
        /// <returns>Either a <c>Stream</c> or a <c>String</c> containing the query text. 
510
        /// The supplied URI will be used as the base URI of the query module.</returns>
511

    
512
        Object GetEntity(Uri absoluteUri);
513

    
514
    }
515

    
516
    // internal class that wraps a (.NET) QueryResolver to create a (Java) SchemaURIResolver
517

    
518
    internal class DotNetSchemaURIResolver : net.sf.saxon.type.SchemaURIResolver
519
    {
520

    
521
        internal SchemaResolver resolver;
522

    
523
        public DotNetSchemaURIResolver(SchemaResolver resolver)
524
        {
525
            this.resolver = resolver;
526
        }
527

    
528
        public Source[] resolve(String targetNamespace, String baseURI, String[] locations)
529
        {
530
            Uri baseU = (baseURI == null ? null : new Uri(baseURI));
531
            Uri[] modules = resolver.GetSchemaDocuments(targetNamespace, baseU, locations);
532
            StreamSource[] ss = new StreamSource[modules.Length];
533
            for (int i = 0; i < ss.Length; i++)
534
            {
535
                ss[i] = new StreamSource();
536
                ss[i].setSystemId(modules[i].ToString());
537
                Object doc = resolver.GetEntity(modules[i]);
538
                if (doc is Stream)
539
                {
540
                    ss[i].setInputStream(new DotNetInputStream((Stream)doc));
541
                }
542
                else if (doc is String)
543
                {
544
                    ss[i].setReader(new DotNetReader(new StringReader((String)doc)));
545
                }
546
                else
547
                {
548
                    throw new ArgumentException("Invalid response from GetEntity()");
549
                }
550
            }
551
            return ss;
552
        }
553
    }
554

    
555

    
556

    
557

    
558

    
559

    
560
}
561

    
562
//
563
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
564
// you may not use this file except in compliance with the License. You may obtain a copy of the
565
// License at http://www.mozilla.org/MPL/
566
//
567
// Software distributed under the License is distributed on an "AS IS" basis,
568
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
569
// See the License for the specific language governing rights and limitations under the License.
570
//
571
// The Original Code is: all this file.
572
//
573
// The Initial Developer of the Original Code is Michael H. Kay.
574
//
575
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
576
//
577
// Contributor(s): none.
578
//
(5-5/8)