    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    }