001    /* ========================================================================
002     * JCommon : a free general purpose class library for the Java(tm) platform
003     * ========================================================================
004     *
005     * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
006     * 
007     * Project Info:  http://www.jfree.org/jcommon/index.html
008     *
009     * This library is free software; you can redistribute it and/or modify it 
010     * under the terms of the GNU Lesser General Public License as published by 
011     * the Free Software Foundation; either version 2.1 of the License, or 
012     * (at your option) any later version.
013     *
014     * This library is distributed in the hope that it will be useful, but 
015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
017     * License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this library; if not, write to the Free Software
021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
022     * USA.  
023     *
024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
025     * in the United States and other countries.]
026     * 
027     * -----------------------
028     * DefaultModelReader.java
029     * -----------------------
030     * (C)opyright 2003, 2004, by Thomas Morgner and Contributors.
031     *
032     * Original Author:  Thomas Morgner;
033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
034     *
035     * $Id: DefaultModelReader.java,v 1.2 2005/10/18 13:32:20 mungady Exp $
036     *
037     * Changes
038     * -------
039     * 12-Nov-2003 : Initial version (TM);
040     * 26-Nov-2003 : Updated header and Javadocs (DG);
041     *
042     */
043    
044    package org.jfree.xml.generator;
045    
046    import java.beans.BeanInfo;
047    import java.beans.IntrospectionException;
048    import java.beans.Introspector;
049    import java.beans.PropertyDescriptor;
050    import java.io.File;
051    import java.io.IOException;
052    import java.net.URL;
053    import java.util.ArrayList;
054    
055    import org.jfree.io.IOUtils;
056    import org.jfree.xml.generator.model.ClassDescription;
057    import org.jfree.xml.generator.model.Comments;
058    import org.jfree.xml.generator.model.DescriptionModel;
059    import org.jfree.xml.generator.model.IgnoredPropertyInfo;
060    import org.jfree.xml.generator.model.ManualMappingInfo;
061    import org.jfree.xml.generator.model.MultiplexMappingInfo;
062    import org.jfree.xml.generator.model.PropertyInfo;
063    import org.jfree.xml.generator.model.PropertyType;
064    import org.jfree.xml.generator.model.TypeInfo;
065    import org.jfree.xml.util.AbstractModelReader;
066    import org.jfree.xml.util.ObjectDescriptionException;
067    
068    /**
069     * A reader for the class model.
070     */
071    public class DefaultModelReader extends AbstractModelReader {
072    
073        /** A model containing classes and the corresponding class descriptions. */
074        private DescriptionModel model;
075    
076        /** The class description under construction. */
077        private ClassDescription currentClassDescription;
078    
079        /** Information about the class being processed. */
080        private BeanInfo currentBeanInfo;
081    
082        /** The base URL. */
083        private URL baseURL;
084        
085        /** The source. */
086        private String source;
087        
088        /** The multiplex mapping info. */
089        private MultiplexMappingInfo multiplexInfo;
090        
091        /** The multiplex type info.*/
092        private ArrayList multiplexTypeInfos;
093    
094        /** Storage for the properties of the current class. */
095        private ArrayList propertyList;
096    
097        /** Storage for the constructors of the current class. */
098        private ArrayList constructorList;
099    
100        /**
101         * Creates a new model reader.
102         */
103        public DefaultModelReader() {
104            super();
105        }
106    
107        /**
108         * Loads a description model.
109         * 
110         * @param file  the file name.
111         * 
112         * @return A description model.
113         * 
114         * @throws IOException  if there is an I/O problem.
115         * @throws ObjectDescriptionException  if there is a problem reading the object descriptions.
116         */
117        public synchronized DescriptionModel load(final String file) throws IOException,
118                                                                      ObjectDescriptionException {
119            
120            this.model = new DescriptionModel();
121            this.baseURL = new File (file).toURL();
122            parseXml(this.baseURL);
123            fillSuperClasses();
124            return this.model;
125            
126        }
127    
128        /**
129         * Iterates through all the class descriptions in the model, setting the superclass
130         * attribute in all cases where the superclass definitions are contained in the model.
131         */
132        protected void fillSuperClasses() {
133            for (int i = 0; i < this.model.size(); i++) {
134                final ClassDescription cd = this.model.get(i);
135                final Class parent = cd.getObjectClass().getSuperclass();
136                if (parent == null) {
137                    continue;
138                }
139                final ClassDescription superCD = this.model.get(parent);
140                if (superCD != null) {
141                    cd.setSuperClass(superCD.getObjectClass());
142                }
143            }
144        }
145    
146        /**
147         * Begin processing an object definition element.
148         *
149         * @param className  the class name.
150         * @param register  the register name (<code>null</code> permitted).
151         * @param ignore  ??
152         *
153         * @return <code>true</code> if the class is available, and <code>false</code> otherwise.
154         */
155        protected boolean startObjectDefinition(final String className, final String register, final boolean ignore) {
156            final Class c = loadClass(className);
157            if (c == null) {
158                return false;
159            }
160            this.currentClassDescription = new ClassDescription(c);
161            this.currentClassDescription.setPreserve(ignore);
162            this.currentClassDescription.setRegisterKey(register);
163            try {
164                this.currentBeanInfo = Introspector.getBeanInfo(c, Object.class);
165            }
166            catch (IntrospectionException ie) {
167                return false;
168            }
169            this.propertyList = new java.util.ArrayList();
170            this.constructorList = new java.util.ArrayList();
171            return true;
172        }
173    
174        /**
175         * Finishes processing an object definition (sets the constructor and property info for the
176         * class description, and adds the class description to the model).
177         * 
178         * @throws ObjectDescriptionException if there is a problem with the object description.
179         */
180        protected void endObjectDefinition() throws ObjectDescriptionException {
181            final PropertyInfo[] pis = (PropertyInfo[])
182                this.propertyList.toArray(new PropertyInfo[this.propertyList.size()]);
183            this.currentClassDescription.setProperties(pis);
184    
185            final TypeInfo[] tis = (TypeInfo[])
186            this.constructorList.toArray(new TypeInfo[this.constructorList.size()]);
187    
188            this.currentClassDescription.setConstructorDescription(tis);
189            this.currentClassDescription.setComments
190                (new Comments(getOpenComment(), getCloseComment()));
191            this.currentClassDescription.setSource(this.source);
192    
193            this.model.addClassDescription(this.currentClassDescription);
194    
195            this.propertyList = null;
196            this.currentBeanInfo = null;
197            this.currentClassDescription = null;
198        }
199    
200        /**
201         * Handles the description of an attribute within an object definition.
202         *
203         * @param name  the name.
204         * @param attribName  the attribute name.
205         * @param handlerClass  the fully qualified class name for the attribute handler.
206         * 
207         * @throws ObjectDescriptionException if there is a problem with the object description.
208         */
209        protected void handleAttributeDefinition(final String name, final String attribName, final String handlerClass)
210            throws ObjectDescriptionException {
211    
212            final PropertyInfo propertyInfo = ModelBuilder.getInstance().createSimplePropertyInfo
213                (getPropertyDescriptor(name));
214    
215            if (propertyInfo == null) {
216                throw new ObjectDescriptionException("Unable to load property " + name);
217            }
218    
219            propertyInfo.setComments(new Comments(getOpenComment(), getCloseComment()));
220            propertyInfo.setPropertyType(PropertyType.ATTRIBUTE);
221            propertyInfo.setXmlName(attribName);
222            propertyInfo.setXmlHandler(handlerClass);
223            this.propertyList.add(propertyInfo);
224        }
225    
226        /**
227         * Handles the constructor definition.
228         * 
229         * @param tagName  the tag name.
230         * @param parameterClass  the parameter class.
231         * 
232         * @throws ObjectDescriptionException if there is a problem with the object description.
233         */
234        protected void handleConstructorDefinition(final String tagName, final String parameterClass)
235            throws ObjectDescriptionException {
236    
237            final Class c = loadClass(parameterClass);
238            if (c == null) {
239                throw new ObjectDescriptionException("Failed to load class " + parameterClass);
240            }
241            final TypeInfo ti = new TypeInfo(tagName, c);
242            ti.setComments(new Comments(getOpenComment(), getCloseComment()));
243            this.constructorList.add (ti);
244        }
245    
246        /**
247         * Handles the description of an element within an object definition.
248         *
249         * @param name  the property name.
250         * @param element  the element name.
251         * 
252         * @throws ObjectDescriptionException if there is a problem with the object description.
253         */
254        protected void handleElementDefinition(final String name, final String element)
255            throws ObjectDescriptionException {
256    
257            final PropertyInfo propertyInfo = ModelBuilder.getInstance().createSimplePropertyInfo
258                (getPropertyDescriptor(name));
259    
260            if (propertyInfo == null) {
261                throw new ObjectDescriptionException("Unable to load property " + name);
262            }
263    
264            propertyInfo.setComments(new Comments(getOpenComment(), getCloseComment()));
265            propertyInfo.setPropertyType(PropertyType.ELEMENT);
266            propertyInfo.setXmlName(element);
267            propertyInfo.setXmlHandler(null);
268            this.propertyList.add(propertyInfo);
269    
270        }
271    
272        /**
273         * Handles a lookup definition.
274         * 
275         * @param name  the name.
276         * @param lookupKey  the lookup key.
277         * 
278         * @throws ObjectDescriptionException if there is a problem with the object description.
279         */
280        protected void handleLookupDefinition(final String name, final String lookupKey)
281            throws ObjectDescriptionException {
282            final PropertyInfo propertyInfo = ModelBuilder.getInstance().createSimplePropertyInfo
283                (getPropertyDescriptor(name));
284    
285            if (propertyInfo == null) {
286                throw new ObjectDescriptionException("Unable to load property " + name);
287            }
288    
289            propertyInfo.setComments(new Comments(getOpenComment(), getCloseComment()));
290            propertyInfo.setPropertyType(PropertyType.LOOKUP);
291            propertyInfo.setXmlName(lookupKey);
292            propertyInfo.setXmlHandler(null);
293            this.propertyList.add(propertyInfo);
294        }
295    
296        /**
297         * Returns a property descriptor for the named property, or <code>null</code> if there is
298         * no descriptor with the given name.
299         *
300         * @param propertyName  the property name.
301         *
302         * @return a property descriptor.
303         */
304        protected PropertyDescriptor getPropertyDescriptor(final String propertyName) {
305            final PropertyDescriptor[] pds = this.currentBeanInfo.getPropertyDescriptors();
306            for (int i = 0; i < pds.length; i++) {
307                if (pds[i].getName().equals(propertyName)) {
308                    return pds[i];
309                }
310            }
311            return null;
312        }
313    
314        /**
315         * Handles an ignored property.
316         * 
317         * @param name  the name.
318         */
319        protected void handleIgnoredProperty(final String name) {
320            final IgnoredPropertyInfo propertyInfo = new IgnoredPropertyInfo(name);
321            propertyInfo.setComments(new Comments(getOpenComment(), getCloseComment()));
322            this.propertyList.add(propertyInfo);
323        }
324    
325        /**
326         * Handles a manual mapping.
327         *
328         * @param className  the class name.
329         * @param readHandler  the read handler.
330         * @param writeHandler  the write handler.
331         * 
332         * @return A boolean.
333         * 
334         * @throws ObjectDescriptionException if there is a problem with the object description.
335         */
336        protected boolean handleManualMapping(final String className, final String readHandler, final String writeHandler)
337            throws ObjectDescriptionException {
338    
339            final ManualMappingInfo manualMappingInfo =
340                new ManualMappingInfo(loadClass(className),
341                    loadClass(readHandler), loadClass(writeHandler));
342            manualMappingInfo.setComments(new Comments(getOpenComment(), getCloseComment()));
343            manualMappingInfo.setSource(this.source);
344            this.model.getMappingModel().addManualMapping(manualMappingInfo);
345            return true;
346        }
347    
348        /**
349         * Start a multiplex mapping.
350         * 
351         * @param className  the class name.
352         * @param typeAttr  the type.
353         */
354        protected void startMultiplexMapping(final String className, final String typeAttr) {
355            this.multiplexInfo = new MultiplexMappingInfo(loadClass(className), typeAttr);
356            this.multiplexInfo.setSource(this.source);
357            this.multiplexTypeInfos = new ArrayList();
358        }
359    
360        /**
361         * Handles a multiplex mapping.
362         * 
363         * @param typeName  the type name.
364         * @param className  the class name.
365         * 
366         * @throws ObjectDescriptionException if there is a problem with the object description.
367         */
368        protected void handleMultiplexMapping(final String typeName, final String className)
369            throws ObjectDescriptionException {
370            final TypeInfo info = new TypeInfo(typeName, loadClass(className));
371            info.setComments(new Comments(getOpenComment(), getCloseComment()));
372            this.multiplexTypeInfos.add (info);
373        }
374    
375        /**
376         * Ends a multiplex mapping.
377         * 
378         * @throws ObjectDescriptionException if there is a problem with the object description. 
379         */
380        protected void endMultiplexMapping() throws ObjectDescriptionException {
381            final TypeInfo[] typeInfos = (TypeInfo[]) this.multiplexTypeInfos.toArray(
382                new TypeInfo[this.multiplexTypeInfos.size()]
383            );
384            this.multiplexInfo.setComments(new Comments(getOpenComment(), getCloseComment()));
385            this.multiplexInfo.setChildClasses(typeInfos);
386            this.model.getMappingModel().addMultiplexMapping(this.multiplexInfo);
387            this.multiplexInfo = null;
388        }
389    
390        /**
391         * Starts include handling.
392         * 
393         * @param resource  the URL.
394         */
395        protected void startIncludeHandling(final URL resource) {
396            this.source = IOUtils.getInstance().createRelativeURL(resource, this.baseURL);
397            this.model.addSource(this.source);
398            this.model.addIncludeComment(
399                this.source, new Comments(getOpenComment(), getCloseComment())
400            );
401        }
402    
403        /**
404         * Ends include handling.
405         */
406        protected void endIncludeHandling() {
407            this.source = "";
408        }
409    
410        /**
411         * Starts the root document.
412         */
413        protected void startRootDocument() {
414            this.source = "";
415        }
416    
417        /**
418         * Ends the root document.
419         */
420        protected void endRootDocument() {
421            this.model.setModelComments(new Comments(getOpenComment(), getCloseComment()));
422        }
423    }