1 /**
2 * Redistribution and use of this software and associated documentation
3 * ("Software"), with or without modification, are permitted provided
4 * that the following conditions are met:
5 *
6 * 1. Redistributions of source code must retain copyright
7 * statements and notices. Redistributions must also contain a
8 * copy of this document.
9 *
10 * 2. Redistributions in binary form must reproduce the
11 * above copyright notice, this list of conditions and the
12 * following disclaimer in the documentation and/or other
13 * materials provided with the distribution.
14 *
15 * 3. The name "Exolab" must not be used to endorse or promote
16 * products derived from this Software without prior written
17 * permission of Intalio, Inc. For written permission,
18 * please contact info@exolab.org.
19 *
20 * 4. Products derived from this Software may not be called "Exolab"
21 * nor may "Exolab" appear in their names without prior written
22 * permission of Intalio, Inc. Exolab is a registered
23 * trademark of Intalio, Inc.
24 *
25 * 5. Due credit should be given to the Exolab Project
26 * (http://www.exolab.org/).
27 *
28 * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
29 * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
32 * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39 * OF THE POSSIBILITY OF SUCH DAMAGE.
40 *
41 * Copyright 1999 (C) Intalio, Inc. All Rights Reserved.
42 *
43 * $Id$
44 */
45
46 package org.exolab.castor.util;
47
48 import java.net.URL;
49 import java.net.MalformedURLException;
50 import java.io.IOException;
51 import org.xml.sax.SAXException;
52 import org.xml.sax.EntityResolver;
53 import org.xml.sax.InputSource;
54 import org.exolab.castor.net.util.URIUtils;
55
56 /**
57 * Entity resolver for various DTD/schema. Holds information and performs
58 * resolving on a variety of DTD and schema, both those defined by Castor and
59 * those used by Castor and cached by it.
60 * <p>
61 * The following DTD and XML schema are supported:
62 * <ul>
63 * <li>Castor mapping DTD/Schema
64 * <li>Castor JDO configuration DTD/Schema
65 * <li>XML Schema DTDs
66 * </ul>
67 * <p>
68 * This resolver can resolve both public and system identifiers, and will return
69 * an input stream into a cached resource in the Castor JAR.
70 * <p>
71 * This resolver can be used as wrapper to another entity resolver. For example,
72 * if a resolver is used for external entities in the mapping file, construct a
73 * new resolver using the {@link #DTDResolver(EntityResolver)} constructor.
74 * <p>
75 *
76 * @author <a href="mailto:arkin@intalio.com">Assaf Arkin</a>
77 * @version $Revision$ $Date: 2005-12-13 14:58:48 -0700 (Tue, 13 Dec 2005) $
78 */
79 public class DTDResolver implements EntityResolver {
80
81 /**
82 * Holds information about a given DTD of XML Schema.
83 */
84 static class DTDInfo {
85
86 /**
87 * The public identifier. Null if unknown.
88 */
89 private final String publicId;
90
91 /**
92 * The system identifier. Null if unknown.
93 */
94 private final String systemId;
95
96 /**
97 * The resource name, if a copy of the document is available.
98 */
99 private final String resource;
100
101 /**
102 * Creates an instance of DTDInfo.
103 *
104 * @param publicId public id
105 * @param systemId system id
106 * @param namespace namespace
107 * @param prefix prefix
108 * @param resource resource
109 */
110 DTDInfo(final String publicId,
111 final String systemId,
112 final String namespace,
113 final String prefix,
114 final String resource) {
115 this.publicId = publicId;
116 this.systemId = systemId;
117 this.resource = resource;
118 }
119 }
120
121 /**
122 * Defines information about a variety of DTDs, both those defined by Castor
123 * and those defined elsewhere and cached by Castor.
124 */
125 private final DTDInfo[] _dtdInfo = new DTDInfo[] {
126 // Information for Mapping DTD and schema
127 new DTDInfo("-//EXOLAB/Castor Mapping DTD Version 1.0//EN",
128 "http://castor.exolab.org/mapping.dtd",
129 "castor.exolab.org", "castor",
130 "/org/exolab/castor/mapping/mapping.dtd"),
131 new DTDInfo("-//EXOLAB/Castor Mapping Schema Version 1.0//EN",
132 "http://castor.exolab.org/mapping.xsd",
133 "castor.exolab.org", "castor",
134 "/org/exolab/castor/mapping/mapping.xsd"),
135 new DTDInfo("-//EXOLAB/Castor Mapping DTD Version 1.0//EN",
136 "http://castor.org/mapping.dtd", "castor.org", "castor",
137 "/org/exolab/castor/mapping/mapping.dtd"),
138 new DTDInfo("-//EXOLAB/Castor Mapping Schema Version 1.0//EN",
139 "http://castor.org/mapping.xsd", "castor.org", "castor",
140 "/org/exolab/castor/mapping/mapping.xsd"),
141 // Information for JDO configuration DTD and schema
142 new DTDInfo(
143 "-//EXOLAB/Castor JDO Configuration DTD Version 1.0//EN",
144 "http://castor.exolab.org/jdo-conf.dtd",
145 "castor.exolab.org", "castor",
146 "/org/castor/jdo/conf/jdo-conf.dtd"),
147 new DTDInfo(
148 "-//EXOLAB/Castor JDO Configuration Schema Version 1.0//EN",
149 "http://castor.exolab.org/jdo-conf.xsd",
150 "castor.exolab.org", "castor",
151 "/org/castor/jdo/conf/jdo-conf.xsd"),
152 new DTDInfo(
153 "-//EXOLAB/Castor JDO Configuration DTD Version 1.0//EN",
154 "http://castor.org/jdo-conf.dtd", "castor.org", "castor",
155 "/org/castor/jdo/conf/jdo-conf.dtd"),
156 new DTDInfo(
157 "-//EXOLAB/Castor JDO Configuration Schema Version 1.0//EN",
158 "http://castor.org/jdo-conf.xsd", "castor.org", "castor",
159 "/org/castor/jdo/conf/jdo-conf.xsd"),
160 // Resolving for XML Schema DTDs
161 new DTDInfo(
162 "-//W3C//DTD XMLSCHEMA 19991216//EN",
163 "http://www.w3.org/TR/2000/WD-xmlschema-1-20000225/structures.dtd",
164 null, null,
165 "/org/exolab/castor/util/resources/structures.dtd"),
166 new DTDInfo(
167 null,
168 "http://www.w3.org/TR/2000/WD-xmlschema-2-20000225/datatypes.dtd",
169 null, null,
170 "/org/exolab/castor/util/resources/datatypes.dtd"),
171 new DTDInfo(
172 null,
173 "http://www.w3.org/TR/2000/WD-xmlschema-1-20000225/structures.xsd",
174 null, null,
175 "/org/exolab/castor/util/resources/structures.xsd"),
176
177 };
178
179 /**
180 * The wrapped resolver.
181 */
182 private EntityResolver _resolver;
183
184 /**
185 * Base URL, if known.
186 */
187 private URL _baseUrl;
188
189 /**
190 * Constructs a new DTD resolver. This resolver wraps another resolver and
191 * will delegate all resolving not related to the Castor mapping files to
192 * that resolver. The wrapper resolver will typically be used for entities
193 * appearing in the actual mapping file.
194 */
195 public DTDResolver(EntityResolver resolver) {
196 _resolver = resolver;
197 }
198
199 /**
200 * Constructs a new DTD resolver.
201 */
202 public DTDResolver() {
203 }
204
205 /**
206 * Sets the base URL to use.
207 * @param baseUrl Base URL.
208 */
209 public void setBaseURL(final URL baseUrl) {
210 _baseUrl = baseUrl;
211
212 // //-- make sure we have a document base and not
213 // //-- a full URL to an actual file
214 // if (baseUrl != null) {
215 // String urlString = baseUrl.toExternalForm();
216 // String docBase = URIUtils.getDocumentBase(urlString);
217 // if (urlString.equals(docBase)) {
218 // _baseUrl = baseUrl;
219 // }
220 // else if ((docBase != null) && (docBase.length() > 0)) {
221 // try {
222 // _baseUrl = new URL(docBase);
223 // }
224 // catch(MalformedURLException mue) {
225 // // TODO: bubble up exception instead of
226 // // rethrowing
227 // String error = "Malformed URL: " + docBase;
228 // throw new IllegalStateException(error);
229 // }
230 // }
231 // else {
232 // _baseUrl = null;
233 // }
234 // }
235 }
236
237 /**
238 * Returns the base URL in use.
239 * @return The base URL.
240 */
241 public URL getBaseURL() {
242 return _baseUrl;
243 }
244
245 /**
246 * Resolves public & system ids to files stored within the JAR.
247 *
248 * @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String,
249 * java.lang.String)
250 */
251 public InputSource resolveEntity(final String publicId,
252 final String systemId) throws IOException, SAXException {
253 int i;
254 InputSource source = null;
255
256 // First, resolve all the DTD/schema.
257 for (i = 0; i < _dtdInfo.length; ++i) {
258 if (publicId != null && publicId.equals(_dtdInfo[i].publicId)) {
259 source = new InputSource(getClass().getResourceAsStream(
260 _dtdInfo[i].resource));
261 source.setPublicId(publicId);
262 return source;
263 }
264 if (systemId != null && systemId.equals(_dtdInfo[i].systemId)) {
265 source = new InputSource(getClass().getResourceAsStream(
266 _dtdInfo[i].resource));
267 source.setSystemId(systemId);
268 return source;
269 }
270 }
271
272 // If a resolver was specified, use it.
273 if (_resolver != null) {
274 source = _resolver.resolveEntity(publicId, systemId);
275 if (source != null) {
276 return source;
277 }
278 }
279
280 // Can't resolve public id, but might be able to resolve relative
281 // system id, since we have a base URI.
282 if (systemId != null && _baseUrl != null) {
283 URL url;
284
285 try {
286 url = new URL(_baseUrl, systemId);
287 source = new InputSource(url.openStream());
288 source.setSystemId(systemId);
289 return source;
290 } catch (MalformedURLException except) {
291 try {
292 String absURL = URIUtils.resolveAsString(systemId, _baseUrl
293 .toString());
294 url = new URL(absURL);
295 source = new InputSource(url.openStream());
296 source.setSystemId(systemId);
297 return source;
298 } catch (MalformedURLException ex2) {
299 // nothing to do
300 }
301 } catch (java.io.FileNotFoundException fnfe) {
302 try {
303 String absURL = URIUtils.resolveAsString(systemId, _baseUrl
304 .toString());
305 url = new URL(absURL);
306 source = new InputSource(url.openStream());
307 source.setSystemId(systemId);
308 return source;
309 } catch (MalformedURLException ex2) {
310 // nothing to do
311 }
312 }
313 return null;
314 }
315
316 // No resolving.
317 return null;
318 }
319
320 }