KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > struts > action > DynaActionForm


1 /*
2  * $Id: DynaActionForm.java 168012 2005-05-03 22:39:10Z hrabago $
3  *
4  * Copyright 2000-2004 The Apache Software Foundation.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18
19 package org.apache.struts.action;
20
21 import java.lang.reflect.Array JavaDoc;
22 import java.util.HashMap JavaDoc;
23 import java.util.Iterator JavaDoc;
24 import java.util.List JavaDoc;
25 import java.util.Map JavaDoc;
26
27 import javax.servlet.ServletRequest JavaDoc;
28 import javax.servlet.http.HttpServletRequest JavaDoc;
29
30 import org.apache.commons.beanutils.ConversionException;
31 import org.apache.commons.beanutils.DynaBean;
32 import org.apache.commons.beanutils.DynaClass;
33 import org.apache.commons.beanutils.DynaProperty;
34 import org.apache.struts.config.FormBeanConfig;
35 import org.apache.struts.config.FormPropertyConfig;
36
37
38 /**
39  * <p>Specialized subclass of <code>ActionForm</code> that allows the creation
40  * of form beans with dynamic sets of properties, without requiring the
41  * developer to create a Java class for each type of form bean.</p>
42  *
43  * <p><strong>USAGE NOTE</strong> - Since Struts 1.1, the
44  * <code>reset</code> method no longer initializes property values to those
45  * specified in <code>&lt;form-property&gt;</code> elements in the Struts
46  * module configuration file. If you wish to utilize that behavior, the
47  * simplest solution is to subclass <code>DynaActionForm</code> and call
48  * the <code>initialize</code> method inside it.</p>
49  *
50  * @version $Rev: 168012 $ $Date: 2005-05-03 23:39:10 +0100 (Tue, 03 May 2005) $
51  * @since Struts 1.1
52  */

53 public class DynaActionForm extends ActionForm implements DynaBean {
54
55
56     // ----------------------------------------------------- Instance Variables
57

58
59     /**
60      * <p>The <code>DynaActionFormClass</code> with which we are associated.
61      * </p>
62      */

63     protected DynaActionFormClass dynaClass = null;
64
65
66     /**
67      * <p>The set of property values for this <code>DynaActionForm</code>,
68      * keyed by property name.</p>
69      */

70     protected HashMap JavaDoc dynaValues = new HashMap JavaDoc();
71
72
73     // ----------------------------------------------------- ActionForm Methods
74

75
76     /**
77      * <p>Initialize all bean properties to their initial values, as specified
78      * in the {@link FormPropertyConfig} elements associated with the
79      * definition of this <code>DynaActionForm</code>.</p>
80      *
81      * @param mapping The mapping used to select this instance
82      */

83     public void initialize(ActionMapping mapping) {
84
85         String JavaDoc name = mapping.getName();
86         if (name == null) {
87             return;
88         }
89         FormBeanConfig config =
90             mapping.getModuleConfig().findFormBeanConfig(name);
91         if (config == null) {
92             return;
93         }
94
95         initialize(config);
96     }
97
98     public void initialize(FormBeanConfig config) {
99
100         FormPropertyConfig props[] = config.findFormPropertyConfigs();
101         for (int i = 0; i < props.length; i++) {
102             set(props[i].getName(), props[i].initial());
103         }
104
105     }
106
107
108     // :FIXME: Is there any point in retaining these reset methods
109
// since they now simply replicate the superclass behavior?
110

111     /**
112      * <p>Reset bean properties to their default state, as needed.
113      * This method is called before the properties are repopulated by
114      * the controller.</p>
115      *
116      * <p>The default implementation attempts to forward to the HTTP
117      * version of this method.</p>
118      *
119      * @param mapping The mapping used to select this instance
120      * @param request The servlet request we are processing
121      */

122     public void reset(ActionMapping mapping, ServletRequest JavaDoc request) {
123         super.reset(mapping,request);
124     }
125
126
127     /**
128      * <p>Reset bean properties to their default state, as needed. This method is
129      * called before the properties are repopulated by the controller.</p>
130      *
131      * <p>The default implementation (since Struts 1.1) does nothing.
132      * Subclasses may override this method to reset bean properties to
133      * default values, or the <code>initialize</code> method may be used to
134      * initialize property values to those provided in the form property
135      * configuration information (which was the behavior of
136      * this method in some release candidates).</p>
137      *
138      * @param mapping The mapping used to select this instance
139      * @param request The servlet request we are processing
140      */

141     public void reset(ActionMapping mapping, HttpServletRequest JavaDoc request) {
142         super.reset(mapping,request);
143     }
144
145
146     // ------------------------------------------------------- DynaBean Methods
147

148
149     /**
150      * <p>Indicates if the specified mapped property contain a value for the
151      * specified key value.</p>
152      *
153      * @param name Name of the property to check
154      * @param key Name of the key to check
155      *
156      * @exception IllegalArgumentException if there is no property
157      * of the specified name
158      */

159     public boolean contains(String JavaDoc name, String JavaDoc key) {
160
161         Object JavaDoc value = dynaValues.get(name);
162         if (value == null) {
163             throw new NullPointerException JavaDoc
164                 ("No mapped value for '" + name + "(" + key + ")'");
165         } else if (value instanceof Map JavaDoc) {
166             return (((Map JavaDoc) value).containsKey(key));
167         } else {
168             throw new IllegalArgumentException JavaDoc
169                 ("Non-mapped property for '" + name + "(" + key + ")'");
170         }
171
172     }
173
174
175     /**
176      * <p>Return the value of a simple property with the specified name.</p>
177      *
178      * @param name Name of the property whose value is to be retrieved
179      *
180      * @exception IllegalArgumentException if there is no property
181      * of the specified name
182      * @exception NullPointerException if the type specified for the
183      * property is invalid
184      */

185     public Object JavaDoc get(String JavaDoc name) {
186
187         // Return any non-null value for the specified property
188
Object JavaDoc value = dynaValues.get(name);
189         if (value != null) {
190             return (value);
191         }
192
193         // Return a null value for a non-primitive property
194
Class JavaDoc type = getDynaProperty(name).getType();
195         if (type == null) {
196             throw new NullPointerException JavaDoc
197                 ("The type for property " + name + " is invalid");
198         }
199         if (!type.isPrimitive()) {
200             return (value);
201         }
202
203         // Manufacture default values for primitive properties
204
if (type == Boolean.TYPE) {
205             return (Boolean.FALSE);
206         } else if (type == Byte.TYPE) {
207             return (new Byte JavaDoc((byte) 0));
208         } else if (type == Character.TYPE) {
209             return (new Character JavaDoc((char) 0));
210         } else if (type == Double.TYPE) {
211             return (new Double JavaDoc(0.0));
212         } else if (type == Float.TYPE) {
213             return (new Float JavaDoc((float) 0.0));
214         } else if (type == Integer.TYPE) {
215             return (new Integer JavaDoc(0));
216         } else if (type == Long.TYPE) {
217             return (new Long JavaDoc(0));
218         } else if (type == Short.TYPE) {
219             return (new Short JavaDoc((short) 0));
220         } else {
221             return (null);
222         }
223
224     }
225
226
227     /**
228      * <p>Return the value of an indexed property with the specified name.
229      * </p>
230      *
231      * @param name Name of the property whose value is to be retrieved
232      * @param index Index of the value to be retrieved
233      *
234      * @exception IllegalArgumentException if there is no property
235      * of the specified name
236      * @exception IllegalArgumentException if the specified property
237      * exists, but is not indexed
238      * @exception IndexOutOfBoundsException if the specified index
239      * is outside the range of the underlying property
240      * @exception NullPointerException if no array or List has been
241      * initialized for this property
242      */

243     public Object JavaDoc get(String JavaDoc name, int index) {
244
245         Object JavaDoc value = dynaValues.get(name);
246         if (value == null) {
247             throw new NullPointerException JavaDoc
248                 ("No indexed value for '" + name + "[" + index + "]'");
249         } else if (value.getClass().isArray()) {
250             return (Array.get(value, index));
251         } else if (value instanceof List JavaDoc) {
252             return ((List JavaDoc) value).get(index);
253         } else {
254             throw new IllegalArgumentException JavaDoc
255                 ("Non-indexed property for '" + name + "[" + index + "]'");
256         }
257
258     }
259
260
261     /**
262      * <p>Return the value of a mapped property with the specified name,
263      * or <code>null</code> if there is no value for the specified key.
264      * </p>
265      *
266      * @param name Name of the property whose value is to be retrieved
267      * @param key Key of the value to be retrieved
268      *
269      * @exception IllegalArgumentException if there is no property
270      * of the specified name
271      * @exception IllegalArgumentException if the specified property
272      * exists, but is not mapped
273      */

274     public Object JavaDoc get(String JavaDoc name, String JavaDoc key) {
275
276         Object JavaDoc value = dynaValues.get(name);
277         if (value == null) {
278             throw new NullPointerException JavaDoc
279                 ("No mapped value for '" + name + "(" + key + ")'");
280         } else if (value instanceof Map JavaDoc) {
281             return (((Map JavaDoc) value).get(key));
282         } else {
283             throw new IllegalArgumentException JavaDoc
284                 ("Non-mapped property for '" + name + "(" + key + ")'");
285         }
286
287     }
288
289
290     /**
291      * <p>Return the value of a <code>String</code> property with the specified
292      * name. This is equivalent to calling
293      * <code>(String) dynaForm.get(name)</code>.</p>
294      *
295      * @param name Name of the property whose value is to be retrieved
296      *
297      * @throws IllegalArgumentException if there is no property
298      * of the specified name
299      * @throws NullPointerException if the type specified for the
300      * property is invalid
301      * @throws ClassCastException if the property is not a String.
302      * @since Struts 1.2
303      */

304     public String JavaDoc getString(String JavaDoc name) {
305
306         return (String JavaDoc) this.get(name);
307
308     }
309
310
311     /**
312      * <p>Return the value of a <code>String[]</code> property with the
313      * specified name. This is equivalent to calling
314      * <code>(String[]) dynaForm.get(name)</code>.</p>
315      *
316      * @param name Name of the property whose value is to be retrieved
317      *
318      * @throws IllegalArgumentException if there is no property
319      * of the specified name
320      * @throws NullPointerException if the type specified for the
321      * property is invalid
322      * @throws ClassCastException if the property is not a String[].
323      * @since Struts 1.2
324      */

325     public String JavaDoc[] getStrings(String JavaDoc name) {
326
327         return (String JavaDoc[]) this.get(name);
328
329     }
330
331
332     /**
333      * <p>Return the <code>DynaClass</code> instance that describes the set
334      * of properties available for this <code>DynaBean</code>.</p>
335      */

336     public DynaClass getDynaClass() {
337
338         return (this.dynaClass);
339
340     }
341
342
343     /**
344      * <p>Returns the <code>Map</code> containing the property values. This is
345      * done mostly to facilitate accessing the <code>DynaActionForm</code>
346      * through JavaBeans accessors, in order to use the JavaServer Pages
347      * Standard Tag Library (JSTL).</p>
348      *
349      * <p>For instance, the normal JSTL EL syntax for accessing an
350      * <code>ActionForm</code> would be something like this:
351      * <pre>
352      * ${formbean.prop}</pre>
353      * The JSTL EL syntax for accessing a <code>DynaActionForm</code> looks
354      * something like this (because of the presence of this
355      * <code>getMap()</code> method):
356      * <pre>
357      * ${dynabean.map.prop}</pre>
358      * </p>
359      */

360     public Map JavaDoc getMap() {
361
362         return (dynaValues);
363
364     }
365
366
367     /**
368      * <p>Remove any existing value for the specified key on the
369      * specified mapped property.</p>
370      *
371      * @param name Name of the property for which a value is to
372      * be removed
373      * @param key Key of the value to be removed
374      *
375      * @exception IllegalArgumentException if there is no property
376      * of the specified name
377      */

378     public void remove(String JavaDoc name, String JavaDoc key) {
379
380         Object JavaDoc value = dynaValues.get(name);
381         if (value == null) {
382             throw new NullPointerException JavaDoc
383                 ("No mapped value for '" + name + "(" + key + ")'");
384         } else if (value instanceof Map JavaDoc) {
385             ((Map JavaDoc) value).remove(key);
386         } else {
387             throw new IllegalArgumentException JavaDoc
388                 ("Non-mapped property for '" + name + "(" + key + ")'");
389         }
390
391     }
392
393
394     /**
395      * <p>Set the value of a simple property with the specified name.</p>
396      *
397      * @param name Name of the property whose value is to be set
398      * @param value Value to which this property is to be set
399      *
400      * @exception ConversionException if the specified value cannot be
401      * converted to the type required for this property
402      * @exception IllegalArgumentException if there is no property
403      * of the specified name
404      * @exception NullPointerException if the type specified for the
405      * property is invalid
406      * @exception NullPointerException if an attempt is made to set a
407      * primitive property to null
408      */

409     public void set(String JavaDoc name, Object JavaDoc value) {
410
411         DynaProperty descriptor = getDynaProperty(name);
412         if (descriptor.getType() == null) {
413             throw new NullPointerException JavaDoc
414                 ("The type for property " + name + " is invalid");
415         }
416         if (value == null) {
417             if (descriptor.getType().isPrimitive()) {
418                 throw new NullPointerException JavaDoc
419                     ("Primitive value for '" + name + "'");
420             }
421         } else if (!isDynaAssignable(descriptor.getType(), value.getClass())) {
422             throw new ConversionException
423                 ("Cannot assign value of type '" +
424                  value.getClass().getName() +
425                  "' to property '" + name + "' of type '" +
426                  descriptor.getType().getName() + "'");
427         }
428         dynaValues.put(name, value);
429
430     }
431
432
433     /**
434      * <p>Set the value of an indexed property with the specified name.</p>
435      *
436      * @param name Name of the property whose value is to be set
437      * @param index Index of the property to be set
438      * @param value Value to which this property is to be set
439      *
440      * @exception ConversionException if the specified value cannot be
441      * converted to the type required for this property
442      * @exception IllegalArgumentException if there is no property
443      * of the specified name
444      * @exception IllegalArgumentException if the specified property
445      * exists, but is not indexed
446      * @exception IndexOutOfBoundsException if the specified index
447      * is outside the range of the underlying property
448      */

449     public void set(String JavaDoc name, int index, Object JavaDoc value) {
450
451         Object JavaDoc prop = dynaValues.get(name);
452         if (prop == null) {
453             throw new NullPointerException JavaDoc
454                 ("No indexed value for '" + name + "[" + index + "]'");
455         } else if (prop.getClass().isArray()) {
456             Array.set(prop, index, value);
457         } else if (prop instanceof List JavaDoc) {
458             try {
459                 ((List JavaDoc) prop).set(index, value);
460             } catch (ClassCastException JavaDoc e) {
461                 throw new ConversionException(e.getMessage());
462             }
463         } else {
464             throw new IllegalArgumentException JavaDoc
465                 ("Non-indexed property for '" + name + "[" + index + "]'");
466         }
467
468     }
469
470
471     /**
472      * <p>Set the value of a mapped property with the specified name.</p>
473      *
474      * @param name Name of the property whose value is to be set
475      * @param key Key of the property to be set
476      * @param value Value to which this property is to be set
477      *
478      * @exception ConversionException if the specified value cannot be
479      * converted to the type required for this property
480      * @exception IllegalArgumentException if there is no property
481      * of the specified name
482      * @exception IllegalArgumentException if the specified property
483      * exists, but is not mapped
484      */

485     public void set(String JavaDoc name, String JavaDoc key, Object JavaDoc value) {
486
487         Object JavaDoc prop = dynaValues.get(name);
488         if (prop == null) {
489             throw new NullPointerException JavaDoc
490                 ("No mapped value for '" + name + "(" + key + ")'");
491         } else if (prop instanceof Map JavaDoc) {
492             ((Map JavaDoc) prop).put(key, value);
493         } else {
494             throw new IllegalArgumentException JavaDoc
495                 ("Non-mapped property for '" + name + "(" + key + ")'");
496         }
497
498     }
499
500
501     // --------------------------------------------------------- Public Methods
502

503
504     /**
505      * <p>Render a String representation of this object.</p>
506      */

507     public String JavaDoc toString() {
508
509         StringBuffer JavaDoc sb = new StringBuffer JavaDoc("DynaActionForm[dynaClass=");
510         DynaClass dynaClass = getDynaClass();
511         if (dynaClass == null) {
512             return sb.append("null]").toString();
513         }
514         
515         sb.append(dynaClass.getName());
516         DynaProperty props[] = dynaClass.getDynaProperties();
517         if (props == null) {
518             props = new DynaProperty[0];
519         }
520         for (int i = 0; i < props.length; i++) {
521             sb.append(',');
522             sb.append(props[i].getName());
523             sb.append('=');
524             Object JavaDoc value = get(props[i].getName());
525             if (value == null) {
526                 sb.append("<NULL>");
527             } else if (value.getClass().isArray()) {
528                 int n = Array.getLength(value);
529                 sb.append("{");
530                 for (int j = 0; j < n; j++) {
531                     if (j > 0) {
532                         sb.append(',');
533                     }
534                     sb.append(Array.get(value, j));
535                 }
536                 sb.append("}");
537             } else if (value instanceof List JavaDoc) {
538                 int n = ((List JavaDoc) value).size();
539                 sb.append("{");
540                 for (int j = 0; j < n; j++) {
541                     if (j > 0) {
542                         sb.append(',');
543                     }
544                     sb.append(((List JavaDoc) value).get(j));
545                 }
546                 sb.append("}");
547             } else if (value instanceof Map JavaDoc) {
548                 int n = 0;
549                 Iterator JavaDoc keys = ((Map JavaDoc) value).keySet().iterator();
550                 sb.append("{");
551                 while (keys.hasNext()) {
552                     if (n > 0) {
553                         sb.append(',');
554                     }
555                     n++;
556                     Object JavaDoc key = keys.next();
557                     sb.append(key);
558                     sb.append('=');
559                     sb.append(((Map JavaDoc) value).get(key));
560                 }
561                 sb.append("}");
562             } else {
563                 sb.append(value);
564             }
565         }
566         sb.append("]");
567         return (sb.toString());
568
569     }
570
571
572     // -------------------------------------------------------- Package Methods
573

574
575     /**
576      * <p>Set the <code>DynaActionFormClass</code> instance with which we are
577      * associated.</p>
578      *
579      * @param dynaClass The DynaActionFormClass instance for this bean
580      */

581     void setDynaActionFormClass(DynaActionFormClass dynaClass) {
582
583         this.dynaClass = dynaClass;
584
585     }
586
587
588     // ------------------------------------------------------ Protected Methods
589

590
591     /**
592      * <p>Return the property descriptor for the specified property name.</p>
593      *
594      * @param name Name of the property for which to retrieve the descriptor
595      *
596      * @exception IllegalArgumentException if this is not a valid property
597      * name for our DynaClass
598      */

599     protected DynaProperty getDynaProperty(String JavaDoc name) {
600
601         DynaProperty descriptor = getDynaClass().getDynaProperty(name);
602         if (descriptor == null) {
603             throw new IllegalArgumentException JavaDoc
604                 ("Invalid property name '" + name + "'");
605         }
606         return (descriptor);
607
608     }
609
610
611     /**
612      * <p>Indicates if an object of the source class is assignable to the
613      * destination class.</p>
614      *
615      * @param dest Destination class
616      * @param source Source class
617      */

618     protected boolean isDynaAssignable(Class JavaDoc dest, Class JavaDoc source) {
619
620         if (dest.isAssignableFrom(source) ||
621             ((dest == Boolean.TYPE) && (source == Boolean JavaDoc.class)) ||
622             ((dest == Byte.TYPE) && (source == Byte JavaDoc.class)) ||
623             ((dest == Character.TYPE) && (source == Character JavaDoc.class)) ||
624             ((dest == Double.TYPE) && (source == Double JavaDoc.class)) ||
625             ((dest == Float.TYPE) && (source == Float JavaDoc.class)) ||
626             ((dest == Integer.TYPE) && (source == Integer JavaDoc.class)) ||
627             ((dest == Long.TYPE) && (source == Long JavaDoc.class)) ||
628             ((dest == Short.TYPE) && (source == Short JavaDoc.class))) {
629             return (true);
630         } else {
631             return (false);
632         }
633
634     }
635
636
637 }
638
Popular Tags