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: 556229 $ $Date: 2007-07-14 07:11:19 +0100 (Sat, 14 Jul 2007) $
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    }