View Javadoc
1   /*
2    * Copyright 2005 Ralf Joachim
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.exolab.castor.mapping;
17  
18  import java.io.IOException;
19  import java.net.MalformedURLException;
20  import java.net.URL;
21  import java.util.ArrayList;
22  import java.util.Collections;
23  import java.util.HashSet;
24  import java.util.List;
25  import java.util.Set;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.castor.core.util.Messages;
30  import org.castor.mapping.MappingSource;
31  import org.exolab.castor.mapping.xml.MappingRoot;
32  import org.exolab.castor.net.util.URIUtils;
33  import org.exolab.castor.util.DTDResolver;
34  import org.xml.sax.EntityResolver;
35  import org.xml.sax.InputSource;
36  import org.xml.sax.SAXException;
37  
38  /**
39   * Utility class for loading mapping files and providing them to the
40   * XML marshaller, JDO engine etc. The mapping file can be loaded from
41   * a URL, input stream or SAX <tt>InputSource</tt>.
42   * <p>
43   * Multiple mapping files can be loaded with the same <tt>Mapping</tt>
44   * object. When loading master mapping files that include other mapping
45   * files it might be convenient to use {@link #setBaseURL} or {@link
46   * #setEntityResolver}.
47   * <p>
48   * If the desired class loader is different than the one used by Castor
49   * (e.g. if Castor is installed as a Java extension), the <tt>Mapping</tt>
50   * object can be constructed with the proper class loader.
51   * <p>
52   * The following example loads two mapping files:
53   * <pre>
54   * Mapping mapping;
55   *
56   * mapping = new Mapping( getClass().getClassLoader() );
57   * mapping.loadMapping( "mapping.xml" );
58   * mapping.loadMapping( url );
59   * </pre>
60   *
61   * @author <a href="mailto:arkin AT intalio DOT com">Assaf Arkin</a>
62   * @author <a href="mailto:ralf DOT joachim AT syscon DOT eu">Ralf Joachim</a>
63   * @version $Revision$ $Date: 2006-04-25 16:09:10 -0600 (Tue, 25 Apr 2006) $
64   */
65  public final class Mapping {
66      //--------------------------------------------------------------------------
67      
68      /** The <a href="http://jakarta.apache.org/commons/logging/">Jakarta Commons
69       *  Logging </a> instance used for all logging. */
70      private static final Log LOG = LogFactory.getLog(Mapping.class);
71      
72      private static final String DEFAULT_SOURCE_TYPE = "CastorXmlMapping";
73      
74      /** List of mapping sources to resolve. */
75      private final List _mappings = new ArrayList();
76  
77      /** Set of processed systemID's. */
78      private final Set _processed = new HashSet();
79  
80      /** The loaded mapping. */
81      private final MappingRoot _root = new MappingRoot();
82  
83      /** The class loader to use. */
84      private final ClassLoader _classLoader;
85      
86      /** The entity resolver to use. May be null. */
87      private DTDResolver _resolver = new DTDResolver();
88      
89      //--------------------------------------------------------------------------
90  
91      /**
92       * Constructs a new mapping.
93       *
94       * @param loader The class loader to use, null for the default
95       */
96      public Mapping(final ClassLoader loader) {
97          if (loader == null) {
98              _classLoader = getClass().getClassLoader();
99          } else {
100             _classLoader = loader;
101         }
102     }
103 
104     /**
105      * Constructs a new mapping.
106      */
107     public Mapping() { this(null); }
108 
109     //--------------------------------------------------------------------------
110 
111     /**
112      * Get list of mapping sources to resolve.
113      * 
114      * @return List of mapping sources to resolve.
115      * @throws MappingException If no mapping source has been loaded previously.
116      */
117     public List getMappingSources() throws MappingException {
118         return Collections.unmodifiableList(_mappings);
119     }
120 
121     /**
122      * Marks the given mapping as having been processed.
123      * 
124      * @param id systemID or stream to identify the mapping to mark.
125      */
126     public void markAsProcessed(final Object id) {
127         _processed.add(id);
128     }
129 
130     /**
131      * Returns true if the given systemID or stream has been marked as processed.
132      * 
133      * @param id systemID or stream to check for being marked as processed.
134      * @return true if the given systemID or stream has been marked as processed.
135      */
136     public boolean processed(final Object id) {
137         return _processed.contains(id);
138     }
139     
140     /**
141      * Get the loaded mapping.
142      * 
143      * @return The loaded mapping.
144      */
145     public MappingRoot getRoot() { return _root; }
146     
147     //--------------------------------------------------------------------------
148 
149     /**
150      * Returns the class loader used by this mapping object. The returned
151      * class loaded may be the one passed in the constructor, the one used
152      * to load Castor, or in some 1.1 JVMs null.
153      *
154      * @return The class loader used by this mapping object (may be null)
155      */
156     public ClassLoader getClassLoader() {
157         return _classLoader;
158     }
159     
160     /**
161      * Sets the entity resolver. The entity resolver can be used to
162      * resolve external entities and cached documents that are used
163      * from within mapping files.
164      *
165      * @param resolver The entity resolver to use
166      */
167     public void setEntityResolver(final EntityResolver resolver) {
168         _resolver = new DTDResolver(resolver);
169     }
170 
171     /**
172      * Sets the base URL for the mapping and related files. If the base
173      * URL is known, files can be included using relative names. Any URL
174      * can be passed, if the URL can serve as a base URL it will be used.
175      * If url is an absolute path, it is converted to a file URL.
176      *
177      * @param url The base URL
178      */
179     public void setBaseURL(final String url) {
180         String location = url;
181         //-- remove filename if necessary:
182         if (location != null) {        
183             int idx = location.lastIndexOf('/');
184             if (idx < 0) idx = location.lastIndexOf('\\');
185             if (idx >= 0) {
186                 int extIdx = location.indexOf('.', idx);
187                 if (extIdx > 0) {
188                     location = location.substring(0, idx);
189                 }
190             }
191         }
192         
193         try {
194           _resolver.setBaseURL(new URL(location));
195         } catch (MalformedURLException except) {
196           // try to parse the url as an absolute path
197           try {
198               LOG.info(Messages.format("mapping.wrongURL", location));
199               _resolver.setBaseURL(new URL("file", null, location));
200           } catch (MalformedURLException except2) { }
201         }
202     }
203 
204     //--------------------------------------------------------------------------
205 
206     /**
207      * Loads the mapping from the specified URL with type defaults to
208      * 'CastorXmlMapping'. If an entity resolver was specified, will use
209      * the entity resolver to resolve the URL. This method is also used
210      * to load mappings referenced from another mapping or configuration
211      * file.
212      *
213      * @param url The URL of the mapping file.
214      * @throws IOException An error occured when reading the mapping file.
215      * @throws MappingException The mapping file is invalid.
216      */
217     public void loadMapping(final String url) throws IOException, MappingException {
218         loadMapping(url, DEFAULT_SOURCE_TYPE);
219     }
220 
221     /**
222      * Loads the mapping from the specified URL. If an entity resolver
223      * was specified, will use the entity resolver to resolve the URL.
224      * This method is also used to load mappings referenced from another
225      * mapping or configuration file.
226      *
227      * @param url The URL of the mapping file.
228      * @param type The source type.
229      * @throws IOException An error occured when reading the mapping file.
230      * @throws MappingException The mapping file is invalid.
231      */
232     public void loadMapping(final String url, final String type)
233     throws IOException, MappingException {
234         String location = url;
235         if (_resolver.getBaseURL() == null) {
236             setBaseURL(location);
237             location = URIUtils.getRelativeURI(location);
238         }
239         try {
240             InputSource source = _resolver.resolveEntity(null, location);
241             if (source == null) { source = new InputSource(location); }
242             if (source.getSystemId() == null) { source.setSystemId(location); }
243             LOG.info(Messages.format("mapping.loadingFrom", location));
244             loadMapping(source, type);
245         } catch (SAXException ex) {
246             throw new MappingException(ex);
247         }
248     }
249 
250     /**
251      * Loads the mapping from the specified URL with type defaults to
252      * 'CastorXmlMapping'.
253      *
254      * @param url The URL of the mapping file.
255      * @throws IOException An error occured when reading the mapping file.
256      * @throws MappingException The mapping file is invalid.
257      */
258     public void loadMapping(final URL url) throws IOException, MappingException {
259         loadMapping(url, DEFAULT_SOURCE_TYPE);
260     }
261 
262     /**
263      * Loads the mapping from the specified URL.
264      *
265      * @param url The URL of the mapping file.
266      * @param type The source type.
267      * @throws IOException An error occured when reading the mapping file.
268      * @throws MappingException The mapping file is invalid.
269      */
270     public void loadMapping(final URL url, final String type)
271     throws IOException, MappingException {
272         try {
273             if (_resolver.getBaseURL() == null) {
274                 _resolver.setBaseURL(url);
275             }
276             InputSource source = _resolver.resolveEntity(null, url.toExternalForm());
277             if (source == null) {
278                 source = new InputSource(url.toExternalForm());
279                 source.setByteStream(url.openStream());
280             } else
281                 source.setSystemId(url.toExternalForm());
282             LOG.info(Messages.format("mapping.loadingFrom", url.toExternalForm()));
283            loadMapping(source, type);
284         } catch (SAXException ex) {
285             throw new MappingException(ex);
286         }
287     }
288 
289     /**
290      * Loads the mapping from the specified input source with type defaults to
291      * 'CastorXmlMapping'.
292      *
293      * @param source The input source.
294      */
295     public void loadMapping(final InputSource source) {
296         loadMapping(source, DEFAULT_SOURCE_TYPE);
297     }
298 
299     /**
300      * Loads the mapping from the specified input source.
301      *
302      * @param source The input source.
303      * @param type The source type.
304      */
305     public void loadMapping(final InputSource source, final String type) {
306         _mappings.add(new MappingSource(source, type, _resolver));
307     }
308 
309     //--------------------------------------------------------------------------
310 }
311 
312