KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jdom > contrib > schema > Schema


1 /*--
2
3  $Id: Schema.java,v 1.4 2004/09/07 06:39:46 jhunter Exp $
4
5  Copyright (C) 2003-2004 Jason Hunter & Brett McLaughlin.
6  All rights reserved.
7
8  Redistribution and use in source and binary forms, with or without
9  modification, are permitted provided that the following conditions
10  are met:
11
12  1. Redistributions of source code must retain the above copyright
13     notice, this list of conditions, and the following disclaimer.
14
15  2. Redistributions in binary form must reproduce the above copyright
16     notice, this list of conditions, and the disclaimer that follows
17     these conditions in the documentation and/or other materials
18     provided with the distribution.
19
20  3. The name "JDOM" must not be used to endorse or promote products
21     derived from this software without prior written permission. For
22     written permission, please contact <request_AT_jdom_DOT_org>.
23
24  4. Products derived from this software may not be called "JDOM", nor
25     may "JDOM" appear in their name, without prior written permission
26     from the JDOM Project Management <request_AT_jdom_DOT_org>.
27
28  In addition, we request (but do not require) that you include in the
29  end-user documentation provided with the redistribution and/or in the
30  software itself an acknowledgement equivalent to the following:
31      "This product includes software developed by the
32       JDOM Project (http://www.jdom.org/)."
33  Alternatively, the acknowledgment may be graphical using the logos
34  available at http://www.jdom.org/images/logos.
35
36  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
40  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  SUCH DAMAGE.
48
49  This software consists of voluntary contributions made by many
50  individuals on behalf of the JDOM Project and was originally
51  created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
52  Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
53  on the JDOM Project, please see <http://www.jdom.org/>.
54
55  */

56
57 package org.jdom.contrib.schema;
58
59 import java.io.File JavaDoc;
60 import java.io.InputStream JavaDoc;
61 import java.io.FileInputStream JavaDoc;
62 import java.io.Reader JavaDoc;
63 import java.io.IOException JavaDoc;
64 import java.util.List JavaDoc;
65 import java.util.ArrayList JavaDoc;
66 import java.util.LinkedList JavaDoc;
67
68 import org.xml.sax.InputSource JavaDoc;
69 import org.xml.sax.Locator JavaDoc;
70 import org.xml.sax.XMLReader JavaDoc;
71 import org.xml.sax.SAXException JavaDoc;
72 import org.xml.sax.SAXParseException JavaDoc;
73 import org.xml.sax.helpers.XMLFilterImpl JavaDoc;
74
75 import org.iso_relax.verifier.Verifier;
76 import org.iso_relax.verifier.VerifierFactory;
77 import org.iso_relax.verifier.VerifierConfigurationException;
78
79 import org.jdom.Document;
80 import org.jdom.Element;
81 import org.jdom.JDOMException;
82 import org.jdom.output.SAXOutputter;
83 import org.jdom.output.JDOMLocator;
84
85 /**
86  * The compiled representation of a schema definition capable of
87  * performing in-memory validation of JDOM documents and elements.
88  * <p>
89  * This class relies on
90  * <a HREF="http://iso-relax.sourceforge.net/JARV/">JARV</a> (Java
91  * API for RELAX Verifiers) and requires an implementation of this
92  * API at runtime, such as Sun's
93  * <a HREF="http://wwws.sun.com/software/xml/developers/multischema/">Multi-Schema
94  * Validator</a>.</p>
95  * <p>
96  * To validate a document against a W3C XML Schema definition:</p>
97  * <pre>
98  * import org.jdom.contrib.schema.Schema;
99  *
100  * String uri = &lt;The URL of the schema document&gt;;
101  * Document doc = &lt;a JDOM document&gt;;
102  *
103  * Schema schema = Schema.parse(uri, Schema.W3C_XML_SCHEMA);
104  * List errors = schema.validate(doc);
105  * if (errors != null) {
106  * // Validation errors
107  * for (Iterator i=errors.iterator(); i.hasNext(); ) {
108  * ValidationError e = (ValidationError)(i.next());
109  * System.out.println(e);
110  * }
111  * }
112  * // Else: No error, document is valid.
113  * </pre>
114  * <p>
115  * The current limitations are those of JARV, i&#46;e&#46; no support for
116  * validating a document against multiple schemas. This can be work around
117  * for elements (calling validate(Element) on another Schema) but not for
118  * attributes.</p>
119  *
120  * @author Laurent Bihanic
121  */

122 public class Schema {
123
124     /**
125      * Type for W3C XML Schema definitions.
126      */

127     public final static Type W3C_XML_SCHEMA =
128             new Type("W3C XML Schema", "http://www.w3.org/2001/XMLSchema");
129     /**
130      * Type for RELAX NG schema definitions.
131      */

132     public final static Type RELAX_NG =
133             new Type("RELAX NG", "http://relaxng.org/ns/structure/0.9");
134     /**
135      * Type for RELAX Core schema definitions.
136      */

137     public final static Type RELAX_CORE =
138             new Type("RELAX Core", "http://www.xml.gr.jp/xmlns/relaxCore");
139     /**
140      * Type for RELAX Namespace schema definitions.
141      */

142     public final static Type RELAX_NAMESPACE =
143             new Type("RELAX Namespace",
144                      "http://www.xml.gr.jp/xmlns/relaxNamespace");
145     /**
146      * Type for TREX schema definitions.
147      */

148     public final static Type TREX =
149             new Type("TREX", "http://www.thaiopensource.com/trex");
150
151     /**
152      * The URI of the schema document, if known.
153      */

154     private final String JavaDoc uri;
155
156     /**
157      * The schema type.
158      */

159     private final Type type;
160
161     /**
162      * The JARV compiled schema.
163      */

164     private final org.iso_relax.verifier.Schema compiledSchema;
165
166     /**
167      * Compiles a schema definition.
168      *
169      * @param source the SAX input source to read the schema
170      * definition from.
171      * @param type the schema type.
172      *
173      * @throws JDOMException if the schema document can not be
174      * parsed according to the specfied type.
175      * @throws IOException if an I/O error occurred while reading
176      * the schema document.
177      */

178     private Schema(InputSource JavaDoc source, Type type)
179             throws JDOMException, IOException JavaDoc {
180
181         if ((source == null) || (type == null)) {
182             throw new IllegalArgumentException JavaDoc("source/type/compiledSchema");
183         }
184         this.uri = source.getSystemId();
185         this.type = type;
186
187         try {
188             VerifierFactory vf = VerifierFactory.newInstance(type.getLanguage());
189
190             this.compiledSchema = vf.compileSchema(source);
191         }
192         catch (IOException JavaDoc e) {
193             throw e;
194         }
195         catch (Exception JavaDoc e) {
196             throw new JDOMException("Failed to parse schema \"" + this.uri +
197                                     "\": " + e.getMessage(), e);
198         }
199     }
200
201     /**
202      * Returns the location of the schema document, if known.
203      *
204      * @return the location of the schema document or
205      * <code>null</code> if inknown.
206      */

207     public String JavaDoc getURI() {
208         return this.uri;
209     }
210
211     /**
212      * Returns the schema type.
213      *
214      * @return the schema type.
215      */

216     public Type getType() {
217         return this.type;
218     }
219
220     /**
221      * Allocates an JARV <code>Verifier</code> object for
222      * validating against this schema.
223      *
224      * @return an JARV <code>Verifier</code> configured with this
225      * schema.
226      *
227      * @throws JDOMException if the verifier allocation failed.
228      */

229     private Verifier newVerifier() throws JDOMException {
230         try {
231             return this.compiledSchema.newVerifier();
232         }
233         catch (VerifierConfigurationException e) {
234             throw new JDOMException(
235                     "Failed to allocate schema verifier: " + e.getMessage(), e);
236         }
237     }
238
239     /**
240      * Validates a JDOM document against this schema.
241      *
242      * @param doc the JDOM document to validate.
243      *
244      * @return a list of {@link ValidationError} objects or
245      * <code>null</code> if the document is compliant with
246      * this schema.
247      *
248      * @throws JDOMException if errors were encountered that
249      * prevented the validation to proceed.
250      */

251     public List JavaDoc validate(Document doc) throws JDOMException {
252         ValidationErrorHandler errorHandler = new ValidationErrorHandler();
253         try {
254             Verifier verifier = this.newVerifier();
255             verifier.setErrorHandler(errorHandler);
256
257             errorHandler.setContentHandler(verifier.getVerifierHandler());
258             new SAXOutputter(errorHandler).output(doc);
259         }
260         catch (SAXException JavaDoc e) { /* Fatal validation error encountered. */
261         }
262
263         // Retrieve validation errors, if any.
264
return errorHandler.getErrors();
265     }
266
267     /**
268      * Validates a JDOM element against this schema.
269      *
270      * @param element the JDOM element to validate.
271      *
272      * @return a list of {@link ValidationError} objects or
273      * <code>null</code> if the element is compliant with
274      * this schema.
275      *
276      * @throws JDOMException if errors were encountered that
277      * prevented the validation to proceed.
278      */

279     public List JavaDoc validate(Element element) throws JDOMException {
280         ValidationErrorHandler errorHandler = new ValidationErrorHandler();
281         try {
282             Verifier verifier = this.newVerifier();
283             verifier.setErrorHandler(errorHandler);
284
285             List JavaDoc nodes = new ArrayList JavaDoc();
286             nodes.add(element);
287
288             errorHandler.setContentHandler(verifier.getVerifierHandler());
289             new SAXOutputter(errorHandler).output(nodes);
290         }
291         catch (SAXException JavaDoc e) { /* Fatal validation error encountered. */
292         }
293
294         // Retrieve validation errors, if any.
295
return errorHandler.getErrors();
296     }
297
298     /**
299      * Parses a schema definition located at the specified URI
300      * according to the specified schema type and returns a compiled
301      * schema object.
302      *
303      * @param uri the location of the schema document.
304      * @param type the schema type.
305      *
306      * @return the compiled schema.
307      *
308      * @throws JDOMException if the schema document can not be
309      * parsed according to the specfied type.
310      * @throws IOException if an I/O error occurred while reading
311      * the schema document.
312      */

313     public static Schema parse(String JavaDoc uri, Type type)
314             throws JDOMException, IOException JavaDoc {
315         return parse(new InputSource JavaDoc(uri), type);
316     }
317
318     /**
319      * Parses a schema definition from the specified byte stream
320      * according to the specified schema type and returns a compiled
321      * schema object.
322      *
323      * @param byteStream the byte stream to read the schema
324      * definition from.
325      * @param type the schema type.
326      * @param uri the location of the schema document
327      * (optional).
328      *
329      * @return the compiled schema.
330      *
331      * @throws JDOMException if the schema document can not be
332      * parsed according to the specfied type.
333      * @throws IOException if an I/O error occurred while reading
334      * the schema document.
335      */

336     public static Schema parse(InputStream JavaDoc byteStream, Type type, String JavaDoc uri)
337             throws JDOMException, IOException JavaDoc {
338         InputSource JavaDoc source = new InputSource JavaDoc(byteStream);
339         source.setSystemId(uri);
340
341         return parse(source, type);
342     }
343
344     /**
345      * Parses a schema definition from the specified character stream
346      * according to the specified schema type and returns a compiled
347      * schema object.
348      *
349      * @param reader the character stream to read the schema
350      * definition from.
351      * @param type the schema type.
352      * @param uri the location of the schema document
353      * (optional).
354      *
355      * @return the compiled schema.
356      *
357      * @throws JDOMException if the schema document can not be
358      * parsed according to the specfied type.
359      * @throws IOException if an I/O error occurred while reading
360      * the schema document.
361      */

362     public static Schema parse(Reader JavaDoc reader, Type type, String JavaDoc uri)
363             throws JDOMException, IOException JavaDoc {
364         InputSource JavaDoc source = new InputSource JavaDoc(reader);
365         source.setSystemId(uri);
366
367         return parse(source, type);
368     }
369
370     /**
371      * Parses a schema definition from the specified file
372      * according to the specified schema type and returns a compiled
373      * schema object.
374      *
375      * @param file the file to read the schema definition from.
376      * @param type the schema type.
377      *
378      * @return the compiled schema.
379      *
380      * @throws JDOMException if the schema document can not be
381      * parsed according to the specfied type.
382      * @throws IOException if an I/O error occurred while reading
383      * the schema document.
384      */

385     public static Schema parse(File JavaDoc file, Type type)
386             throws JDOMException, IOException JavaDoc {
387         InputSource JavaDoc source = new InputSource JavaDoc(new FileInputStream JavaDoc(file));
388         source.setSystemId(file.getAbsolutePath());
389
390         return parse(source, type);
391     }
392
393     /**
394      * Parses a schema definition from the specified SAX input source
395      * according to the specified schema type and returns a compiled
396      * schema object.
397      *
398      * @param source the SAX inout source to read the schema
399      * definition from.
400      * @param type the schema type.
401      *
402      * @return the compiled schema.
403      *
404      * @throws JDOMException if the schema document can not be
405      * parsed according to the specfied type.
406      * @throws IOException if an I/O error occurred while reading
407      * the schema document.
408      */

409     public static Schema parse(InputSource JavaDoc source, Type type)
410             throws JDOMException, IOException JavaDoc {
411         return new Schema(source, type);
412     }
413
414
415     /**
416      * A SAX XML filter implementation to capture the document locator
417      * and make all validation errors and warnings available once the
418      * validation is complete.
419      */

420     private static final class ValidationErrorHandler extends XMLFilterImpl JavaDoc {
421         /** The list of validation errors. */
422         private List JavaDoc errors = new LinkedList JavaDoc();
423         /** The JDOM locator object provided by SAXOutputter. */
424         private JDOMLocator locator = null;
425
426         /**
427          * Constructs a new ValidationErrorHandler XML filter with no
428          * parent.
429          */

430         public ValidationErrorHandler() {
431             super();
432         }
433
434         /**
435          * Constructs a new ValidationErrorHandler XML filter with the
436          * specified parent.
437          *
438          * @param parent the parent XMLReader or XMLFilter.
439          */

440         public ValidationErrorHandler(XMLReader JavaDoc parent) {
441             super(parent);
442         }
443
444         /**
445          * Returns the list of validation errors reported during
446          * document validation.
447          *
448          * @return the list of validation errors or <code>null</code>
449          * if the document is valid.
450          */

451         public List JavaDoc getErrors() {
452             return (this.errors.size() == 0) ? null : this.errors;
453         }
454
455         /**
456          * Returns the JDOM node currently being ouputted by
457          * SAXOuputter.
458          *
459          * @return the current JDOM node.
460          */

461         private Object JavaDoc getCurrentNode() {
462             return (this.locator != null) ? this.locator.getNode() : null;
463         }
464
465         /**
466          * <i>[ContentHandler interface support]</i> Sets the locator
467          * object for locating the origin of SAX document events.
468          *
469          * @param locator an object that can return the location of
470          * any SAX document event.
471          */

472         public void setDocumentLocator(Locator JavaDoc locator) {
473             if (locator instanceof JDOMLocator) {
474                 this.locator = (JDOMLocator) locator;
475             }
476         }
477
478         /**
479          * <i>[ErrorHandler interface support]</i> Receives
480          * notification of a non-recoverable error.
481          *
482          * @param e the error information encapsulated in a SAX
483          * parse exception.
484          *
485          * @throws SAXException any SAX exception, possibly wrapping
486          * another exception.
487          */

488         public void fatalError(SAXParseException JavaDoc e) throws SAXException JavaDoc {
489             this.errors.add(new ValidationError(ValidationError.FATAL,
490                                                 e.getMessage(), this.getCurrentNode()));
491             throw e;
492         }
493
494         /**
495          * <i>[ErrorHandler interface support]</i> Receives
496          * notification of a recoverable error.
497          *
498          * @param e the error information encapsulated in a SAX
499          * parse exception.
500          *
501          * @throws SAXException any SAX exception, possibly wrapping
502          * another exception.
503          */

504         public void error(SAXParseException JavaDoc e) throws SAXException JavaDoc {
505             this.errors.add(new ValidationError(ValidationError.ERROR,
506                                                 e.getMessage(), this.getCurrentNode()));
507         }
508
509         /**
510          * <i>[ErrorHandler interface support]</i> Receives
511          * notification of a warning.
512          *
513          * @param e the warning information encapsulated in a SAX
514          * parse exception.
515          *
516          * @throws SAXException any SAX exception, possibly wrapping
517          * another exception.
518          */

519         public void warning(SAXParseException JavaDoc e) throws SAXException JavaDoc {
520             this.errors.add(new ValidationError(ValidationError.WARNING,
521                                                 e.getMessage(), this.getCurrentNode()));
522         }
523     }
524
525
526     /**
527      * Class to support type-safe enumeration design pattern to
528      * represent schema types
529      */

530     public static final class Type {
531         /** Schema type name. */
532         private final String JavaDoc name;
533         /** JARV schema type identifier. */
534         private final String JavaDoc language;
535
536         /**
537          * Type constructor, private on purpose.
538          *
539          * @param name the schema type printable name.
540          * @param language the unique identifier for the schema
541          * type (URI).
542          */

543         protected Type(String JavaDoc name, String JavaDoc language) {
544             this.name = name;
545             this.language = language;
546         }
547
548         /**
549          * Returns the printable name of this schema type.
550          *
551          * @return the schema type name.
552          */

553         public String JavaDoc getName() {
554             return this.name;
555         }
556
557         /**
558          * Returns the URI that uniquemy identifies this schema type.
559          *
560          * @return the schema type identifier.
561          */

562         public String JavaDoc getLanguage() {
563             return this.language;
564         }
565
566         /**
567          * Returns a unique identifier for this type.
568          *
569          * @return a unique identifier for this type.
570          *
571          * @see java.lang.Object#hashCode()
572          */

573         public int hashCode() {
574             return this.language.hashCode();
575         }
576
577         /**
578          * Returns a string representation of this type suitable for
579          * debugging and diagnosis.
580          *
581          * @return a string representation of this type.
582          *
583          * @see java.lang.Object#toString()
584          */

585         public String JavaDoc toString() {
586             return this.language;
587         }
588
589         /**
590          * Tests for type equality. This is only necessary to handle
591          * cases where two <code>Type</code> objects are loaded by
592          * different class loaders.
593          *
594          * @param o the object compared for equality to this type.
595          *
596          * @return <code>true</code> if and only if <code>o</code>
597          * represents the same type as this object.
598          *
599          * @see java.lang.Object#equals(Object)
600          */

601         public boolean equals(Object JavaDoc o) {
602             return ((o == this) ||
603                     ((o != null) && (this.hashCode() == o.hashCode()) &&
604                     (this.getClass().getName().equals(o.getClass().getName()))));
605         }
606     }
607 }
608
609
Popular Tags