1 /*
2 * $Id$
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.config;
22
23 import java.lang.reflect.Array;
24 import java.lang.reflect.InvocationTargetException;
25
26 import org.apache.commons.beanutils.ConvertUtils;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 /**
31 * <p>A JavaBean representing the configuration information of a
32 * <code><form-property></code> element in a Struts configuration
33 * file.<p>
34 *
35 * @version $Rev$ $Date: 2005-11-12 11:52:08 -0500 (Sat, 12 Nov 2005)$
36 * @since Struts 1.1
37 */
38 public class FormPropertyConfig extends BaseConfig {
39 private static final long serialVersionUID = 8436264202472421426L;
40
41 /**
42 * The {@code Log} instance for this class.
43 */
44 private transient final Logger log =
45 LoggerFactory.getLogger(FormPropertyConfig.class);
46
47 // ----------------------------------------------------- Instance Variables
48 // ------------------------------------------------------------- Properties
49
50 /**
51 * String representation of the initial value for this property.
52 */
53 protected String initial = null;
54
55 /**
56 * The JavaBean property name of the property described by this element.
57 */
58 protected String name = null;
59
60 /**
61 * <p>The conditions under which the property described by this element
62 * should be reset to its <code>initial</code> value when the form's
63 * <code>reset</code> method is called.</p> <p>This may be set to true (to
64 * always reset the property) or a comma-separated list of HTTP request
65 * methods.</p>
66 *
67 * @since Struts 1.3
68 */
69 protected String reset = null;
70
71 /**
72 * <p>The size of the array to be created if this property is an array
73 * type and there is no specified <code>initial</code> value. This value
74 * must be non-negative.</p>
75 *
76 * @since Struts 1.1
77 */
78 protected int size = 0;
79
80 /**
81 * The fully qualified Java class name of the implementation class of this
82 * bean property, optionally followed by <code>[]</code> to indicate that
83 * the property is indexed.
84 */
85 protected String type = null;
86
87 // ----------------------------------------------------------- Constructors
88
89 /**
90 * Standard no-arguments constructor for dynamic instantiation.
91 */
92 public FormPropertyConfig() {
93 super();
94 }
95
96 /**
97 * Constructor that preconfigures the relevant properties.
98 *
99 * @param name Name of this property
100 * @param type Fully qualified class name of this property
101 * @param initial Initial value of this property (if any)
102 */
103 public FormPropertyConfig(String name, String type, String initial) {
104 this(name, type, initial, 0);
105 }
106
107 /**
108 * Constructor that preconfigures the relevant properties.
109 *
110 * @param name Name of this property
111 * @param type Fully qualified class name of this property
112 * @param initial Initial value of this property (if any)
113 * @param reset The conditions under which this property will be reset
114 * to its initial value.
115 */
116 public FormPropertyConfig(String name, String type, String initial,
117 String reset) {
118 this(name, type, initial, reset, 0);
119 }
120
121 /**
122 * Constructor that preconfigures the relevant properties.
123 *
124 * @param name Name of this property
125 * @param type Fully qualified class name of this property
126 * @param initial Initial value of this property (if any)
127 * @param size Size of the array to be created if this property is an
128 * array with no defined initial value
129 */
130 public FormPropertyConfig(String name, String type, String initial, int size) {
131 this(name, type, initial, null, size);
132 }
133
134 /**
135 * Constructor that preconfigures the relevant properties.
136 *
137 * @param name Name of this property
138 * @param type Fully qualified class name of this property
139 * @param initial Initial value of this property (if any)
140 * @param size Size of the array to be created if this property is an
141 * array with no defined initial value
142 * @param reset The conditions under which this property will be reset
143 * to its initial value.
144 */
145 public FormPropertyConfig(String name, String type, String initial,
146 String reset, int size) {
147 super();
148 setName(name);
149 setType(type);
150 setInitial(initial);
151 setReset(reset);
152 setSize(size);
153 }
154
155 public String getInitial() {
156 return (this.initial);
157 }
158
159 public void setInitial(String initial) {
160 if (configured) {
161 throw new IllegalStateException("Configuration is frozen");
162 }
163
164 this.initial = initial;
165 }
166
167 public String getName() {
168 return (this.name);
169 }
170
171 public void setName(String name) {
172 if (configured) {
173 throw new IllegalStateException("Configuration is frozen");
174 }
175
176 this.name = name;
177 }
178
179 public String getReset() {
180 return (this.reset);
181 }
182
183 public void setReset(String reset) {
184 if (configured) {
185 throw new IllegalStateException("Configuration is frozen");
186 }
187
188 this.reset = reset;
189 }
190
191 public int getSize() {
192 return (this.size);
193 }
194
195 public void setSize(int size) {
196 if (configured) {
197 throw new IllegalStateException("Configuration is frozen");
198 }
199
200 if (size < 0) {
201 throw new IllegalArgumentException("size < 0");
202 }
203
204 this.size = size;
205 }
206
207 public String getType() {
208 return (this.type);
209 }
210
211 public void setType(String type) {
212 if (configured) {
213 throw new IllegalStateException("Configuration is frozen");
214 }
215
216 this.type = type;
217 }
218
219 /**
220 * Return a Class corresponds to the value specified for the
221 * <code>type</code> property, taking into account the trailing "[]" for
222 * arrays (as well as the ability to specify primitive Java types).
223 */
224 public Class<?> getTypeClass() {
225 // Identify the base class (in case an array was specified)
226 String baseType = getType();
227 boolean indexed = false;
228
229 if (baseType.endsWith("[]")) {
230 baseType = baseType.substring(0, baseType.length() - 2);
231 indexed = true;
232 }
233
234 // Construct an appropriate Class instance for the base class
235 Class<?> baseClass = null;
236
237 if ("boolean".equals(baseType)) {
238 baseClass = Boolean.TYPE;
239 } else if ("byte".equals(baseType)) {
240 baseClass = Byte.TYPE;
241 } else if ("char".equals(baseType)) {
242 baseClass = Character.TYPE;
243 } else if ("double".equals(baseType)) {
244 baseClass = Double.TYPE;
245 } else if ("float".equals(baseType)) {
246 baseClass = Float.TYPE;
247 } else if ("int".equals(baseType)) {
248 baseClass = Integer.TYPE;
249 } else if ("long".equals(baseType)) {
250 baseClass = Long.TYPE;
251 } else if ("short".equals(baseType)) {
252 baseClass = Short.TYPE;
253 } else {
254 ClassLoader classLoader =
255 Thread.currentThread().getContextClassLoader();
256
257 if (classLoader == null) {
258 classLoader = this.getClass().getClassLoader();
259 }
260
261 try {
262 baseClass = classLoader.loadClass(baseType);
263 } catch (ClassNotFoundException ex) {
264 log.error("Class '{}' not found for property '{}'",
265 baseType, name);
266 baseClass = null;
267 }
268 }
269
270 // Return the base class or an array appropriately
271 if (indexed) {
272 return (Array.newInstance(baseClass, 0).getClass());
273 } else {
274 return (baseClass);
275 }
276 }
277
278 // --------------------------------------------------------- Public Methods
279
280 /**
281 * <p>Return an object representing the initial value of this property.
282 * This is calculated according to the following algorithm:</p>
283 *
284 * <ul>
285 *
286 * <li>If the value you have specified for the <code>type</code> property
287 * represents an array (i.e. it ends with "[]"):
288 *
289 * <ul>
290 *
291 * <li>If you have specified a value for the <code>initial</code>
292 * property, <code>ConvertUtils.convert</code> will be called to convert
293 * it into an instance of the specified array type.</li>
294 *
295 * <li>If you have not specified a value for the <code>initial</code>
296 * property, an array of the length specified by the <code>size</code>
297 * property will be created. Each element of the array will be
298 * instantiated via the zero-args constructor on the specified class (if
299 * any). Otherwise, <code>null</code> will be returned.</li>
300 *
301 * </ul></li>
302 *
303 * <li>If the value you have specified for the <code>type</code> property
304 * does not represent an array:
305 *
306 * <ul>
307 *
308 * <li>If you have specified a value for the <code>initial</code>
309 * property, <code>ConvertUtils.convert</code> will be called to convert
310 * it into an object instance.</li>
311 *
312 * <li>If you have not specified a value for the <code>initial</code>
313 * attribute, Struts will instantiate an instance via the zero-args
314 * constructor on the specified class (if any). Otherwise,
315 * <code>null</code> will be returned.</li>
316 *
317 * </ul></li>
318 *
319 * </ul>
320 */
321 public Object initial() {
322 Object initialValue = null;
323
324 try {
325 Class<?> clazz = getTypeClass();
326
327 if (clazz.isArray()) {
328 if (initial != null) {
329 initialValue = ConvertUtils.convert(initial, clazz);
330 } else {
331 initialValue =
332 Array.newInstance(clazz.getComponentType(), size);
333
334 if (!(clazz.getComponentType().isPrimitive())) {
335 for (int i = 0; i < size; i++) {
336 try {
337 Array.set(initialValue, i,
338 clazz.getComponentType().getDeclaredConstructor().newInstance());
339 } catch (Throwable t) {
340 log.error("Unable to create instance of {} for property={}, "
341 + "type={}, initial={}, size={}.",
342 clazz.getName(), name, type, initial, size);
343
344 //FIXME: Should we just dump the entire application/module ?
345 }
346 }
347 }
348 }
349 } else {
350 if (initial != null) {
351 initialValue = ConvertUtils.convert(initial, clazz);
352 } else {
353 initialValue = clazz.getDeclaredConstructor().newInstance();
354 }
355 }
356 } catch (Throwable t) {
357 initialValue = null;
358 }
359
360 return (initialValue);
361 }
362
363 /**
364 * <p>Inherit values that have not been overridden from the provided
365 * config object. Subclasses overriding this method should verify that
366 * the given parameter is of a class that contains a property it is trying
367 * to inherit:</p>
368 * <pre>
369 * if (config instanceof MyCustomFormPropertyConfig) {
370 * MyCustomFormPropertyConfig myConfig =
371 * (MyCustomFormPropertyConfig) config;
372 *
373 * if (getMyCustomProp() == null) {
374 * setMyCustomProp(myConfig.getMyCustomProp());
375 * }
376 * }
377 * </pre>
378 *
379 * @param config The object that this instance will be inheriting its
380 * values from.
381 */
382 public void inheritFrom(FormPropertyConfig config)
383 throws IllegalAccessException, InvocationTargetException,
384 InstantiationException, ClassNotFoundException {
385 if (configured) {
386 throw new IllegalStateException("Configuration is frozen");
387 }
388
389 if (getInitial() == null) {
390 setInitial(config.getInitial());
391 }
392
393 if (getName() == null) {
394 setName(config.getName());
395 }
396
397 if (getSize() == 0) {
398 setSize(config.getSize());
399 }
400
401 if (getType() == null) {
402 setType(config.getType());
403 }
404
405 inheritProperties(config);
406 }
407
408 /**
409 * Return a String representation of this object.
410 */
411 public String toString() {
412 StringBuilder sb = new StringBuilder("FormPropertyConfig[");
413
414 sb.append("name=");
415 sb.append(this.name);
416 sb.append(",type=");
417 sb.append(this.type);
418 sb.append(",initial=");
419 sb.append(this.initial);
420 sb.append(",reset=");
421 sb.append(this.reset);
422 sb.append("]");
423
424 return (sb.toString());
425 }
426 }