/**
* 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 (C) Intalio, Inc. All Rights Reserved.
*
* $Id$
*/
package org.exolab.castor.util;
import java.net.URL;
import java.net.MalformedURLException;
import java.io.IOException;
import org.xml.sax.SAXException;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.exolab.castor.net.util.URIUtils;
/**
* Entity resolver for various DTD/schema. Holds information and performs
* resolving on a variety of DTD and schema, both those defined by Castor and
* those used by Castor and cached by it.
* <p>
* The following DTD and XML schema are supported:
* <ul>
* <li>Castor mapping DTD/Schema
* <li>Castor JDO configuration DTD/Schema
* <li>XML Schema DTDs
* </ul>
* <p>
* This resolver can resolve both public and system identifiers, and will return
* an input stream into a cached resource in the Castor JAR.
* <p>
* This resolver can be used as wrapper to another entity resolver. For example,
* if a resolver is used for external entities in the mapping file, construct a
* new resolver using the {@link #DTDResolver(EntityResolver)} constructor.
* <p>
*
* @author <a href="mailto:arkin@intalio.com">Assaf Arkin</a>
* @version $Revision$ $Date: 2005-12-13 14:58:48 -0700 (Tue, 13 Dec 2005) $
*/
public class DTDResolver implements EntityResolver {
/**
* Holds information about a given DTD of XML Schema.
*/
static class DTDInfo {
/**
* The public identifier. Null if unknown.
*/
private final String publicId;
/**
* The system identifier. Null if unknown.
*/
private final String systemId;
/**
* The resource name, if a copy of the document is available.
*/
private final String resource;
/**
* Creates an instance of DTDInfo.
*
* @param publicId public id
* @param systemId system id
* @param namespace namespace
* @param prefix prefix
* @param resource resource
*/
DTDInfo(final String publicId,
final String systemId,
final String namespace,
final String prefix,
final String resource) {
this.publicId = publicId;
this.systemId = systemId;
this.resource = resource;
}
}
/**
* Defines information about a variety of DTDs, both those defined by Castor
* and those defined elsewhere and cached by Castor.
*/
private final DTDInfo[] _dtdInfo = new DTDInfo[] {
// Information for Mapping DTD and schema
new DTDInfo("-//EXOLAB/Castor Mapping DTD Version 1.0//EN",
"http://castor.exolab.org/mapping.dtd",
"castor.exolab.org", "castor",
"/org/exolab/castor/mapping/mapping.dtd"),
new DTDInfo("-//EXOLAB/Castor Mapping Schema Version 1.0//EN",
"http://castor.exolab.org/mapping.xsd",
"castor.exolab.org", "castor",
"/org/exolab/castor/mapping/mapping.xsd"),
new DTDInfo("-//EXOLAB/Castor Mapping DTD Version 1.0//EN",
"http://castor.org/mapping.dtd", "castor.org", "castor",
"/org/exolab/castor/mapping/mapping.dtd"),
new DTDInfo("-//EXOLAB/Castor Mapping Schema Version 1.0//EN",
"http://castor.org/mapping.xsd", "castor.org", "castor",
"/org/exolab/castor/mapping/mapping.xsd"),
// Information for JDO configuration DTD and schema
new DTDInfo(
"-//EXOLAB/Castor JDO Configuration DTD Version 1.0//EN",
"http://castor.exolab.org/jdo-conf.dtd",
"castor.exolab.org", "castor",
"/org/castor/jdo/conf/jdo-conf.dtd"),
new DTDInfo(
"-//EXOLAB/Castor JDO Configuration Schema Version 1.0//EN",
"http://castor.exolab.org/jdo-conf.xsd",
"castor.exolab.org", "castor",
"/org/castor/jdo/conf/jdo-conf.xsd"),
new DTDInfo(
"-//EXOLAB/Castor JDO Configuration DTD Version 1.0//EN",
"http://castor.org/jdo-conf.dtd", "castor.org", "castor",
"/org/castor/jdo/conf/jdo-conf.dtd"),
new DTDInfo(
"-//EXOLAB/Castor JDO Configuration Schema Version 1.0//EN",
"http://castor.org/jdo-conf.xsd", "castor.org", "castor",
"/org/castor/jdo/conf/jdo-conf.xsd"),
// Resolving for XML Schema DTDs
new DTDInfo(
"-//W3C//DTD XMLSCHEMA 19991216//EN",
"http://www.w3.org/TR/2000/WD-xmlschema-1-20000225/structures.dtd",
null, null,
"/org/exolab/castor/util/resources/structures.dtd"),
new DTDInfo(
null,
"http://www.w3.org/TR/2000/WD-xmlschema-2-20000225/datatypes.dtd",
null, null,
"/org/exolab/castor/util/resources/datatypes.dtd"),
new DTDInfo(
null,
"http://www.w3.org/TR/2000/WD-xmlschema-1-20000225/structures.xsd",
null, null,
"/org/exolab/castor/util/resources/structures.xsd"),
};
/**
* The wrapped resolver.
*/
private EntityResolver _resolver;
/**
* Base URL, if known.
*/
private URL _baseUrl;
/**
* Constructs a new DTD resolver. This resolver wraps another resolver and
* will delegate all resolving not related to the Castor mapping files to
* that resolver. The wrapper resolver will typically be used for entities
* appearing in the actual mapping file.
*/
public DTDResolver(EntityResolver resolver) {
_resolver = resolver;
}
/**
* Constructs a new DTD resolver.
*/
public DTDResolver() {
}
/**
* Sets the base URL to use.
* @param baseUrl Base URL.
*/
public void setBaseURL(final URL baseUrl) {
_baseUrl = baseUrl;
// //-- make sure we have a document base and not
// //-- a full URL to an actual file
// if (baseUrl != null) {
// String urlString = baseUrl.toExternalForm();
// String docBase = URIUtils.getDocumentBase(urlString);
// if (urlString.equals(docBase)) {
// _baseUrl = baseUrl;
// }
// else if ((docBase != null) && (docBase.length() > 0)) {
// try {
// _baseUrl = new URL(docBase);
// }
// catch(MalformedURLException mue) {
// // TODO: bubble up exception instead of
// // rethrowing
// String error = "Malformed URL: " + docBase;
// throw new IllegalStateException(error);
// }
// }
// else {
// _baseUrl = null;
// }
// }
}
/**
* Returns the base URL in use.
* @return The base URL.
*/
public URL getBaseURL() {
return _baseUrl;
}
/**
* Resolves public & system ids to files stored within the JAR.
*
* @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String,
* java.lang.String)
*/
public InputSource resolveEntity(final String publicId,
final String systemId) throws IOException, SAXException {
int i;
InputSource source = null;
// First, resolve all the DTD/schema.
for (i = 0; i < _dtdInfo.length; ++i) {
if (publicId != null && publicId.equals(_dtdInfo[i].publicId)) {
source = new InputSource(getClass().getResourceAsStream(
_dtdInfo[i].resource));
source.setPublicId(publicId);
return source;
}
if (systemId != null && systemId.equals(_dtdInfo[i].systemId)) {
source = new InputSource(getClass().getResourceAsStream(
_dtdInfo[i].resource));
source.setSystemId(systemId);
return source;
}
}
// If a resolver was specified, use it.
if (_resolver != null) {
source = _resolver.resolveEntity(publicId, systemId);
if (source != null) {
return source;
}
}
// Can't resolve public id, but might be able to resolve relative
// system id, since we have a base URI.
if (systemId != null && _baseUrl != null) {
URL url;
try {
url = new URL(_baseUrl, systemId);
source = new InputSource(url.openStream());
source.setSystemId(systemId);
return source;
} catch (MalformedURLException except) {
try {
String absURL = URIUtils.resolveAsString(systemId, _baseUrl
.toString());
url = new URL(absURL);
source = new InputSource(url.openStream());
source.setSystemId(systemId);
return source;
} catch (MalformedURLException ex2) {
// nothing to do
}
} catch (java.io.FileNotFoundException fnfe) {
try {
String absURL = URIUtils.resolveAsString(systemId, _baseUrl
.toString());
url = new URL(absURL);
source = new InputSource(url.openStream());
source.setSystemId(systemId);
return source;
} catch (MalformedURLException ex2) {
// nothing to do
}
}
return null;
}
// No resolving.
return null;
}
}