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