1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 19 package org.apache.commons.beanutils; 20 21 22 import java.io.Serializable; 23 import java.lang.reflect.Constructor; 24 import java.lang.reflect.InvocationTargetException; 25 import java.util.HashMap; 26 27 28 /** 29 * <p>Minimal implementation of the <code>DynaClass</code> interface. Can be 30 * used as a convenience base class for more sophisticated implementations.</p> * 31 * <p><strong>IMPLEMENTATION NOTE</strong> - The <code>DynaBean</code> 32 * implementation class supplied to our constructor MUST have a one-argument 33 * constructor of its own that accepts a <code>DynaClass</code>. This is 34 * used to associate the DynaBean instance with this DynaClass.</p> 35 * 36 * @version $Id$ 37 */ 38 39 public class BasicDynaClass implements DynaClass, Serializable { 40 41 42 // ----------------------------------------------------------- Constructors 43 44 45 /** 46 * Construct a new BasicDynaClass with default parameters. 47 */ 48 public BasicDynaClass() { 49 50 this(null, null, null); 51 52 } 53 54 55 /** 56 * Construct a new BasicDynaClass with the specified parameters. 57 * 58 * @param name Name of this DynaBean class 59 * @param dynaBeanClass The implementation class for new instances 60 */ 61 public BasicDynaClass(final String name, final Class<?> dynaBeanClass) { 62 63 this(name, dynaBeanClass, null); 64 65 } 66 67 68 /** 69 * Construct a new BasicDynaClass with the specified parameters. 70 * 71 * @param name Name of this DynaBean class 72 * @param dynaBeanClass The implementation class for new intances 73 * @param properties Property descriptors for the supported properties 74 */ 75 public BasicDynaClass(final String name, Class<?> dynaBeanClass, 76 final DynaProperty[] properties) { 77 78 super(); 79 if (name != null) { 80 this.name = name; 81 } 82 if (dynaBeanClass == null) { 83 dynaBeanClass = BasicDynaBean.class; 84 } 85 setDynaBeanClass(dynaBeanClass); 86 if (properties != null) { 87 setProperties(properties); 88 } 89 90 } 91 92 93 // ----------------------------------------------------- Instance Variables 94 95 96 /** 97 * The constructor of the <code>dynaBeanClass</code> that we will use 98 * for creating new instances. 99 */ 100 protected transient Constructor<?> constructor = null; 101 102 103 /** 104 * The method signature of the constructor we will use to create 105 * new DynaBean instances. 106 */ 107 protected static Class<?>[] constructorTypes = { DynaClass.class }; 108 109 110 /** 111 * The argument values to be passed to the constructore we will use 112 * to create new DynaBean instances. 113 */ 114 protected Object[] constructorValues = { this }; 115 116 117 /** 118 * The <code>DynaBean</code> implementation class we will use for 119 * creating new instances. 120 */ 121 protected Class<?> dynaBeanClass = BasicDynaBean.class; 122 123 124 /** 125 * The "name" of this DynaBean class. 126 */ 127 protected String name = this.getClass().getName(); 128 129 130 /** 131 * The set of dynamic properties that are part of this DynaClass. 132 */ 133 protected DynaProperty[] properties = new DynaProperty[0]; 134 135 136 /** 137 * The set of dynamic properties that are part of this DynaClass, 138 * keyed by the property name. Individual descriptor instances will 139 * be the same instances as those in the <code>properties</code> list. 140 */ 141 protected HashMap<String, DynaProperty> propertiesMap = new HashMap<String, DynaProperty>(); 142 143 144 // ------------------------------------------------------ DynaClass Methods 145 146 147 /** 148 * Return the name of this DynaClass (analogous to the 149 * <code>getName()</code> method of <code>java.lang.Class</code), which 150 * allows the same <code>DynaClass</code> implementation class to support 151 * different dynamic classes, with different sets of properties. 152 * 153 * @return the name of the DynaClass 154 */ 155 public String getName() { 156 157 return (this.name); 158 159 } 160 161 162 /** 163 * Return a property descriptor for the specified property, if it exists; 164 * otherwise, return <code>null</code>. 165 * 166 * @param name Name of the dynamic property for which a descriptor 167 * is requested 168 * @return The descriptor for the specified property 169 * 170 * @throws IllegalArgumentException if no property name is specified 171 */ 172 public DynaProperty getDynaProperty(final String name) { 173 174 if (name == null) { 175 throw new IllegalArgumentException 176 ("No property name specified"); 177 } 178 return propertiesMap.get(name); 179 180 } 181 182 183 /** 184 * <p>Return an array of <code>ProperyDescriptors</code> for the properties 185 * currently defined in this DynaClass. If no properties are defined, a 186 * zero-length array will be returned.</p> 187 * 188 * <p><strong>FIXME</strong> - Should we really be implementing 189 * <code>getBeanInfo()</code> instead, which returns property descriptors 190 * and a bunch of other stuff?</p> 191 * 192 * @return the set of properties for this DynaClass 193 */ 194 public DynaProperty[] getDynaProperties() { 195 196 return (properties); 197 198 } 199 200 201 /** 202 * Instantiate and return a new DynaBean instance, associated 203 * with this DynaClass. 204 * 205 * @return A new <code>DynaBean</code> instance 206 * @throws IllegalAccessException if the Class or the appropriate 207 * constructor is not accessible 208 * @throws InstantiationException if this Class represents an abstract 209 * class, an array class, a primitive type, or void; or if instantiation 210 * fails for some other reason 211 */ 212 public DynaBean newInstance() 213 throws IllegalAccessException, InstantiationException { 214 215 try { 216 // Refind the constructor after a deserialization (if needed) 217 if (constructor == null) { 218 setDynaBeanClass(this.dynaBeanClass); 219 } 220 // Invoke the constructor to create a new bean instance 221 return ((DynaBean) constructor.newInstance(constructorValues)); 222 } catch (final InvocationTargetException e) { 223 throw new InstantiationException 224 (e.getTargetException().getMessage()); 225 } 226 227 } 228 229 230 // --------------------------------------------------------- Public Methods 231 232 233 /** 234 * Return the Class object we will use to create new instances in the 235 * <code>newInstance()</code> method. This Class <strong>MUST</strong> 236 * implement the <code>DynaBean</code> interface. 237 * 238 * @return The class of the {@link DynaBean} 239 */ 240 public Class<?> getDynaBeanClass() { 241 242 return (this.dynaBeanClass); 243 244 } 245 246 247 // ------------------------------------------------------ Protected Methods 248 249 250 /** 251 * Set the Class object we will use to create new instances in the 252 * <code>newInstance()</code> method. This Class <strong>MUST</strong> 253 * implement the <code>DynaBean</code> interface. 254 * 255 * @param dynaBeanClass The new Class object 256 * 257 * @throws IllegalArgumentException if the specified Class does not 258 * implement the <code>DynaBean</code> interface 259 */ 260 protected void setDynaBeanClass(final Class<?> dynaBeanClass) { 261 262 // Validate the argument type specified 263 if (dynaBeanClass.isInterface()) { 264 throw new IllegalArgumentException 265 ("Class " + dynaBeanClass.getName() + 266 " is an interface, not a class"); 267 } 268 if (!DynaBean.class.isAssignableFrom(dynaBeanClass)) { 269 throw new IllegalArgumentException 270 ("Class " + dynaBeanClass.getName() + 271 " does not implement DynaBean"); 272 } 273 274 // Identify the Constructor we will use in newInstance() 275 try { 276 this.constructor = dynaBeanClass.getConstructor(constructorTypes); 277 } catch (final NoSuchMethodException e) { 278 throw new IllegalArgumentException 279 ("Class " + dynaBeanClass.getName() + 280 " does not have an appropriate constructor"); 281 } 282 this.dynaBeanClass = dynaBeanClass; 283 284 } 285 286 287 /** 288 * Set the list of dynamic properties supported by this DynaClass. 289 * 290 * @param properties List of dynamic properties to be supported 291 */ 292 protected void setProperties(final DynaProperty[] properties) { 293 294 this.properties = properties; 295 propertiesMap.clear(); 296 for (DynaProperty propertie : properties) { 297 propertiesMap.put(propertie.getName(), propertie); 298 } 299 300 } 301 302 303 }