View Javadoc
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 }