    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     * BeanObjectDescription.java
    029     * --------------------------
    030     * (C)opyright 2003-2005, by Thomas Morgner and Contributors.
    031     *
    032     * Original Author:  Thomas Morgner;
    033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
    034     *
    035     * $Id: BeanObjectDescription.java,v 1.6 2005/10/18 13:31:58 mungady Exp $
    036     *
    037     * Changes (from 19-Feb-2003)
    038     * -------------------------
    039     * 19-Feb-2003 : Added standard header and Javadocs (DG);
    040     * 29-Apr-2003 : Distilled from the JFreeReport project and moved into
    041     *               JCommon (TM);
    042     * 18-Aug-2005 : Added casts to suppress compiler warnings, as suggested in
    043     *               patch 1260622 (DG);
    044     *
    045     */
    046
    047    package org.jfree.xml.factory.objects;
    048
    049    import java.lang.reflect.Method;
    050    import java.lang.reflect.Modifier;
    051    import java.util.Iterator;
    052    import java.util.TreeSet;
    053    import java.util.HashMap;
    054    import java.beans.Introspector;
    055    import java.beans.IntrospectionException;
    056    import java.beans.BeanInfo;
    057    import java.beans.PropertyDescriptor;
    058    import java.io.ObjectInputStream;
    059    import java.io.IOException;
    060
    061    import org.jfree.util.Log;
    062
    063    /**
    064     * An object-description for a bean object. This object description
    065     * is very dangerous, if the bean contains properties with undefined
    066     * types.
    067     *
    068     * @author Thomas Morgner
    069     */
    070    public class BeanObjectDescription extends AbstractObjectDescription {
    071
    072        private TreeSet ignoredParameters;
    073        private transient HashMap properties;
    074
    075        /**
    076         * Creates a new object description.
    077         *
    078         * @param className  the class.
    079         */
    080        public BeanObjectDescription(final Class className) {
    081            this(className, true);
    082        }
    083
    084        /**
    085         * Creates a new object description.
    086         *
    087         * @param className  the class.
    088         * @param init  set to true, to autmaoticly initialise the object
    089         *              description. If set to false, the initialisation is
    090         *              elsewhere.
    091         */
    092        public BeanObjectDescription(final Class className, final boolean init) {
    093            super(className);
    094            // now create some method descriptions ..
    095            this.ignoredParameters = new TreeSet();
    096            readBeanDescription(className, init);
    097        }
    098
    099        private boolean isValidMethod (final Method method, final int parCount)
    100        {
    101            if (method == null) {
    102                return false;
    103            }
    104            if (!Modifier.isPublic(method.getModifiers())) {
    105                return false;
    106            }
    107            if (Modifier.isStatic(method.getModifiers())) {
    108                return false;
    109            }
    110            if (method.getParameterTypes().length != parCount) {
    111                return false;
    112            }
    113            return true;
    114        }
    115
    116        /**
    117         * Creates an object based on this description.
    118         *
    119         * @return The object.
    120         */
    121        public Object createObject() {
    122            try {
    123                final Object o = getObjectClass().newInstance();
    124                // now add the various parameters ...
    125
    126                final Iterator it = getParameterNames();
    127                while (it.hasNext()) {
    128                    final String name = (String) it.next();
    129
    130                    if (isParameterIgnored(name)) {
    131                        continue;
    132                    }
    133
    134                    final Method method = findSetMethod(name);
    135                    final Object parameterValue = getParameter(name);
    136                    if (parameterValue == null) {
    137                        // Log.debug ("Parameter: " + name + " is null");
    138                    }
    139                    else {
    140                        method.invoke(o, new Object[]{parameterValue});
    141                    }
    142                }
    143                return o;
    144            }
    145            catch (Exception e) {
    146                Log.error("Unable to invoke bean method", e);
    147            }
    148            return null;
    149        }
    150
    151        /**
    152         * Finds a set method in the bean.
    153         *
    154         * @param parameterName  the parameter name.
    155         *
    156         * @return The method.
    157         */
    158        private Method findSetMethod(final String parameterName) {
    159            final PropertyDescriptor descriptor
    160                = (PropertyDescriptor) this.properties.get(parameterName);
    161            return descriptor.getWriteMethod();
    162        }
    163
    164        /**
    165         * Finds a get method in the bean.
    166         *
    167         * @param parameterName  the paramater name.
    168         * @return The method.
    169         */
    170        private Method findGetMethod(final String parameterName) {
    171            final PropertyDescriptor descriptor
    172                = (PropertyDescriptor) this.properties.get(parameterName);
    173            return descriptor.getReadMethod();
    174        }
    175
    176        /**
    177         * Sets the parameters in the description to match the supplied object.
    178         *
    179         * @param o  the object (<code>null</code> not allowed).
    180         *
    181         * @throws ObjectFactoryException if there is a problem.
    182         */
    183        public void setParameterFromObject(final Object o)
    184            throws ObjectFactoryException {
    185            if (o == null) {
    186                throw new NullPointerException("Given object is null");
    187            }
    188            final Class c = getObjectClass();
    189            if (!c.isInstance(o)) {
    190                throw new ObjectFactoryException("Object is no instance of " + c
    191                    + "(is " + o.getClass() + ")");
    192            }
    193
    194            final Iterator it = getParameterNames();
    195            while (it.hasNext()) {
    196                final String propertyName = (String) it.next();
    197
    198                if (isParameterIgnored(propertyName)) {
    199                    continue;
    200                }
    201
    202                try {
    203                    final Method method = findGetMethod(propertyName);
    204                    final Object retval = method.invoke(o, (Object[]) null);
    205                    if (retval != null) {
    206                        setParameter(propertyName, retval);
    207                    }
    208                }
    209                catch (Exception e) {
    210                    Log.info("Exception on method invokation.", e);
    211                }
    212
    213            }
    214        }
    215
    216        /**
    217         * Adds a parameter to the ignored parameters.
    218         *
    219         * @param parameter  the parameter.
    220         */
    221        protected void ignoreParameter(final String parameter) {
    222            this.ignoredParameters.add (parameter);
    223        }
    224
    225        /**
    226         * Returns a flag that indicates whether or not the specified parameter is
    227         * ignored.
    228         *
    229         * @param parameter  the parameter.
    230         *
    231         * @return The flag.
    232         */
    233        protected boolean isParameterIgnored (final String parameter) {
    234            return this.ignoredParameters.contains(parameter);
    235        }
    236
    237      private void readObject(final ObjectInputStream in)
    238          throws IOException, ClassNotFoundException {
    239        in.defaultReadObject();
    240        readBeanDescription(getObjectClass(), false);
    241      }
    242
    243      private void readBeanDescription(final Class className, final boolean init) {
    244        try {
    245            this.properties = new HashMap();
    246
    247            final BeanInfo bi = Introspector.getBeanInfo(className);
    248            final PropertyDescriptor[] propertyDescriptors
    249                = bi.getPropertyDescriptors();
    250            for (int i = 0; i < propertyDescriptors.length; i++)
    251            {
    252                final PropertyDescriptor propertyDescriptor = propertyDescriptors[i];
    253                final Method readMethod = propertyDescriptor.getReadMethod();
    254                final Method writeMethod = propertyDescriptor.getWriteMethod();
    255                if (isValidMethod(readMethod, 0) && isValidMethod(writeMethod, 1))
    256                {
    257                    final String name = propertyDescriptor.getName();
    258                    this.properties.put(name, propertyDescriptor);
    259                    if (init) {
    260                        super.setParameterDefinition(name,
    261                                propertyDescriptor.getPropertyType());
    262                    }
    263                }
    264            }
    265        }
    266        catch (IntrospectionException e) {
    267            Log.error ("Unable to build bean description", e);
    268        }
    269      }
    270    }