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 }