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 }