View Javadoc
1   /*
2    * Copyright 2005 Ralf Joachim
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  package org.castor.mapping;
15  
16  import java.io.IOException;
17  import java.util.Enumeration;
18  import java.util.Iterator;
19  
20  import org.apache.commons.logging.Log;
21  import org.apache.commons.logging.LogFactory;
22  import org.castor.core.CoreProperties;
23  import org.castor.core.util.Messages;
24  import org.castor.xml.AbstractInternalContext;
25  import org.castor.xml.InternalContext;
26  import org.exolab.castor.mapping.Mapping;
27  import org.exolab.castor.mapping.MappingException;
28  import org.exolab.castor.mapping.MappingLoader;
29  import org.exolab.castor.mapping.loader.AbstractMappingLoader;
30  import org.exolab.castor.mapping.xml.ClassMapping;
31  import org.exolab.castor.mapping.xml.FieldHandlerDef;
32  import org.exolab.castor.mapping.xml.Include;
33  import org.exolab.castor.mapping.xml.KeyGeneratorDef;
34  import org.exolab.castor.mapping.xml.MappingRoot;
35  import org.exolab.castor.util.DTDResolver;
36  import org.exolab.castor.xml.ClassDescriptorResolverFactory;
37  import org.exolab.castor.xml.Introspector;
38  import org.exolab.castor.xml.Unmarshaller;
39  import org.exolab.castor.xml.XMLClassDescriptorResolver;
40  import org.exolab.castor.xml.util.ResolverStrategy;
41  import org.exolab.castor.xml.util.resolvers.CastorXMLStrategy;
42  import org.xml.sax.InputSource;
43  import org.xml.sax.SAXException;
44  
45  /**
46   * @author <a href="mailto:ralf DOT joachim AT syscon DOT eu">Ralf Joachim</a>
47   * @version $Revision: 5951 $ $Date: 2006-04-25 16:09:10 -0600 (Tue, 25 Apr 2006) $
48   */
49  public final class MappingUnmarshaller {
50    // --------------------------------------------------------------------------
51  
52    /**
53     * The <a href="http://jakarta.apache.org/commons/logging/">Jakarta Commons Logging </a> instance
54     * used for all logging.
55     */
56    private static final Log LOG = LogFactory.getLog(MappingUnmarshaller.class);
57  
58    /** The registry of MappingLoader's. */
59    private final MappingLoaderRegistry _registry;
60  
61    /**
62     * The IDResolver to give to the Unmarshaller. This allows resolving "extends" and "depends" for
63     * included Mappings.
64     */
65    private final MappingUnmarshallIDResolver _idResolver;
66  
67    /**
68     * A flag that indicates of whether or not to allow redefinitions of class mappings.
69     */
70    private boolean _allowRedefinitions = false;
71  
72    /**
73     * The {@link AbstractInternalContext}?holds all 'global' Castor states and access to
74     * configuration.
75     */
76    private InternalContext _internalContext;
77  
78    // --------------------------------------------------------------------------
79  
80    /**
81     * Construct a new MappingUnmarshaller.
82     */
83    public MappingUnmarshaller() {
84      _registry = new MappingLoaderRegistry(new CoreProperties());
85      _idResolver = new MappingUnmarshallIDResolver();
86      AbstractInternalContext internalContext = new AbstractInternalContext() {};
87      internalContext.setClassLoader(getClass().getClassLoader());
88  
89      XMLClassDescriptorResolver cdr = (XMLClassDescriptorResolver) ClassDescriptorResolverFactory
90          .createClassDescriptorResolver(BindingType.XML);
91      cdr.setInternalContext(internalContext);
92      internalContext.setXMLClassDescriptorResolver(cdr);
93  
94      Introspector introspector = new Introspector();
95      introspector.setInternalContext(internalContext);
96      internalContext.setIntrospector(introspector);
97      cdr.setIntrospector(introspector);
98  
99      ResolverStrategy resolverStrategy = new CastorXMLStrategy();
100     internalContext.setResolverStrategy(resolverStrategy);
101     cdr.setResolverStrategy(resolverStrategy);
102 
103     _internalContext = internalContext;
104   }
105 
106   /**
107    * Enables or disables the ability to allow the redefinition of class mappings.
108    * 
109    * @param allow a boolean that when true enables redefinitions.
110    **/
111   public void setAllowRedefinitions(final boolean allow) {
112     _allowRedefinitions = allow;
113   }
114 
115   // --------------------------------------------------------------------------
116 
117   /**
118    * Returns a mapping resolver for the suitable engine. The engine's specific mapping loader is
119    * created and used to create engine specific descriptors, returning a suitable mapping resolver.
120    * The mapping resolver is cached in memory and returned in subsequent method calls.
121    *
122    * @param mapping The mapping to load and resolve.
123    * @param bindingType The binding type to read from mapping.
124    * @return A mapping resolver.
125    * @throws MappingException A mapping error occured preventing descriptors from being generated
126    *         from the loaded mapping.
127    */
128   public MappingLoader getMappingLoader(final Mapping mapping, final BindingType bindingType)
129       throws MappingException {
130     return getMappingLoader(mapping, bindingType, null);
131   }
132 
133   /**
134    * Returns a mapping resolver for the suitable engine. The engine's specific mapping loader is
135    * created and used to create engine specific descriptors, returning a suitable mapping resolver.
136    * The mapping resolver is cached in memory and returned in subsequent method calls.
137    *
138    * @param mapping The mapping to load and resolve.
139    * @param bindingType The binding type to read from mapping.
140    * @param param Arbitrary parameter that is to be passed to resolver.loadMapping().
141    * @return A mapping resolver
142    * @throws MappingException A mapping error occured preventing descriptors from being generated
143    *         from the loaded mapping.
144    */
145   public MappingLoader getMappingLoader(final Mapping mapping, final BindingType bindingType,
146       final Object param) throws MappingException {
147     synchronized (this) {
148       Iterator iter = mapping.getMappingSources().iterator();
149       while (iter.hasNext()) {
150         MappingSource source = (MappingSource) iter.next();
151         loadMappingInternal(mapping, source.getResolver(), source.getSource());
152       }
153 
154       AbstractMappingLoader loader;
155       loader = (AbstractMappingLoader) _registry.getMappingLoader("CastorXmlMapping", bindingType);
156       loader.setClassLoader(mapping.getClassLoader());
157       loader.setAllowRedefinitions(_allowRedefinitions);
158       loader.setInternalContext(_internalContext);
159       loader.loadMapping(mapping.getRoot(), param);
160       return loader;
161     }
162   }
163 
164   public void loadMappingOnly(final Mapping mapping) throws MappingException {
165     synchronized (this) {
166       Iterator iter = mapping.getMappingSources().iterator();
167       while (iter.hasNext()) {
168         MappingSource source = (MappingSource) iter.next();
169         loadMappingInternal(mapping, source.getResolver(), source.getSource());
170       }
171     }
172   }
173 
174   // --------------------------------------------------------------------------
175 
176   /**
177    * Internal recursive loading method. This method will load the mapping document into a mapping
178    * object and load all the included mapping along the way into a single collection.
179    *
180    * @param mapping The mapping instance.
181    * @param resolver The entity resolver to use.
182    * @param url The URL of the mapping file.
183    * @throws IOException An error occured when reading the mapping file.
184    * @throws MappingException The mapping file is invalid.
185    */
186   protected void loadMappingInternal(final Mapping mapping, final DTDResolver resolver,
187       final String url) throws IOException, MappingException {
188     try {
189       InputSource source = resolver.resolveEntity(null, url);
190       if (source == null) {
191         source = new InputSource(url);
192       }
193       if (source.getSystemId() == null) {
194         source.setSystemId(url);
195       }
196       LOG.info(Messages.format("mapping.loadingFrom", url));
197       loadMappingInternal(mapping, resolver, source);
198     } catch (SAXException ex) {
199       throw new MappingException(ex);
200     }
201   }
202 
203   /**
204    * Internal recursive loading method. This method will load the mapping document into a mapping
205    * object and load all the included mapping along the way into a single collection.
206    *
207    * @param mapping The mapping instance.
208    * @param resolver The entity resolver to use. May be null.
209    * @param source The input source.
210    * @throws MappingException The mapping file is invalid.
211    */
212   private void loadMappingInternal(final Mapping mapping, final DTDResolver resolver,
213       final InputSource source) throws MappingException {
214     // Clear all the cached resolvers, so they can be reconstructed a
215     // second time based on the new mappings loaded
216     _registry.clear();
217 
218     Object id = source.getSystemId();
219     if (id == null) {
220       id = source.getByteStream();
221     }
222     if (id != null) {
223       // check that the mapping has already been processed
224       if (mapping.processed(id)) {
225         return;
226       }
227 
228       // mark the mapping as being processed
229       mapping.markAsProcessed(id);
230     }
231 
232     MappingRoot root = mapping.getRoot();
233     _idResolver.setMapping(root);
234 
235     try {
236       // Load the specificed mapping source
237       Unmarshaller unm = new Unmarshaller(MappingRoot.class);
238       unm.setValidation(false);
239       unm.setEntityResolver(resolver);
240       unm.setClassLoader(Mapping.class.getClassLoader());
241       unm.setIDResolver(_idResolver);
242       unm.setUnmarshalListener(new MappingUnmarshallListener(this, mapping, resolver));
243 
244       MappingRoot loaded = (MappingRoot) unm.unmarshal(source);
245 
246       // Load all the included mapping by reference
247       // -- note: this is just for processing any
248       // -- includes which may have previously failed
249       // -- using the IncludeListener...and to
250       // -- report any potential errors.
251       Enumeration includes = loaded.enumerateInclude();
252       while (includes.hasMoreElements()) {
253         Include include = (Include) includes.nextElement();
254         if (!mapping.processed(include.getHref())) {
255           try {
256             loadMappingInternal(mapping, resolver, include.getHref());
257           } catch (Exception ex) {
258             throw new MappingException(ex);
259           }
260         }
261       }
262 
263       // gather "class" tags
264       Enumeration<? extends ClassMapping> classMappings = loaded.enumerateClassMapping();
265       while (classMappings.hasMoreElements()) {
266         root.addClassMapping(classMappings.nextElement());
267       }
268 
269       // gather "key-generator" tags
270       Enumeration<? extends KeyGeneratorDef> keyGeneratorDefinitions =
271           loaded.enumerateKeyGeneratorDef();
272       while (keyGeneratorDefinitions.hasMoreElements()) {
273         root.addKeyGeneratorDef(keyGeneratorDefinitions.nextElement());
274       }
275 
276       // gather "field-handler" tags
277       Enumeration<? extends FieldHandlerDef> fieldHandlerDefinitions =
278           loaded.enumerateFieldHandlerDef();
279       while (fieldHandlerDefinitions.hasMoreElements()) {
280         root.addFieldHandlerDef(fieldHandlerDefinitions.nextElement());
281       }
282     } catch (Exception ex) {
283       throw new MappingException(ex);
284     }
285   }
286 
287   /**
288    * To set the internal context.
289    * 
290    * @param internalContext the {@link AbstractInternalContext}?to use
291    */
292   // public void setInternalContext(final InternalContext internalContext) {
293   // _internalContext = internalContext;
294   // }
295 
296   // --------------------------------------------------------------------------
297 }