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