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