All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jfree.xml.generator.ModelBuilder Maven / Gradle / Ivy

Go to download

JCommon is a free general purpose Java class library that is used in several projects at www.jfree.org, including JFreeChart and JFreeReport.

There is a newer version: 1.0.24
Show newest version
/* ========================================================================
 * JCommon : a free general purpose class library for the Java(tm) platform
 * ========================================================================
 *
 * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
 * 
 * Project Info:  http://www.jfree.org/jcommon/index.html
 *
 * This library is free software; you can redistribute it and/or modify it 
 * under the terms of the GNU Lesser General Public License as published by 
 * the Free Software Foundation; either version 2.1 of the License, or 
 * (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
 * USA.  
 *
 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
 * in the United States and other countries.]
 * 
 * -----------------
 * ModelBuilder.java
 * -----------------
 * (C)opyright 2003, 2004, by Thomas Morgner and Contributors.
 *
 * Original Author:  Thomas Morgner;
 * Contributor(s):   David Gilbert (for Object Refinery Limited);
 *
 * $Id: ModelBuilder.java,v 1.3 2005/10/18 13:32:20 mungady Exp $
 *
 * Changes
 * -------
 * 21-Jun-2003 : Initial version (TM);
 * 26-Nov-2003 : Updated header and Javadocs (DG);
 * 
 */

package org.jfree.xml.generator;

import java.beans.BeanInfo;
import java.beans.IndexedPropertyDescriptor;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Properties;

import org.jfree.util.HashNMap;
import org.jfree.xml.generator.model.ClassDescription;
import org.jfree.xml.generator.model.DescriptionModel;
import org.jfree.xml.generator.model.MultiplexMappingInfo;
import org.jfree.xml.generator.model.PropertyInfo;
import org.jfree.xml.generator.model.PropertyType;
import org.jfree.xml.generator.model.TypeInfo;
import org.jfree.xml.util.BasicTypeSupport;

/**
 * A model builder.  This class performs the work of creating a class description model from
 * a set of source files.
 */
public final class ModelBuilder {

    /** The single instance. */
    private static ModelBuilder instance;

    /**
     * Returns the single instance of this class.
     * 
     * @return the single instance of this class.
     */
    public static ModelBuilder getInstance() {
        if (instance == null) {
            instance = new ModelBuilder();
        }
        return instance;
    }

    /** The handler mapping. */
    private Properties handlerMapping;

    /**
     * Creates a single instance.
     */
    private ModelBuilder() {
        this.handlerMapping = new Properties();
    }

    /**
     * Adds attribute handlers.
     * 
     * @param p  the handlers.
     */
    public void addAttributeHandlers(final Properties p) {
        this.handlerMapping.putAll(p);
    }

    /**
     * Builds a model from the classes provided by the {@link SourceCollector}. 
     * 

* The {@link DescriptionGenerator} class invokes this. * * @param c the source collector. * @param model the model under construction (null permitted). * * @return The completed model. */ public DescriptionModel buildModel(final SourceCollector c, DescriptionModel model) { Class[] classes = c.getClasses(); if (model == null) { model = new DescriptionModel(); } while (classes.length != 0) { classes = fillModel(classes, model); } fillSuperClasses(model); // search for multiplexer classes // first search all classes used in parameters and add them to // our list of possible base classes final Class[] baseClasses = findElementTypes(model); final HashNMap classMap = new HashNMap(); for (int i = 0; i < baseClasses.length; i++) { final Class base = baseClasses[i]; for (int j = 0; j < baseClasses.length; j++) { final Class child = baseClasses[j]; if (Modifier.isAbstract(child.getModifiers())) { continue; } if (base.isAssignableFrom(child)) { classMap.add(base, child); } } } // at this point, the keys of 'classMap' represent all required // multiplexers, while the values assigned to these keys define the // possible childs final Iterator keys = classMap.keys(); while (keys.hasNext()) { final Class base = (Class) keys.next(); final Class[] childs = (Class[]) classMap.toArray(base, new Class[0]); if (childs.length < 2) { continue; } boolean isNew = false; MultiplexMappingInfo mmi = model.getMappingModel().lookupMultiplexMapping(base); final ArrayList typeInfoList; if (mmi == null) { mmi = new MultiplexMappingInfo(base); typeInfoList = new ArrayList(); isNew = true; } else { typeInfoList = new ArrayList(Arrays.asList(mmi.getChildClasses())); } for (int i = 0; i < childs.length; i++) { // the generic information is only added, if no other information // is already present ... final TypeInfo typeInfo = new TypeInfo(childs[i].getName(), childs[i]); if (!typeInfoList.contains(typeInfo)) { typeInfoList.add(typeInfo); } } mmi.setChildClasses((TypeInfo[]) typeInfoList.toArray(new TypeInfo[0])); if (isNew) { model.getMappingModel().addMultiplexMapping(mmi); } } // when resolving a class to an handler, the resolver first has to // search for an multiplexer before searching for handlers. Otherwise // non-abstract baseclasses will be found before the multiplexer can // resolve the situation. return model; } private Class[] findElementTypes(final DescriptionModel model) { final ArrayList baseClasses = new ArrayList(); for (int i = 0; i < model.size(); i++) { final ClassDescription cd = model.get(i); if (!baseClasses.contains(cd.getObjectClass())) { baseClasses.add(cd.getObjectClass()); } final PropertyInfo[] properties = cd.getProperties(); for (int p = 0; p < properties.length; p++) { // filter primitive types ... they cannot form a generalization // relation if (!properties[p].getPropertyType().equals(PropertyType.ELEMENT)) { continue; } final Class type = properties[p].getType(); if (baseClasses.contains(type)) { continue; } // filter final classes, they too cannot have derived classes if (Modifier.isFinal(type.getModifiers())) { continue; } baseClasses.add(type); } } return (Class[]) baseClasses.toArray(new Class[baseClasses.size()]); } /** * Fills the super class for all object descriptions of the model. The * super class is only filled, if the object's super class is contained * in the model. * * @param model the model which should get its superclasses updated. */ private void fillSuperClasses(final DescriptionModel model) { // Fill superclasses for (int i = 0; i < model.size(); i++) { final ClassDescription cd = model.get(i); final Class parent = cd.getObjectClass().getSuperclass(); if (parent == null) { continue; } final ClassDescription superCD = model.get(parent); if (superCD != null) { cd.setSuperClass(superCD.getObjectClass()); } } } /** * Updates the model to contain the given classes. * * @param classes a list of classes which should be part of the model. * @param model the model which is updated * * @return A list of super classes which should also be contained in the model. */ private Class[] fillModel(final Class[] classes, final DescriptionModel model) { // first check all direct matches from the source collector. // but make sure that we also detect external superclasses - // we have to get all properties ... final ArrayList superClasses = new ArrayList(); for (int i = 0; i < classes.length; i++) { Class superClass = classes[i].getSuperclass(); if (superClass != null) { if (!Object.class.equals(superClass) && !contains(classes, superClass) && !superClasses.contains(superClass)) { superClasses.add(superClass); } } else { superClass = Object.class; } try { final BeanInfo bi = Introspector.getBeanInfo(classes[i], superClass); final ClassDescription parent = model.get(classes[i]); final ClassDescription cd = createClassDescription(bi, parent); if (cd != null) { model.addClassDescription(cd); } } catch (IntrospectionException ie) { // swallowed.... } } return (Class[]) superClasses.toArray(new Class[0]); } /** * Creates a {@link ClassDescription} object for the specified bean info. * * @param beanInfo the bean info. * @param parent the parent class description. * * @return The class description. */ private ClassDescription createClassDescription (final BeanInfo beanInfo, final ClassDescription parent) { final PropertyDescriptor[] props = beanInfo.getPropertyDescriptors(); final ArrayList properties = new ArrayList(); for (int i = 0; i < props.length; i++) { final PropertyDescriptor propertyDescriptor = props[i]; PropertyInfo pi; if (parent != null) { pi = parent.getProperty(propertyDescriptor.getName()); if (pi != null) { // Property already found, don't touch it // Log.info (new Log.SimpleMessage // ("Ignore predefined property: ", propertyDescriptor.getName())); properties.add(pi); continue; } } if (props[i] instanceof IndexedPropertyDescriptor) { // this would handle lists and array access. We don't support // this in the direct approach. We will need some cheating: // // // // // pi = createIndexedPropertyInfo((IndexedPropertyDescriptor) props[i]); } else { pi = createSimplePropertyInfo(props[i]); if (pi != null) { properties.add(pi); } } } final PropertyInfo[] propArray = (PropertyInfo[]) properties.toArray(new PropertyInfo[properties.size()]); final ClassDescription cd; if (parent != null) { cd = parent; } else { cd = new ClassDescription(beanInfo.getBeanDescriptor().getBeanClass()); cd.setDescription(beanInfo.getBeanDescriptor().getShortDescription()); } cd.setProperties(propArray); return cd; } /** * Checks, whether the given method can be called from the generic object factory. * * @param method the method descriptor * @return true, if the method is not null and public, false otherwise. */ public static boolean isValidMethod(final Method method) { if (method == null) { return false; } if (!Modifier.isPublic(method.getModifiers())) { return false; } return true; } /** * Creates a {@link PropertyInfo} object from a {@link PropertyDescriptor}. * * @param pd the property descriptor. * * @return the property info (null possible). */ public PropertyInfo createSimplePropertyInfo(final PropertyDescriptor pd) { final boolean readMethod = isValidMethod(pd.getReadMethod()); final boolean writeMethod = isValidMethod(pd.getWriteMethod()); if (!writeMethod || !readMethod) { // a property is useless for our purposes without having a read or write method. return null; } final PropertyInfo pi = new PropertyInfo(pd.getName(), pd.getPropertyType()); pi.setConstrained(pd.isConstrained()); pi.setDescription(pd.getShortDescription()); pi.setNullable(true); pi.setPreserve(false); pi.setReadMethodAvailable(readMethod); pi.setWriteMethodAvailable(writeMethod); pi.setXmlName(pd.getName()); if (isAttributeProperty(pd.getPropertyType())) { pi.setPropertyType(PropertyType.ATTRIBUTE); pi.setXmlHandler(getHandlerClass(pd.getPropertyType())); } else { pi.setPropertyType(PropertyType.ELEMENT); } return pi; } /** * Checks, whether the given class can be handled as attribute. * All primitive types can be attributes as well as all types which have * a custom attribute handler defined. * * @param c the class which should be checked * @return true, if the class can be handled as attribute, false otherwise. */ private boolean isAttributeProperty(final Class c) { if (BasicTypeSupport.isBasicDataType(c)) { return true; } return this.handlerMapping.containsKey(c.getName()); } /** * Returns the class name for the attribute handler for a property of the specified class. * * @param c the class for which to search an attribute handler * @return the handler class or null, if this class cannot be handled * as attribute. */ private String getHandlerClass(final Class c) { if (BasicTypeSupport.isBasicDataType(c)) { final String handler = BasicTypeSupport.getHandlerClass(c); if (handler != null) { return handler; } } return this.handlerMapping.getProperty(c.getName()); } /** * Checks, whether the class c is contained in the given * class array. * * @param cAll the list of all classes * @param c the class to be searched * @return true, if the class is contained in the array, false otherwise. */ private boolean contains(final Class[] cAll, final Class c) { for (int i = 0; i < cAll.length; i++) { if (cAll[i].equals(c)) { return true; } } return false; } // private PropertyInfo createIndexedPropertyInfo(IndexedPropertyDescriptor prop) // { // // MethodInfo readMethod = createMethodInfo(prop.getIndexedReadMethod()); // MethodInfo writeMethod = createMethodInfo(prop.getIndexedWriteMethod()); // if (writeMethod == null) // { // return null; // } // IndexedPropertyInfo pi = new IndexedPropertyInfo(prop.getName()); // pi.setConstrained(prop.isConstrained()); // pi.setDescription(prop.getShortDescription()); // pi.setNullable(true); // pi.setPreserve(false); // pi.setType(prop.getIndexedPropertyType()); // pi.setReadMethod(readMethod); // pi.setWriteMethod(writeMethod); // // TypeInfo keyInfo = new TypeInfo("index"); // keyInfo.setType(Integer.TYPE); // keyInfo.setNullable(false); // keyInfo.setConstrained(true); // throws indexoutofboundsexception // keyInfo.setDescription("Generic index value"); // KeyDescription kd = new KeyDescription(new TypeInfo[]{keyInfo}); // pi.setKey(kd); // return pi; // } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy