/** * Redistribution and use of this software and associated documentation * ("Software"), with or without modification, are permitted provided * that the following conditions are met: * * 1. Redistributions of source code must retain copyright * statements and notices. Redistributions must also contain a * copy of this document. * * 2. Redistributions in binary form must reproduce the * above copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. The name "Exolab" must not be used to endorse or promote * products derived from this Software without prior written * permission of Intalio, Inc. For written permission, * please contact info@exolab.org. * * 4. Products derived from this Software may not be called "Exolab" * nor may "Exolab" appear in their names without prior written * permission of Intalio, Inc. Exolab is a registered * trademark of Intalio, Inc. * * 5. Due credit should be given to the Exolab Project * (http://www.exolab.org/). * * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * Copyright 1999-2002 (C) Intalio, Inc. All Rights Reserved. * * $Id$ */ package org.exolab.castor.xml.schema.reader; import java.io.IOException; import java.io.Reader; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.exolab.castor.net.URIException; import org.exolab.castor.net.URILocation; import org.exolab.castor.net.URIResolver; import org.exolab.castor.util.NestedIOException; import org.exolab.castor.xml.XMLException; import org.exolab.castor.xml.schema.Schema; import org.exolab.castor.xml.schema.SchemaContext; import org.exolab.castor.xml.schema.SchemaContextImpl; import org.xml.sax.EntityResolver; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.Parser; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; /** * A class for reading XML Schemas. * * @author <a href="mailto:kvisco@intalio.com">Keith Visco</a> * @version $Revision$ $Date: 2004-10-05 14:27:10 -0600 (Tue, 05 Oct 2004) $ **/ public class SchemaReader { /** * The {@link Log} instance to use. */ private static final Log LOG = LogFactory.getLog(SchemaReader.class); /** * The Castor XML Context... mother of all. */ private SchemaContext _schemaContext; /** * XML Parser instance */ private Parser _parser = null; /** * SAX InputSource to Schema */ private InputSource _source = null; /** * SAX EntityResolver */ private EntityResolver _resolver = null; /** * SAX ErrorHandler */ private ErrorHandler _errorHandler = null; /** * The resolver to be used for resolving href */ private URIResolver _uriResolver; /** * A flag that indicates that included schemas should be cached * instead of being inlined [which is the default behavior as specified * by the XML Schema Specification]. * */ private boolean _cacheIncludedSchemas = false; private Schema _schema = null; private boolean _validate = true; /** * Old fashion style to create a SchemaReader instance. * * @throws IOException * if no Parser is available */ private void init() throws IOException { // -- get default parser from Configuration _schemaContext = new SchemaContextImpl(); Parser parser = _schemaContext.getParser(); if (parser == null) { String message = "fatal error: unable to create SAX parser."; LOG.warn(message); throw new IOException(message); } _parser = parser; } // -- SchemaReader /** * Creates a new SchemaReader for the given InputSource * * @param source * the InputSource to read the Schema from. */ public SchemaReader(InputSource source) throws IOException { init(); if (source == null) throw new IllegalArgumentException("InputSource cannot be null"); _source = source; } //-- SchemaReader /** * Creates a new SchemaReader for the given Reader * * @param reader the Reader to read the Schema from. * @param filename for reporting errors. **/ public SchemaReader(Reader reader, String filename) throws IOException { init(); if (reader == null) { String err = "The argument 'reader' must not be null."; throw new IllegalArgumentException(err); } _source = new InputSource(reader); if (filename == null) filename = reader.toString(); _source.setPublicId(filename); } //-- SchemaReader /** * Creates a new SchemaReader for the given URL * * @param url the URL string **/ public SchemaReader(String url) throws IOException { init(); if (url == null) { String err = "The argument 'url' must not be null."; throw new IllegalArgumentException(err); } _source = new InputSource(url); } //-- SchemaReader /** * New style how to create a SchemaReader instance, requiring that {@link SchemaContext} * and InputSource are set before calling {@link read}. */ public SchemaReader() { super(); } /** * To set the {@link SchemaContext} to be used. Also resets the parser as it depends * of the {@link SchemaContext}. * @param schemaContext the {@link SchemaContext} to be used */ public void setSchemaContext(final SchemaContext schemaContext) { this._schemaContext = schemaContext; Parser p = _schemaContext.getParser(); if (p != null) { _parser = p; } } /** * A different way to create a SchemaReader by using an empty constructor and * setting the InputSource afterwards. * @param inputSource the InputSource to read the schema from */ public void setInputSource(final InputSource inputSource) { if (inputSource == null) { String message = "InputSource must not be null"; LOG.warn(message); throw new IllegalArgumentException(message); } _source = inputSource; } /** * Reads the Schema from the source and returns the Schema * object model. * * <BR /> * <B>Note:</B> Subsequent calls to this method will simply * return a cached copy of the Schema object. To read a new * Schema object, create a new Reader. * * @return the new Schema created from the source of this SchemaReader **/ public Schema read() throws IOException { if (_schema != null) { return _schema; } if (_parser == null) { String message = "Required Parser was not specified"; LOG.warn(message); throw new IllegalStateException(message); } if (_source == null) { String message = "Required Source was not specified"; LOG.warn(message); throw new IllegalStateException(message); } SchemaUnmarshaller schemaUnmarshaller = null; try { SchemaUnmarshallerState state = new SchemaUnmarshallerState(); // Joachim state.setConfiguration(_config); state.cacheIncludedSchemas = _cacheIncludedSchemas; schemaUnmarshaller = new SchemaUnmarshaller(_schemaContext, state); if (_uriResolver != null) schemaUnmarshaller.setURIResolver(_uriResolver); //-- make sure we mark the URI as processed for cyclic //-- imports/includes String uri = _source.getSystemId(); if (uri != null) { URIResolver resolver = schemaUnmarshaller.getURIResolver(); try { URILocation location = resolver.resolve(uri, null); if (location != null) uri = location.toString(); } catch(URIException except) { throw new NestedIOException(except); } state.markAsProcessed(uri, schemaUnmarshaller.getSchema()); } Sax2ComponentReader handler = new Sax2ComponentReader(schemaUnmarshaller); _parser.setDocumentHandler(handler); if (_errorHandler == null) _parser.setErrorHandler(handler); else _parser.setErrorHandler(_errorHandler); if (_resolver != null) _parser.setEntityResolver(_resolver); _parser.parse(_source); } catch(XMLException ex) { handleException(ex); } catch(org.xml.sax.SAXException sx) { handleException(sx); } _schema = schemaUnmarshaller.getSchema(); if (_validate) { try { _schema.validate(); } catch(org.exolab.castor.xml.ValidationException vx) { throw new NestedIOException(vx); } } return _schema; } //-- read /** * Sets the ErrorHandler. * * @param errorHandler **/ public void setErrorHandler(ErrorHandler errorHandler) { _errorHandler = errorHandler; } //-- setErrorHandler /** * Sets wheter or not to cache the included xml schemas * instead of inlining them as specified by the XML Schema * specification. * * @param cache true to cache the included XML Schemas. **/ public void setCacheIncludedSchemas(boolean cache) { _cacheIncludedSchemas = cache; } //-- setErrorHandler /** * Sets whether or not post-read validation should * occur. By default, validation is enabled. Note * that certain read validation cannot be disabled. * * @param validate a boolean that when true will force * a call to Schema#validate after the schema is read. **/ public void setValidation(boolean validate) { _validate = validate; } //-- setValidation /** * Sets the EntityResolver used to resolve SYSTEM Identifier. * If the entity resolver is null, the default one will be used. * * @param resolver the EntityResolver to use. */ public void setEntityResolver(EntityResolver resolver) { _resolver = resolver; } /** * Sets the URIResolver used to resolve hrefs. * If the entity resolver is null, the default one will be used. * * @param uriresolver the URIResolver to use. */ public void setURIResolver(URIResolver uriresolver) { _uriResolver = uriresolver; } /** * Handle an exception which is one of our own XMLExceptions. * @param xmlException the XMLException to handle. * @throws IOException */ private void handleException(XMLException xmlException) throws IOException { throw new NestedIOException(xmlException); } //-- handleException /** * Handle an exception which is a foreign SAXException. * @param sx The SAXException to handle. * @throws IOException */ private void handleException(SAXException sx) throws IOException { Exception except = sx.getException(); if (except == null) { except = sx; } else if (except instanceof SAXParseException) { SAXParseException spe = (SAXParseException)except; String filename = spe.getSystemId(); if (filename == null) filename = spe.getPublicId(); if (filename == null) filename = "<filename unavailable>"; String err = spe.getMessage(); err += "; " + filename + " [ line: " + spe.getLineNumber(); err += ", column: " + spe.getColumnNumber() + ']'; throw new NestedIOException(err, except); } else if (except instanceof XMLException) { handleException((XMLException)except); } throw new NestedIOException(except); } //-- handleException } //-- SchemaReader