Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/struts/action/DynaActionForm.java


1   /*
2    * $Id: DynaActionForm.java 54929 2004-10-16 16:38:42Z germuska $ 
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;
22  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  
27  import javax.servlet.ServletRequest;
28  import javax.servlet.http.HttpServletRequest;
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: 54929 $ $Date: 2004-10-16 09:38:42 -0700 (Sat, 16 Oct 2004) $
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 dynaValues = new HashMap();
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 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 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 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 name, String key) {
160 
161         Object value = dynaValues.get(name);
162         if (value == null) {
163             throw new NullPointerException
164                 ("No mapped value for '" + name + "(" + key + ")'");
165         } else if (value instanceof Map) {
166             return (((Map) value).containsKey(key));
167         } else {
168             throw new IllegalArgumentException
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 get(String name) {
186 
187         // Return any non-null value for the specified property
188         Object 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 type = getDynaProperty(name).getType();
195         if (type == null) {
196             throw new NullPointerException
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((byte) 0));
208         } else if (type == Character.TYPE) {
209             return (new Character((char) 0));
210         } else if (type == Double.TYPE) {
211             return (new Double(0.0));
212         } else if (type == Float.TYPE) {
213             return (new Float((float) 0.0));
214         } else if (type == Integer.TYPE) {
215             return (new Integer(0));
216         } else if (type == Long.TYPE) {
217             return (new Long(0));
218         } else if (type == Short.TYPE) {
219             return (new Short((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 get(String name, int index) {
244 
245         Object value = dynaValues.get(name);
246         if (value == null) {
247             throw new NullPointerException
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) {
252             return ((List) value).get(index);
253         } else {
254             throw new IllegalArgumentException
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 get(String name, String key) {
275 
276         Object value = dynaValues.get(name);
277         if (value == null) {
278             throw new NullPointerException
279                 ("No mapped value for '" + name + "(" + key + ")'");
280         } else if (value instanceof Map) {
281             return (((Map) value).get(key));
282         } else {
283             throw new IllegalArgumentException
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 getString(String name) {
305 
306         return (String) 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[] getStrings(String name) {
326 
327         return (String[]) 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 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 name, String key) {
379 
380         Object value = dynaValues.get(name);
381         if (value == null) {
382             throw new NullPointerException
383                 ("No mapped value for '" + name + "(" + key + ")'");
384         } else if (value instanceof Map) {
385             ((Map) value).remove(key);
386         } else {
387             throw new IllegalArgumentException
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 name, Object value) {
410 
411         DynaProperty descriptor = getDynaProperty(name);
412         if (descriptor.getType() == null) {
413             throw new NullPointerException
414                 ("The type for property " + name + " is invalid");
415         }
416         if (value == null) {
417             if (descriptor.getType().isPrimitive()) {
418                 throw new NullPointerException
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 name, int index, Object value) {
450 
451         Object prop = dynaValues.get(name);
452         if (prop == null) {
453             throw new NullPointerException
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) {
458             try {
459                 ((List) prop).set(index, value);
460             } catch (ClassCastException e) {
461                 throw new ConversionException(e.getMessage());
462             }
463         } else {
464             throw new IllegalArgumentException
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 name, String key, Object value) {
486 
487         Object prop = dynaValues.get(name);
488         if (prop == null) {
489             throw new NullPointerException
490                 ("No mapped value for '" + name + "(" + key + ")'");
491         } else if (prop instanceof Map) {
492             ((Map) prop).put(key, value);
493         } else {
494             throw new IllegalArgumentException
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 toString() {
508 
509         StringBuffer sb = new StringBuffer("DynaActionForm[dynaClass=");
510         sb.append(getDynaClass().getName());
511         DynaProperty props[] = getDynaClass().getDynaProperties();
512         if (props == null) {
513             props = new DynaProperty[0];
514         }
515         for (int i = 0; i < props.length; i++) {
516             sb.append(',');
517             sb.append(props[i].getName());
518             sb.append('=');
519             Object value = get(props[i].getName());
520             if (value == null) {
521                 sb.append("<NULL>");
522             } else if (value.getClass().isArray()) {
523                 int n = Array.getLength(value);
524                 sb.append("{");
525                 for (int j = 0; j < n; j++) {
526                     if (j > 0) {
527                         sb.append(',');
528                     }
529                     sb.append(Array.get(value, j));
530                 }
531                 sb.append("}");
532             } else if (value instanceof List) {
533                 int n = ((List) value).size();
534                 sb.append("{");
535                 for (int j = 0; j < n; j++) {
536                     if (j > 0) {
537                         sb.append(',');
538                     }
539                     sb.append(((List) value).get(j));
540                 }
541                 sb.append("}");
542             } else if (value instanceof Map) {
543                 int n = 0;
544                 Iterator keys = ((Map) value).keySet().iterator();
545                 sb.append("{");
546                 while (keys.hasNext()) {
547                     if (n > 0) {
548                         sb.append(',');
549                     }
550                     n++;
551                     Object key = keys.next();
552                     sb.append(key);
553                     sb.append('=');
554                     sb.append(((Map) value).get(key));
555                 }
556                 sb.append("}");
557             } else {
558                 sb.append(value);
559             }
560         }
561         sb.append("]");
562         return (sb.toString());
563 
564     }
565 
566 
567     // -------------------------------------------------------- Package Methods
568 
569 
570     /**
571      * <p>Set the <code>DynaActionFormClass</code> instance with which we are
572      * associated.</p>
573      *
574      * @param dynaClass The DynaActionFormClass instance for this bean
575      */
576     void setDynaActionFormClass(DynaActionFormClass dynaClass) {
577 
578         this.dynaClass = dynaClass;
579 
580     }
581 
582 
583     // ------------------------------------------------------ Protected Methods
584 
585 
586     /**
587      * <p>Return the property descriptor for the specified property name.</p>
588      *
589      * @param name Name of the property for which to retrieve the descriptor
590      *
591      * @exception IllegalArgumentException if this is not a valid property
592      *  name for our DynaClass
593      */
594     protected DynaProperty getDynaProperty(String name) {
595 
596         DynaProperty descriptor = getDynaClass().getDynaProperty(name);
597         if (descriptor == null) {
598             throw new IllegalArgumentException
599                 ("Invalid property name '" + name + "'");
600         }
601         return (descriptor);
602 
603     }
604 
605 
606     /**
607      * <p>Indicates if an object of the source class is assignable to the
608      * destination class.</p>
609      *
610      * @param dest Destination class
611      * @param source Source class
612      */
613     protected boolean isDynaAssignable(Class dest, Class source) {
614 
615         if (dest.isAssignableFrom(source) ||
616             ((dest == Boolean.TYPE) && (source == Boolean.class)) ||
617             ((dest == Byte.TYPE) && (source == Byte.class)) ||
618             ((dest == Character.TYPE) && (source == Character.class)) ||
619             ((dest == Double.TYPE) && (source == Double.class)) ||
620             ((dest == Float.TYPE) && (source == Float.class)) ||
621             ((dest == Integer.TYPE) && (source == Integer.class)) ||
622             ((dest == Long.TYPE) && (source == Long.class)) ||
623             ((dest == Short.TYPE) && (source == Short.class))) {
624             return (true);
625         } else {
626             return (false);
627         }
628 
629     }
630 
631 
632 }