    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *      http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017
    018
    019    package org.apache.commons.beanutils;
    020
    021
    022    import java.io.Serializable;
    023    import java.lang.reflect.Constructor;
    024    import java.lang.reflect.InvocationTargetException;
    025    import java.util.HashMap;
    026
    027
    028    /**
    029     * <p>Minimal implementation of the <code>DynaClass</code> interface.  Can be
    030     * used as a convenience base class for more sophisticated implementations.</p> *
    031     * <p><strong>IMPLEMENTATION NOTE</strong> - The <code>DynaBean</code>
    032     * implementation class supplied to our constructor MUST have a one-argument
    033     * constructor of its own that accepts a <code>DynaClass</code>.  This is
    034     * used to associate the DynaBean instance with this DynaClass.</p>
    035     *
    036     * @author Craig McClanahan
    037     * @version $Revision: 926529 $ $Date: 2010-03-23 11:44:24 +0000 (Tue, 23 Mar 2010) $
    038     */
    039
    040    public class BasicDynaClass implements DynaClass, Serializable {
    041
    042
    043        // ----------------------------------------------------------- Constructors
    044
    045
    046        /**
    047         * Construct a new BasicDynaClass with default parameters.
    048         */
    049        public BasicDynaClass() {
    050
    051            this(null, null, null);
    052
    053        }
    054
    055
    056        /**
    057         * Construct a new BasicDynaClass with the specified parameters.
    058         *
    059         * @param name Name of this DynaBean class
    060         * @param dynaBeanClass The implementation class for new instances
    061         */
    062        public BasicDynaClass(String name, Class dynaBeanClass) {
    063
    064            this(name, dynaBeanClass, null);
    065
    066        }
    067
    068
    069        /**
    070         * Construct a new BasicDynaClass with the specified parameters.
    071         *
    072         * @param name Name of this DynaBean class
    073         * @param dynaBeanClass The implementation class for new intances
    074         * @param properties Property descriptors for the supported properties
    075         */
    076        public BasicDynaClass(String name, Class dynaBeanClass,
    077                              DynaProperty[] properties) {
    078
    079            super();
    080            if (name != null) {
    081                this.name = name;
    082            }
    083            if (dynaBeanClass == null) {
    084                dynaBeanClass = BasicDynaBean.class;
    085            }
    086            setDynaBeanClass(dynaBeanClass);
    087            if (properties != null) {
    088                setProperties(properties);
    089            }
    090
    091        }
    092
    093
    094        // ----------------------------------------------------- Instance Variables
    095
    096
    097        /**
    098         * The constructor of the <code>dynaBeanClass</code> that we will use
    099         * for creating new instances.
    100         */
    101        protected transient Constructor constructor = null;
    102
    103
    104        /**
    105         * The method signature of the constructor we will use to create
    106         * new DynaBean instances.
    107         */
    108        protected static Class[] constructorTypes = { DynaClass.class };
    109
    110
    111        /**
    112         * The argument values to be passed to the constructore we will use
    113         * to create new DynaBean instances.
    114         */
    115        protected Object[] constructorValues = { this };
    116
    117
    118        /**
    119         * The <code>DynaBean</code> implementation class we will use for
    120         * creating new instances.
    121         */
    122        protected Class dynaBeanClass = BasicDynaBean.class;
    123
    124
    125        /**
    126         * The "name" of this DynaBean class.
    127         */
    128        protected String name = this.getClass().getName();
    129
    130
    131        /**
    132         * The set of dynamic properties that are part of this DynaClass.
    133         */
    134        protected DynaProperty[] properties = new DynaProperty[0];
    135
    136
    137        /**
    138         * The set of dynamic properties that are part of this DynaClass,
    139         * keyed by the property name.  Individual descriptor instances will
    140         * be the same instances as those in the <code>properties</code> list.
    141         */
    142        protected HashMap propertiesMap = new HashMap();
    143
    144
    145        // ------------------------------------------------------ DynaClass Methods
    146
    147
    148        /**
    149         * Return the name of this DynaClass (analogous to the
    150         * <code>getName()</code> method of <code>java.lang.Class</code), which
    151         * allows the same <code>DynaClass</code> implementation class to support
    152         * different dynamic classes, with different sets of properties.
    153         *
    154         * @return the name of the DynaClass
    155         */
    156        public String getName() {
    157
    158            return (this.name);
    159
    160        }
    161
    162
    163        /**
    164         * Return a property descriptor for the specified property, if it exists;
    165         * otherwise, return <code>null</code>.
    166         *
    167         * @param name Name of the dynamic property for which a descriptor
    168         *  is requested
    169         * @return The descriptor for the specified property
    170         *
    171         * @exception IllegalArgumentException if no property name is specified
    172         */
    173        public DynaProperty getDynaProperty(String name) {
    174
    175            if (name == null) {
    176                throw new IllegalArgumentException
    177                        ("No property name specified");
    178            }
    179            return ((DynaProperty) propertiesMap.get(name));
    180
    181        }
    182
    183
    184        /**
    185         * <p>Return an array of <code>ProperyDescriptors</code> for the properties
    186         * currently defined in this DynaClass.  If no properties are defined, a
    187         * zero-length array will be returned.</p>
    188         *
    189         * <p><strong>FIXME</strong> - Should we really be implementing
    190         * <code>getBeanInfo()</code> instead, which returns property descriptors
    191         * and a bunch of other stuff?</p>
    192         *
    193         * @return the set of properties for this DynaClass
    194         */
    195        public DynaProperty[] getDynaProperties() {
    196
    197            return (properties);
    198
    199        }
    200
    201
    202        /**
    203         * Instantiate and return a new DynaBean instance, associated
    204         * with this DynaClass.
    205         *
    206         * @return A new <code>DynaBean</code> instance
    207         * @exception IllegalAccessException if the Class or the appropriate
    208         *  constructor is not accessible
    209         * @exception InstantiationException if this Class represents an abstract
    210         *  class, an array class, a primitive type, or void; or if instantiation
    211         *  fails for some other reason
    212         */
    213        public DynaBean newInstance()
    214                throws IllegalAccessException, InstantiationException {
    215
    216            try {
    217                // Refind the constructor after a deserialization (if needed)
    218                if (constructor == null) {
    219                    setDynaBeanClass(this.dynaBeanClass);
    220                }
    221                // Invoke the constructor to create a new bean instance
    222                return ((DynaBean) constructor.newInstance(constructorValues));
    223            } catch (InvocationTargetException e) {
    224                throw new InstantiationException
    225                        (e.getTargetException().getMessage());
    226            }
    227
    228        }
    229
    230
    231        // --------------------------------------------------------- Public Methods
    232
    233
    234        /**
    235         * Return the Class object we will use to create new instances in the
    236         * <code>newInstance()</code> method.  This Class <strong>MUST</strong>
    237         * implement the <code>DynaBean</code> interface.
    238         *
    239         * @return The class of the {@link DynaBean}
    240         */
    241        public Class getDynaBeanClass() {
    242
    243            return (this.dynaBeanClass);
    244
    245        }
    246
    247
    248        // ------------------------------------------------------ Protected Methods
    249
    250
    251        /**
    252         * Set the Class object we will use to create new instances in the
    253         * <code>newInstance()</code> method.  This Class <strong>MUST</strong>
    254         * implement the <code>DynaBean</code> interface.
    255         *
    256         * @param dynaBeanClass The new Class object
    257         *
    258         * @exception IllegalArgumentException if the specified Class does not
    259         *  implement the <code>DynaBean</code> interface
    260         */
    261        protected void setDynaBeanClass(Class dynaBeanClass) {
    262
    263            // Validate the argument type specified
    264            if (dynaBeanClass.isInterface()) {
    265                throw new IllegalArgumentException
    266                        ("Class " + dynaBeanClass.getName() +
    267                        " is an interface, not a class");
    268            }
    269            if (!DynaBean.class.isAssignableFrom(dynaBeanClass)) {
    270                throw new IllegalArgumentException
    271                        ("Class " + dynaBeanClass.getName() +
    272                        " does not implement DynaBean");
    273            }
    274
    275            // Identify the Constructor we will use in newInstance()
    276            try {
    277                this.constructor = dynaBeanClass.getConstructor(constructorTypes);
    278            } catch (NoSuchMethodException e) {
    279                throw new IllegalArgumentException
    280                        ("Class " + dynaBeanClass.getName() +
    281                        " does not have an appropriate constructor");
    282            }
    283            this.dynaBeanClass = dynaBeanClass;
    284
    285        }
    286
    287
    288        /**
    289         * Set the list of dynamic properties supported by this DynaClass.
    290         *
    291         * @param properties List of dynamic properties to be supported
    292         */
    293        protected void setProperties(DynaProperty[] properties) {
    294
    295            this.properties = properties;
    296            propertiesMap.clear();
    297            for (int i = 0; i < properties.length; i++) {
    298                propertiesMap.put(properties[i].getName(), properties[i]);
    299            }
    300
    301        }
    302
    303
    304    }