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 2001 (C) Intalio Inc. All Rights Reserved.
42 *
43 * $Id$
44 */
45 package org.exolab.castor.builder.binding;
46
47 //-Castor imports
48 import java.io.IOException;
49 import java.net.MalformedURLException;
50 import java.net.URL;
51 import java.util.Enumeration;
52
53 import org.exolab.castor.builder.binding.xml.AutomaticNamingType;
54 import org.exolab.castor.builder.binding.xml.Binding;
55 import org.exolab.castor.builder.binding.xml.ComponentBindingType;
56 import org.exolab.castor.builder.binding.xml.IncludeType;
57 import org.exolab.castor.builder.binding.xml.NamingXMLType;
58 import org.exolab.castor.builder.binding.xml.PackageType;
59 import org.exolab.castor.xml.MarshalException;
60 import org.exolab.castor.xml.Unmarshaller;
61 import org.exolab.castor.xml.ValidationException;
62 import org.xml.sax.EntityResolver;
63 import org.xml.sax.InputSource;
64 import org.xml.sax.SAXException;
65
66 /**
67 * This class is responsible for loading a binding document into an in-memory
68 * representation that is meant to be used by the SourceGenerator.
69 *
70 * @author <a href="mailto:blandin@intalio.com">Arnaud Blandin</a>
71 * @version $Revision$ $Date: 2005-03-05 06:42:06 -0700 (Sat, 05 Mar 2005) $
72 */
73 public final class BindingLoader {
74 // TODO Implement the enumeration handling
75
76 /**
77 * The Source Generator Binding File loaded by this BindingLoader.
78 */
79 private ExtendedBinding _binding;
80
81 /**
82 * The binding resolver used for resolving entities.
83 */
84 private BindingResolver _resolver = new BindingResolver();
85
86 /**
87 * No-arg constructor.
88 */
89 public BindingLoader() {
90 // Nothing to do
91 }
92
93 /**
94 * Loads the binding file from the {@link URL} given, and populates
95 * the {@link ExtendedBinding} instance from the values given.
96 * @param url The URL for the binding file to process.
97 * @throws BindingException If the binding file cannnot be processed properly.
98 */
99 public void loadBinding(final String url) throws BindingException {
100 InputSource source;
101 try {
102 source = _resolver.resolveEntity(null, url);
103 if (source == null) {
104 source = new InputSource(url);
105 }
106 if (source.getSystemId() == null) {
107 source.setSystemId(url);
108 }
109 loadBinding(source);
110 } catch (SAXException ex) {
111 throw new BindingException(ex);
112 } catch (IOException ioe) {
113 throw new BindingException(ioe);
114 }
115 }
116
117 /**
118 * Loads a Binding Document. This method will load the binding document into
119 * a binding object and load all the included bindings along the way into a
120 * single collection.
121 *
122 * @param source
123 * The binding document to load.
124 * @throws BindingException
125 * thrown when an error occurred during the unmarshalling.
126 */
127 @SuppressWarnings("unchecked")
128 public void loadBinding(final InputSource source) throws BindingException {
129 Binding loaded = null;
130 if (_binding == null) {
131 _binding = new ExtendedBinding();
132 }
133
134 //do not use the static method to ensure validation is turned on
135 Unmarshaller unmarshaller = new Unmarshaller(Binding.class);
136 unmarshaller.setValidation(true);
137
138 try {
139 loaded = (Binding) unmarshaller.unmarshal(source);
140
141 //--Copy one by one the components loaded in the root binding
142 _binding.setDefaultBindingType(loaded.getDefaultBindingType());
143
144 //--packages
145 Enumeration<PackageType> packages = loaded.enumeratePackage();
146 while (packages.hasMoreElements()) {
147 PackageType tempPackage = packages.nextElement();
148 _binding.addPackage(tempPackage);
149 }
150
151 //--NamingXML
152 NamingXMLType naming = loaded.getNamingXML();
153 if (naming != null) {
154 _binding.setNamingXML(naming);
155 }
156
157 //--NamingXML
158 AutomaticNamingType automaticNaming = loaded.getAutomaticNaming();
159 if (automaticNaming != null) {
160 _binding.setAutomaticNaming(automaticNaming);
161 _binding.handleAutomaticNaming(automaticNaming);
162 }
163
164 //--elementBindings
165 Enumeration<ComponentBindingType> elements = loaded.enumerateElementBinding();
166 while (elements.hasMoreElements()) {
167 ComponentBindingType tempComp = elements.nextElement();
168 _binding.addElementBinding(tempComp);
169 }
170
171 //--attributeBindings
172 Enumeration<ComponentBindingType> attributes = loaded.enumerateAttributeBinding();
173 while (attributes.hasMoreElements()) {
174 ComponentBindingType tempComp = attributes.nextElement();
175 _binding.addAttributeBinding(tempComp);
176 }
177
178 //--ComplexTypeBindings
179 Enumeration<ComponentBindingType> complexTypes = loaded.enumerateComplexTypeBinding();
180 while (complexTypes.hasMoreElements()) {
181 ComponentBindingType tempComp = complexTypes.nextElement();
182 _binding.addComplexTypeBinding(tempComp);
183 }
184
185 //--SimpleTypeBindings
186 Enumeration<ComponentBindingType> sts = loaded.enumerateSimpleTypeBinding();
187 while (sts.hasMoreElements()) {
188 ComponentBindingType tempComp = sts.nextElement();
189 _binding.addSimpleTypeBinding(tempComp);
190 }
191
192 //--groupBindings
193 Enumeration<ComponentBindingType> groups = loaded.enumerateGroupBinding();
194 while (groups.hasMoreElements()) {
195 ComponentBindingType tempComp = groups.nextElement();
196 _binding.addGroupBinding(tempComp);
197 }
198
199 //--enumBinding
200 Enumeration<ComponentBindingType> enums = loaded.enumerateEnumBinding();
201 while (enums.hasMoreElements()) {
202 ComponentBindingType tempEnum = enums.nextElement();
203 _binding.addEnumBinding(tempEnum);
204 }
205
206 //--included schemas
207 Enumeration<IncludeType> includes = loaded.enumerateInclude();
208 while (includes.hasMoreElements()) {
209 IncludeType tempInclude = includes.nextElement();
210 try {
211 loadBinding(tempInclude.getURI());
212 } catch (Exception except) {
213 throw new BindingException(except);
214 }
215 }
216 } catch (MarshalException e) {
217 throw new BindingException(e);
218 } catch (ValidationException e) {
219 throw new BindingException(e);
220 }
221 }
222
223 /**
224 * Returns the binding loaded by the BindingLoader.
225 *
226 * @return the binding loaded by this BindingLoader. This will return null
227 * if no call to loadBinding has been previously made.
228 */
229 public ExtendedBinding getBinding() {
230 return _binding;
231 }
232
233 /**
234 * Sets the base URL for the binding and related files. If the base URL is
235 * known, files can be included using relative names. Any URL can be passed,
236 * if the URL can serve as a base URL it will be used.
237 *
238 * @param url
239 * The base URL
240 */
241 public void setBaseURL(final String url) {
242 try {
243 _resolver.setBaseURL(new URL(url));
244 } catch (MalformedURLException except) {
245 throw new IllegalArgumentException(except.getMessage());
246 }
247 }
248
249 /**
250 * Factory method that returns a binding given an InputSource. The
251 * InputSource identifies a Binding Document meant to be loaded.
252 *
253 * @param source the InputSource identifying the binding document to be
254 * loaded.
255 * @return a binding that contains the different component bindings to be
256 * used in the source generator.
257 * @throws BindingException thrown when the given InputSource doesn't refer
258 * to a valid Binding document.
259 */
260 public static ExtendedBinding createBinding(final InputSource source) throws BindingException {
261 BindingLoader loader = new BindingLoader();
262 loader.loadBinding(source);
263 return loader.getBinding();
264 }
265
266 /**
267 * Factory method for unmarshalling an {@link ExtendedBinding} instance from the
268 * binding file as identified by the given file name.
269 * @param fileName Binding file name.
270 * @return An {@link ExtendedBinding} instance populated from the given binding file (name).
271 * @throws BindingException If the binding file cannot be processed properly.
272 */
273 public static ExtendedBinding createBinding(final String fileName) throws BindingException {
274 BindingLoader loader = new BindingLoader();
275 InputSource source = new InputSource(fileName);
276 loader.loadBinding(source);
277 return loader.getBinding();
278 }
279
280 /**
281 * EntityResolver specific to resolving entities related to the Castor XML
282 * code generator binding file.
283 * @author <a href="mailto:werner DOT guttmann AT gmx DOT net">Werner Guttmann</a>
284 */
285 class BindingResolver implements EntityResolver {
286
287 /**
288 * PUBLIC ID for the Castor XML code generator binding file.
289 */
290 private static final String BINDING_PUBLICID =
291 "-//EXOLAB/Castor Binding Schema Version 1.0//EN";
292 /**
293 * SYSTEM ID for the Castor XML code generator binding file.
294 */
295 private static final String BINDING_SYSTEMID =
296 "http://exolab.castor.org/binding.xsd";
297 /**
298 * Classpath-based URL to the binding XML schema as shipped in the
299 * Castor XML code generator binary JAR.
300 */
301 private static final String BINDING_RESOURCE =
302 "/org/exolab/castor/builder/binding/binding.xsd";
303
304 /**
305 * Base URL, if known.
306 */
307 private URL _baseUrl;
308
309 /**
310 * Sets a base URL for relative processing.
311 * @param baseUrl Base URL for relative processing.
312 */
313 public void setBaseURL(final URL baseUrl) {
314 _baseUrl = baseUrl;
315 }
316
317 /**
318 * Returns the base URL for relative processing.
319 * @return base URL for relative processing
320 */
321 public URL getBaseURL() {
322 return _baseUrl;
323 }
324
325 /**
326 * Code adapted from DTDResolver written by Assaf Arkin.
327 *
328 * @param publicId The public identifier of the external entity being
329 * referenced, or null if none was supplied.
330 * @param systemId The system identifier of the external entity being
331 * referenced.
332 * @return An InputSource object describing the new input source, or
333 * null to request that the parser open a regular URI connection
334 * to the system identifier.
335 * @throws org.xml.sax.SAXException Any SAX exception, possibly wrapping
336 * another exception.
337 * @throws java.io.IOException A Java-specific IO exception, possibly
338 * the result of creating a new InputStream or Reader for the
339 * InputSource.
340 * @see org.exolab.castor.util.DTDResolver#resolveEntity(java.lang.String,
341 * java.lang.String)
342 * @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String,
343 * java.lang.String)
344 */
345 public InputSource resolveEntity(final String publicId, final String systemId)
346 throws IOException, SAXException {
347 InputSource source = null;
348
349 // First, resolve the schema if any
350 if (publicId != null && publicId.equals(BINDING_PUBLICID)) {
351 source = new InputSource(getClass().getResourceAsStream(BINDING_RESOURCE));
352 source.setPublicId(publicId);
353 return source;
354 }
355
356 if (systemId != null && systemId.equals(BINDING_SYSTEMID)) {
357 source = new InputSource(getClass().getResourceAsStream(BINDING_RESOURCE));
358 source.setSystemId(systemId);
359 return source;
360 }
361
362 // Can't resolve public id, but might be able to resolve relative
363 // system id, since we have a base URI.
364 if (systemId != null && _baseUrl != null) {
365 URL url;
366 try {
367 url = new URL(systemId);
368 source = new InputSource(url.openStream());
369 source.setSystemId(systemId);
370 return source;
371 } catch (MalformedURLException except) {
372 try {
373 url = new URL(_baseUrl, systemId);
374 source = new InputSource(url.openStream());
375 source.setSystemId(systemId);
376 return source;
377 } catch (MalformedURLException ex2) {
378 throw new SAXException(ex2);
379 }
380 }
381 }
382 // No resolving.
383 return null;
384 }
385
386 } //--BindingResolver
387
388 }