    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
    044package org.jfree.xml.generator;
    045
    046import java.beans.BeanInfo;
    047import java.beans.IntrospectionException;
    048import java.beans.Introspector;
    049import java.beans.PropertyDescriptor;
    050import java.io.File;
    051import java.io.IOException;
    052import java.net.URL;
    053import java.util.ArrayList;
    054
    055import org.jfree.io.IOUtils;
    056import org.jfree.xml.generator.model.ClassDescription;
    057import org.jfree.xml.generator.model.Comments;
    058import org.jfree.xml.generator.model.DescriptionModel;
    059import org.jfree.xml.generator.model.IgnoredPropertyInfo;
    060import org.jfree.xml.generator.model.ManualMappingInfo;
    061import org.jfree.xml.generator.model.MultiplexMappingInfo;
    062import org.jfree.xml.generator.model.PropertyInfo;
    063import org.jfree.xml.generator.model.PropertyType;
    064import org.jfree.xml.generator.model.TypeInfo;
    065import org.jfree.xml.util.AbstractModelReader;
    066import org.jfree.xml.util.ObjectDescriptionException;
    067
    068/**
    069 * A reader for the class model.
    070 */
    071public 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}