1 /* 2 * $Id: DynaActionFormClass.java 471754 2006-11-06 14:55:09Z husted $ 3 * 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the 9 * "License"); you may not use this file except in compliance 10 * with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, 15 * software distributed under the License is distributed on an 16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 * KIND, either express or implied. See the License for the 18 * specific language governing permissions and limitations 19 * under the License. 20 */ 21 package org.apache.struts.action; 22 23 import org.apache.commons.beanutils.DynaBean; 24 import org.apache.commons.beanutils.DynaClass; 25 import org.apache.commons.beanutils.DynaProperty; 26 import org.apache.struts.config.FormBeanConfig; 27 import org.apache.struts.config.FormPropertyConfig; 28 import org.apache.struts.util.RequestUtils; 29 30 import java.io.Serializable; 31 32 import java.util.HashMap; 33 34 /** 35 * <p>Implementation of <code>DynaClass</code> for <code>DynaActionForm</code> 36 * classes that allow developers to define ActionForms without having to 37 * individually code all of the classes. <strong>NOTE</strong> - This class is 38 * only used in the internal implementation of dynamic action form beans. 39 * Application developers never need to consult this documentation.</p> 40 * 41 * @version $Rev: 471754 $ $Date: 2006-01-17 07:26:20 -0500 (Tue, 17 Jan 2006) 42 * $ 43 * @since Struts 1.1 44 */ 45 public class DynaActionFormClass implements DynaClass, Serializable { 46 // ----------------------------------------------------- Instance Variables 47 48 /** 49 * <p>The <code>DynaActionForm</code> implementation <code>Class</code> 50 * which we will use to create new bean instances.</p> 51 */ 52 protected transient Class beanClass = null; 53 54 /** 55 * <p>The form bean configuration information for this class.</p> 56 */ 57 protected FormBeanConfig config = null; 58 59 /** 60 * <p>The "dynamic class name" for this <code>DynaClass</code>.</p> 61 */ 62 protected String name = null; 63 64 /** 65 * <p>The set of dynamic properties that are part of this DynaClass.</p> 66 */ 67 protected DynaProperty[] properties = null; 68 69 /** 70 * <p>The set of dynamic properties that are part of this 71 * <code>DynaClass</code>, keyed by the property name. Individual 72 * descriptor instances will be the same instances as those in the 73 * <code>properties</code> list. 74 */ 75 protected HashMap propertiesMap = new HashMap(); 76 77 // ----------------------------------------------------------- Constructors 78 79 /** 80 * <p>Construct a new <code>DynaActionFormClass</code> for the specified 81 * form bean configuration. This constructor is private; 82 * <code>DynaActionFormClass</code> instances will be created as needed 83 * via calls to the static <code>createDynaActionFormClass()</code> 84 * method.</p> 85 * 86 * @param config The FormBeanConfig instance describing the properties of 87 * the bean to be created 88 * @throws IllegalArgumentException if the bean implementation class 89 * specified in the configuration is not 90 * DynaActionForm (or a subclass of 91 * DynaActionForm) 92 */ 93 public DynaActionFormClass(FormBeanConfig config) { 94 introspect(config); 95 } 96 97 // ------------------------------------------------------ DynaClass Methods 98 99 /** 100 * <p>Return the name of this <code>DynaClass</code> (analogous to the 101 * <code>getName()</code> method of <code>java.lang.Class</code>, which 102 * allows the same <code>DynaClass</code> implementation class to support 103 * different dynamic classes, with different sets of properties. 104 * 105 * @return The name of this <code>DynaClass</code>. 106 */ 107 public String getName() { 108 return (this.name); 109 } 110 111 /** 112 * <p>Return a property descriptor for the specified property, if it 113 * exists; otherwise, return <code>null</code>.</p> 114 * 115 * @param name Name of the dynamic property for which a descriptor is 116 * requested 117 * @return A property descriptor for the specified property. 118 * @throws IllegalArgumentException if no property name is specified 119 */ 120 public DynaProperty getDynaProperty(String name) { 121 if (name == null) { 122 throw new IllegalArgumentException("No property name specified"); 123 } 124 125 return ((DynaProperty) propertiesMap.get(name)); 126 } 127 128 /** 129 * <p>Return an array of <code>DynaProperty</code>s for the properties 130 * currently defined in this <code>DynaClass</code>. If no properties are 131 * defined, a zero-length array will be returned.</p> 132 * 133 * @return An array of property instances for this class. 134 */ 135 public DynaProperty[] getDynaProperties() { 136 return (properties); 137 138 // :FIXME: Should we really be implementing 139 // getBeanInfo instead, which returns property descriptors 140 // and a bunch of other stuff? 141 } 142 143 /** 144 * <p>Instantiate and return a new {@link DynaActionForm} instance, 145 * associated with this <code>DynaActionFormClass</code>. The properties 146 * of the returned {@link DynaActionForm} will have been initialized to 147 * the default values specified in the form bean configuration 148 * information.</p> 149 * 150 * @return A new {@link DynaActionForm} instance. 151 * @throws IllegalAccessException if the Class or the appropriate 152 * constructor is not accessible 153 * @throws InstantiationException if this Class represents an abstract 154 * class, an array class, a primitive type, 155 * or void; or if instantiation fails for 156 * some other reason 157 */ 158 public DynaBean newInstance() 159 throws IllegalAccessException, InstantiationException { 160 DynaActionForm dynaBean = (DynaActionForm) getBeanClass().newInstance(); 161 162 dynaBean.setDynaActionFormClass(this); 163 164 FormPropertyConfig[] props = config.findFormPropertyConfigs(); 165 166 for (int i = 0; i < props.length; i++) { 167 dynaBean.set(props[i].getName(), props[i].initial()); 168 } 169 170 return (dynaBean); 171 } 172 173 // --------------------------------------------------------- Public Methods 174 175 /** 176 * <p>Render a <code>String</code> representation of this object.</p> 177 * 178 * @return The string representation of this instance. 179 */ 180 public String toString() { 181 StringBuffer sb = new StringBuffer("DynaActionFormBean[name="); 182 183 sb.append(name); 184 185 DynaProperty[] props = getDynaProperties(); 186 187 if (props == null) { 188 props = new DynaProperty[0]; 189 } 190 191 for (int i = 0; i < props.length; i++) { 192 sb.append(','); 193 sb.append(props[i].getName()); 194 sb.append('/'); 195 sb.append(props[i].getType()); 196 } 197 198 sb.append("]"); 199 200 return (sb.toString()); 201 } 202 203 // --------------------------------------------------------- Static Methods 204 205 /** 206 * Return the <code>DynaActionFormClass</code> instance for the specified 207 * form bean configuration instance. 208 * 209 * @param config The config for which the class should be created. 210 * @return The instance for the specified form bean config. 211 */ 212 public static DynaActionFormClass createDynaActionFormClass( 213 FormBeanConfig config) { 214 return config.getDynaActionFormClass(); 215 } 216 217 // ------------------------------------------------------ Protected Methods 218 219 /** 220 * <p>Return the implementation class we are using to construct new 221 * instances, re-introspecting our {@link FormBeanConfig} if necessary 222 * (that is, after being deserialized, since <code>beanClass</code> is 223 * marked transient).</p> 224 * 225 * @return The implementation class used to construct new instances. 226 */ 227 protected Class getBeanClass() { 228 if (beanClass == null) { 229 introspect(config); 230 } 231 232 return (beanClass); 233 } 234 235 /** 236 * <p>Introspect our form bean configuration to identify the supported 237 * properties.</p> 238 * 239 * @param config The FormBeanConfig instance describing the properties of 240 * the bean to be created 241 * @throws IllegalArgumentException if the bean implementation class 242 * specified in the configuration is not 243 * DynaActionForm (or a subclass of 244 * DynaActionForm) 245 */ 246 protected void introspect(FormBeanConfig config) { 247 this.config = config; 248 249 // Validate the ActionFormBean implementation class 250 try { 251 beanClass = RequestUtils.applicationClass(config.getType()); 252 } catch (Throwable t) { 253 throw new IllegalArgumentException( 254 "Cannot instantiate ActionFormBean class '" + config.getType() 255 + "': " + t); 256 } 257 258 if (!DynaActionForm.class.isAssignableFrom(beanClass)) { 259 throw new IllegalArgumentException("Class '" + config.getType() 260 + "' is not a subclass of " 261 + "'org.apache.struts.action.DynaActionForm'"); 262 } 263 264 // Set the name we will know ourselves by from the form bean name 265 this.name = config.getName(); 266 267 // Look up the property descriptors for this bean class 268 FormPropertyConfig[] descriptors = config.findFormPropertyConfigs(); 269 270 if (descriptors == null) { 271 descriptors = new FormPropertyConfig[0]; 272 } 273 274 // Create corresponding dynamic property definitions 275 properties = new DynaProperty[descriptors.length]; 276 277 for (int i = 0; i < descriptors.length; i++) { 278 properties[i] = 279 new DynaProperty(descriptors[i].getName(), 280 descriptors[i].getTypeClass()); 281 propertiesMap.put(properties[i].getName(), properties[i]); 282 } 283 } 284 285 // -------------------------------------------------------- Private Methods 286 }