KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > beanutils > PropertyUtilsBean


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

16
17 package org.apache.commons.beanutils;
18
19
20 import java.beans.BeanInfo JavaDoc;
21 import java.beans.IndexedPropertyDescriptor JavaDoc;
22 import java.beans.IntrospectionException JavaDoc;
23 import java.beans.Introspector JavaDoc;
24 import java.beans.PropertyDescriptor JavaDoc;
25 import java.lang.reflect.Array JavaDoc;
26 import java.lang.reflect.InvocationTargetException JavaDoc;
27 import java.lang.reflect.Method JavaDoc;
28 import java.util.HashMap JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.List JavaDoc;
31 import java.util.Map JavaDoc;
32
33 import org.apache.commons.collections.FastHashMap;
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36
37
38 /**
39  * Utility methods for using Java Reflection APIs to facilitate generic
40  * property getter and setter operations on Java objects. Much of this
41  * code was originally included in <code>BeanUtils</code>, but has been
42  * separated because of the volume of code involved.
43  * <p>
44  * In general, the objects that are examined and modified using these
45  * methods are expected to conform to the property getter and setter method
46  * naming conventions described in the JavaBeans Specification (Version 1.0.1).
47  * No data type conversions are performed, and there are no usage of any
48  * <code>PropertyEditor</code> classes that have been registered, although
49  * a convenient way to access the registered classes themselves is included.
50  * <p>
51  * For the purposes of this class, five formats for referencing a particular
52  * property value of a bean are defined, with the layout of an identifying
53  * String in parentheses:
54  * <ul>
55  * <li><strong>Simple (<code>name</code>)</strong> - The specified
56  * <code>name</code> identifies an individual property of a particular
57  * JavaBean. The name of the actual getter or setter method to be used
58  * is determined using standard JavaBeans instrospection, so that (unless
59  * overridden by a <code>BeanInfo</code> class, a property named "xyz"
60  * will have a getter method named <code>getXyz()</code> or (for boolean
61  * properties only) <code>isXyz()</code>, and a setter method named
62  * <code>setXyz()</code>.</li>
63  * <li><strong>Nested (<code>name1.name2.name3</code>)</strong> The first
64  * name element is used to select a property getter, as for simple
65  * references above. The object returned for this property is then
66  * consulted, using the same approach, for a property getter for a
67  * property named <code>name2</code>, and so on. The property value that
68  * is ultimately retrieved or modified is the one identified by the
69  * last name element.</li>
70  * <li><strong>Indexed (<code>name[index]</code>)</strong> - The underlying
71  * property value is assumed to be an array, or this JavaBean is assumed
72  * to have indexed property getter and setter methods. The appropriate
73  * (zero-relative) entry in the array is selected. <code>List</code>
74  * objects are now also supported for read/write. You simply need to define
75  * a getter that returns the <code>List</code></li>
76  * <li><strong>Mapped (<code>name(key)</code>)</strong> - The JavaBean
77  * is assumed to have an property getter and setter methods with an
78  * additional attribute of type <code>java.lang.String</code>.</li>
79  * <li><strong>Combined (<code>name1.name2[index].name3(key)</code>)</strong> -
80  * Combining mapped, nested, and indexed references is also
81  * supported.</li>
82  * </ul>
83  *
84  * @author Craig R. McClanahan
85  * @author Ralph Schaer
86  * @author Chris Audley
87  * @author Rey François
88  * @author Gregor Raıman
89  * @author Jan Sorensen
90  * @author Scott Sanders
91  * @version $Revision: 1.14.2.1 $ $Date: 2004/07/27 21:31:00 $
92  * @see PropertyUtils
93  * @since 1.7
94  */

95
96 public class PropertyUtilsBean {
97
98     // --------------------------------------------------------- Class Methods
99

100     protected static PropertyUtilsBean getInstance() {
101         return BeanUtilsBean.getInstance().getPropertyUtils();
102     }
103
104     // --------------------------------------------------------- Variables
105

106     /**
107      * The cache of PropertyDescriptor arrays for beans we have already
108      * introspected, keyed by the java.lang.Class of this object.
109      */

110     private FastHashMap descriptorsCache = null;
111     private FastHashMap mappedDescriptorsCache = null;
112     
113     /** Log instance */
114     private Log log = LogFactory.getLog(PropertyUtils.class);
115     
116     // ---------------------------------------------------------- Constructors
117

118     /** Base constructor */
119     public PropertyUtilsBean() {
120         descriptorsCache = new FastHashMap();
121         descriptorsCache.setFast(true);
122         mappedDescriptorsCache = new FastHashMap();
123         mappedDescriptorsCache.setFast(true);
124     }
125
126
127     // --------------------------------------------------------- Public Methods
128

129
130     /**
131      * Clear any cached property descriptors information for all classes
132      * loaded by any class loaders. This is useful in cases where class
133      * loaders are thrown away to implement class reloading.
134      */

135     public void clearDescriptors() {
136
137         descriptorsCache.clear();
138         mappedDescriptorsCache.clear();
139         Introspector.flushCaches();
140
141     }
142
143
144     /**
145      * <p>Copy property values from the "origin" bean to the "destination" bean
146      * for all cases where the property names are the same (even though the
147      * actual getter and setter methods might have been customized via
148      * <code>BeanInfo</code> classes). No conversions are performed on the
149      * actual property values -- it is assumed that the values retrieved from
150      * the origin bean are assignment-compatible with the types expected by
151      * the destination bean.</p>
152      *
153      * <p>If the origin "bean" is actually a <code>Map</code>, it is assumed
154      * to contain String-valued <strong>simple</strong> property names as the keys, pointing
155      * at the corresponding property values that will be set in the destination
156      * bean.<strong>Note</strong> that this method is intended to perform
157      * a "shallow copy" of the properties and so complex properties
158      * (for example, nested ones) will not be copied.</p>
159      *
160      * @param dest Destination bean whose properties are modified
161      * @param orig Origin bean whose properties are retrieved
162      *
163      * @exception IllegalAccessException if the caller does not have
164      * access to the property accessor method
165      * @exception IllegalArgumentException if the <code>dest</code> or
166      * <code>orig</code> argument is null
167      * @exception InvocationTargetException if the property accessor method
168      * throws an exception
169      * @exception NoSuchMethodException if an accessor method for this
170      * propety cannot be found
171      */

172     public void copyProperties(Object JavaDoc dest, Object JavaDoc orig)
173             throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc,
174             NoSuchMethodException JavaDoc {
175
176         if (dest == null) {
177             throw new IllegalArgumentException JavaDoc
178                     ("No destination bean specified");
179         }
180         if (orig == null) {
181             throw new IllegalArgumentException JavaDoc("No origin bean specified");
182         }
183
184         if (orig instanceof DynaBean) {
185             DynaProperty origDescriptors[] =
186                 ((DynaBean) orig).getDynaClass().getDynaProperties();
187             for (int i = 0; i < origDescriptors.length; i++) {
188                 String JavaDoc name = origDescriptors[i].getName();
189                 if (dest instanceof DynaBean) {
190                     if (isWriteable(dest, name)) {
191                         Object JavaDoc value = ((DynaBean) orig).get(name);
192                         ((DynaBean) dest).set(name, value);
193                     }
194                 } else /* if (dest is a standard JavaBean) */ {
195                     if (isWriteable(dest, name)) {
196                         Object JavaDoc value = ((DynaBean) orig).get(name);
197                         setSimpleProperty(dest, name, value);
198                     }
199                 }
200             }
201         } else if (orig instanceof Map JavaDoc) {
202             Iterator JavaDoc names = ((Map JavaDoc) orig).keySet().iterator();
203             while (names.hasNext()) {
204                 String JavaDoc name = (String JavaDoc) names.next();
205                 if (dest instanceof DynaBean) {
206                     if (isWriteable(dest, name)) {
207                         Object JavaDoc value = ((Map JavaDoc) orig).get(name);
208                         ((DynaBean) dest).set(name, value);
209                     }
210                 } else /* if (dest is a standard JavaBean) */ {
211                     if (isWriteable(dest, name)) {
212                         Object JavaDoc value = ((Map JavaDoc) orig).get(name);
213                         setSimpleProperty(dest, name, value);
214                     }
215                 }
216             }
217         } else /* if (orig is a standard JavaBean) */ {
218             PropertyDescriptor JavaDoc origDescriptors[] =
219                 getPropertyDescriptors(orig);
220             for (int i = 0; i < origDescriptors.length; i++) {
221                 String JavaDoc name = origDescriptors[i].getName();
222                 if (isReadable(orig, name)) {
223                     if (dest instanceof DynaBean) {
224                         if (isWriteable(dest, name)) {
225                             Object JavaDoc value = getSimpleProperty(orig, name);
226                             ((DynaBean) dest).set(name, value);
227                         }
228                     } else /* if (dest is a standard JavaBean) */ {
229                         if (isWriteable(dest, name)) {
230                             Object JavaDoc value = getSimpleProperty(orig, name);
231                             setSimpleProperty(dest, name, value);
232                         }
233                     }
234                 }
235             }
236         }
237
238     }
239
240
241     /**
242      * <p>Return the entire set of properties for which the specified bean
243      * provides a read method. This map contains the unconverted property
244      * values for all properties for which a read method is provided
245      * (i.e. where the <code>getReadMethod()</code> returns non-null).</p>
246      *
247      * <p><strong>FIXME</strong> - Does not account for mapped properties.</p>
248      *
249      * @param bean Bean whose properties are to be extracted
250      *
251      * @exception IllegalAccessException if the caller does not have
252      * access to the property accessor method
253      * @exception IllegalArgumentException if <code>bean</code> is null
254      * @exception InvocationTargetException if the property accessor method
255      * throws an exception
256      * @exception NoSuchMethodException if an accessor method for this
257      * propety cannot be found
258      */

259     public Map JavaDoc describe(Object JavaDoc bean)
260             throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc,
261             NoSuchMethodException JavaDoc {
262
263         if (bean == null) {
264             throw new IllegalArgumentException JavaDoc("No bean specified");
265         }
266         Map JavaDoc description = new HashMap JavaDoc();
267         if (bean instanceof DynaBean) {
268             DynaProperty descriptors[] =
269                 ((DynaBean) bean).getDynaClass().getDynaProperties();
270             for (int i = 0; i < descriptors.length; i++) {
271                 String JavaDoc name = descriptors[i].getName();
272                 description.put(name, getProperty(bean, name));
273             }
274         } else {
275             PropertyDescriptor JavaDoc descriptors[] =
276                 getPropertyDescriptors(bean);
277             for (int i = 0; i < descriptors.length; i++) {
278                 String JavaDoc name = descriptors[i].getName();
279                 if (descriptors[i].getReadMethod() != null)
280                     description.put(name, getProperty(bean, name));
281             }
282         }
283         return (description);
284
285     }
286
287
288     /**
289      * Return the value of the specified indexed property of the specified
290      * bean, with no type conversions. The zero-relative index of the
291      * required value must be included (in square brackets) as a suffix to
292      * the property name, or <code>IllegalArgumentException</code> will be
293      * thrown. In addition to supporting the JavaBeans specification, this
294      * method has been extended to support <code>List</code> objects as well.
295      *
296      * @param bean Bean whose property is to be extracted
297      * @param name <code>propertyname[index]</code> of the property value
298      * to be extracted
299      *
300      * @exception ArrayIndexOutOfBoundsException if the specified index
301      * is outside the valid range for the underlying array
302      * @exception IllegalAccessException if the caller does not have
303      * access to the property accessor method
304      * @exception IllegalArgumentException if <code>bean</code> or
305      * <code>name</code> is null
306      * @exception InvocationTargetException if the property accessor method
307      * throws an exception
308      * @exception NoSuchMethodException if an accessor method for this
309      * propety cannot be found
310      */

311     public Object JavaDoc getIndexedProperty(Object JavaDoc bean, String JavaDoc name)
312             throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc,
313             NoSuchMethodException JavaDoc {
314
315         if (bean == null) {
316             throw new IllegalArgumentException JavaDoc("No bean specified");
317         }
318         if (name == null) {
319             throw new IllegalArgumentException JavaDoc("No name specified");
320         }
321
322         // Identify the index of the requested individual property
323
int delim = name.indexOf(PropertyUtils.INDEXED_DELIM);
324         int delim2 = name.indexOf(PropertyUtils.INDEXED_DELIM2);
325         if ((delim < 0) || (delim2 <= delim)) {
326             throw new IllegalArgumentException JavaDoc("Invalid indexed property '" +
327                     name + "'");
328         }
329         int index = -1;
330         try {
331             String JavaDoc subscript = name.substring(delim + 1, delim2);
332             index = Integer.parseInt(subscript);
333         } catch (NumberFormatException JavaDoc e) {
334             throw new IllegalArgumentException JavaDoc("Invalid indexed property '" +
335                     name + "'");
336         }
337         name = name.substring(0, delim);
338
339         // Request the specified indexed property value
340
return (getIndexedProperty(bean, name, index));
341
342     }
343
344
345     /**
346      * Return the value of the specified indexed property of the specified
347      * bean, with no type conversions. In addition to supporting the JavaBeans
348      * specification, this method has been extended to support
349      * <code>List</code> objects as well.
350      *
351      * @param bean Bean whose property is to be extracted
352      * @param name Simple property name of the property value to be extracted
353      * @param index Index of the property value to be extracted
354      *
355      * @exception ArrayIndexOutOfBoundsException if the specified index
356      * is outside the valid range for the underlying array
357      * @exception IllegalAccessException if the caller does not have
358      * access to the property accessor method
359      * @exception IllegalArgumentException if <code>bean</code> or
360      * <code>name</code> is null
361      * @exception InvocationTargetException if the property accessor method
362      * throws an exception
363      * @exception NoSuchMethodException if an accessor method for this
364      * propety cannot be found
365      */

366     public Object JavaDoc getIndexedProperty(Object JavaDoc bean,
367                                             String JavaDoc name, int index)
368             throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc,
369             NoSuchMethodException JavaDoc {
370
371         if (bean == null) {
372             throw new IllegalArgumentException JavaDoc("No bean specified");
373         }
374         if (name == null) {
375             throw new IllegalArgumentException JavaDoc("No name specified");
376         }
377
378         // Handle DynaBean instances specially
379
if (bean instanceof DynaBean) {
380             DynaProperty descriptor =
381                     ((DynaBean) bean).getDynaClass().getDynaProperty(name);
382             if (descriptor == null) {
383                 throw new NoSuchMethodException JavaDoc("Unknown property '" +
384                         name + "'");
385             }
386             return (((DynaBean) bean).get(name, index));
387         }
388
389         // Retrieve the property descriptor for the specified property
390
PropertyDescriptor JavaDoc descriptor =
391                 getPropertyDescriptor(bean, name);
392         if (descriptor == null) {
393             throw new NoSuchMethodException JavaDoc("Unknown property '" +
394                     name + "'");
395         }
396
397         // Call the indexed getter method if there is one
398
if (descriptor instanceof IndexedPropertyDescriptor JavaDoc) {
399             Method JavaDoc readMethod = ((IndexedPropertyDescriptor JavaDoc) descriptor).
400                     getIndexedReadMethod();
401             if (readMethod != null) {
402                 Object JavaDoc subscript[] = new Object JavaDoc[1];
403                 subscript[0] = new Integer JavaDoc(index);
404                 try {
405                     return (invokeMethod(readMethod,bean, subscript));
406                 } catch (InvocationTargetException JavaDoc e) {
407                     if (e.getTargetException() instanceof
408                             ArrayIndexOutOfBoundsException JavaDoc) {
409                         throw (ArrayIndexOutOfBoundsException JavaDoc)
410                                 e.getTargetException();
411                     } else {
412                         throw e;
413                     }
414                 }
415             }
416         }
417
418         // Otherwise, the underlying property must be an array
419
Method JavaDoc readMethod = getReadMethod(descriptor);
420         if (readMethod == null) {
421             throw new NoSuchMethodException JavaDoc("Property '" + name +
422                     "' has no getter method");
423         }
424
425         // Call the property getter and return the value
426
Object JavaDoc value = invokeMethod(readMethod, bean, new Object JavaDoc[0]);
427         if (!value.getClass().isArray()) {
428             if (!(value instanceof java.util.List JavaDoc)) {
429                 throw new IllegalArgumentException JavaDoc("Property '" + name
430                         + "' is not indexed");
431             } else {
432                 //get the List's value
433
return ((java.util.List JavaDoc) value).get(index);
434             }
435         } else {
436             //get the array's value
437
return (Array.get(value, index));
438         }
439
440     }
441
442
443     /**
444      * Return the value of the specified mapped property of the
445      * specified bean, with no type conversions. The key of the
446      * required value must be included (in brackets) as a suffix to
447      * the property name, or <code>IllegalArgumentException</code> will be
448      * thrown.
449      *
450      * @param bean Bean whose property is to be extracted
451      * @param name <code>propertyname(key)</code> of the property value
452      * to be extracted
453      *
454      * @exception IllegalAccessException if the caller does not have
455      * access to the property accessor method
456      * @exception InvocationTargetException if the property accessor method
457      * throws an exception
458      * @exception NoSuchMethodException if an accessor method for this
459      * propety cannot be found
460      */

461     public Object JavaDoc getMappedProperty(Object JavaDoc bean, String JavaDoc name)
462             throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc,
463             NoSuchMethodException JavaDoc {
464
465         if (bean == null) {
466             throw new IllegalArgumentException JavaDoc("No bean specified");
467         }
468         if (name == null) {
469             throw new IllegalArgumentException JavaDoc("No name specified");
470         }
471
472         // Identify the index of the requested individual property
473
int delim = name.indexOf(PropertyUtils.MAPPED_DELIM);
474         int delim2 = name.indexOf(PropertyUtils.MAPPED_DELIM2);
475         if ((delim < 0) || (delim2 <= delim)) {
476             throw new IllegalArgumentException JavaDoc
477                     ("Invalid mapped property '" + name + "'");
478         }
479
480         // Isolate the name and the key
481
String JavaDoc key = name.substring(delim + 1, delim2);
482         name = name.substring(0, delim);
483
484         // Request the specified indexed property value
485
return (getMappedProperty(bean, name, key));
486
487     }
488
489
490     /**
491      * Return the value of the specified mapped property of the specified
492      * bean, with no type conversions.
493      *
494      * @param bean Bean whose property is to be extracted
495      * @param name Mapped property name of the property value to be extracted
496      * @param key Key of the property value to be extracted
497      *
498      * @exception IllegalAccessException if the caller does not have
499      * access to the property accessor method
500      * @exception InvocationTargetException if the property accessor method
501      * throws an exception
502      * @exception NoSuchMethodException if an accessor method for this
503      * propety cannot be found
504      */

505     public Object JavaDoc getMappedProperty(Object JavaDoc bean,
506                                            String JavaDoc name, String JavaDoc key)
507             throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc,
508             NoSuchMethodException JavaDoc {
509
510         if (bean == null) {
511             throw new IllegalArgumentException JavaDoc("No bean specified");
512         }
513         if (name == null) {
514             throw new IllegalArgumentException JavaDoc("No name specified");
515         }
516         if (key == null) {
517             throw new IllegalArgumentException JavaDoc("No key specified");
518         }
519
520         // Handle DynaBean instances specially
521
if (bean instanceof DynaBean) {
522             DynaProperty descriptor =
523                     ((DynaBean) bean).getDynaClass().getDynaProperty(name);
524             if (descriptor == null) {
525                 throw new NoSuchMethodException JavaDoc("Unknown property '" +
526                         name + "'");
527             }
528             return (((DynaBean) bean).get(name, key));
529         }
530
531         Object JavaDoc result = null;
532
533         // Retrieve the property descriptor for the specified property
534
PropertyDescriptor JavaDoc descriptor = getPropertyDescriptor(bean, name);
535         if (descriptor == null) {
536             throw new NoSuchMethodException JavaDoc("Unknown property '" +
537                     name + "'");
538         }
539
540         if (descriptor instanceof MappedPropertyDescriptor) {
541             // Call the keyed getter method if there is one
542
Method JavaDoc readMethod = ((MappedPropertyDescriptor) descriptor).
543                     getMappedReadMethod();
544             if (readMethod != null) {
545                 Object JavaDoc keyArray[] = new Object JavaDoc[1];
546                 keyArray[0] = key;
547                 result = invokeMethod(readMethod, bean, keyArray);
548             } else {
549                 throw new NoSuchMethodException JavaDoc("Property '" + name +
550                         "' has no mapped getter method");
551             }
552         } else {
553           /* means that the result has to be retrieved from a map */
554           Method JavaDoc readMethod = descriptor.getReadMethod();
555           if (readMethod != null) {
556             Object JavaDoc invokeResult = invokeMethod(readMethod, bean, new Object JavaDoc[0]);
557             /* test and fetch from the map */
558             if (invokeResult instanceof java.util.Map JavaDoc) {
559               result = ((java.util.Map JavaDoc)invokeResult).get(key);
560             }
561           } else {
562             throw new NoSuchMethodException JavaDoc("Property '" + name +
563                     "' has no mapped getter method");
564           }
565         }
566         return result;
567
568     }
569
570
571     /**
572      * <p>Return the mapped property descriptors for this bean class.</p>
573      *
574      * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
575      *
576      * @param beanClass Bean class to be introspected
577      * @deprecated This method should not be exposed
578      */

579     public FastHashMap getMappedPropertyDescriptors(Class JavaDoc beanClass) {
580
581         if (beanClass == null) {
582             return null;
583         }
584
585         // Look up any cached descriptors for this bean class
586
return (FastHashMap) mappedDescriptorsCache.get(beanClass);
587
588     }
589
590
591     /**
592      * <p>Return the mapped property descriptors for this bean.</p>
593      *
594      * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
595      *
596      * @param bean Bean to be introspected
597      * @deprecated This method should not be exposed
598      */

599     public FastHashMap getMappedPropertyDescriptors(Object JavaDoc bean) {
600
601         if (bean == null) {
602             return null;
603         }
604         return (getMappedPropertyDescriptors(bean.getClass()));
605
606     }
607
608
609     /**
610      * Return the value of the (possibly nested) property of the specified
611      * name, for the specified bean, with no type conversions.
612      *
613      * @param bean Bean whose property is to be extracted
614      * @param name Possibly nested name of the property to be extracted
615      *
616      * @exception IllegalAccessException if the caller does not have
617      * access to the property accessor method
618      * @exception IllegalArgumentException if <code>bean</code> or
619      * <code>name</code> is null
620      * @exception NestedNullException if a nested reference to a
621      * property returns null
622      * @exception InvocationTargetException
623      * if the property accessor method throws an exception
624      * @exception NoSuchMethodException if an accessor method for this
625      * propety cannot be found
626      */

627     public Object JavaDoc getNestedProperty(Object JavaDoc bean, String JavaDoc name)
628             throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc,
629             NoSuchMethodException JavaDoc {
630
631         if (bean == null) {
632             throw new IllegalArgumentException JavaDoc("No bean specified");
633         }
634         if (name == null) {
635             throw new IllegalArgumentException JavaDoc("No name specified");
636         }
637
638         int indexOfINDEXED_DELIM = -1;
639         int indexOfMAPPED_DELIM = -1;
640         int indexOfMAPPED_DELIM2 = -1;
641         int indexOfNESTED_DELIM = -1;
642         while (true) {
643             indexOfNESTED_DELIM = name.indexOf(PropertyUtils.NESTED_DELIM);
644             indexOfMAPPED_DELIM = name.indexOf(PropertyUtils.MAPPED_DELIM);
645             indexOfMAPPED_DELIM2 = name.indexOf(PropertyUtils.MAPPED_DELIM2);
646             if (indexOfMAPPED_DELIM2 >= 0 && indexOfMAPPED_DELIM >=0 &&
647                 (indexOfNESTED_DELIM < 0 || indexOfNESTED_DELIM > indexOfMAPPED_DELIM)) {
648                 indexOfNESTED_DELIM =
649                     name.indexOf(PropertyUtils.NESTED_DELIM, indexOfMAPPED_DELIM2);
650             } else {
651                 indexOfNESTED_DELIM = name.indexOf(PropertyUtils.NESTED_DELIM);
652             }
653             if (indexOfNESTED_DELIM < 0) {
654                 break;
655             }
656             String JavaDoc next = name.substring(0, indexOfNESTED_DELIM);
657             indexOfINDEXED_DELIM = next.indexOf(PropertyUtils.INDEXED_DELIM);
658             indexOfMAPPED_DELIM = next.indexOf(PropertyUtils.MAPPED_DELIM);
659             if (bean instanceof Map JavaDoc) {
660                 bean = ((Map JavaDoc) bean).get(next);
661             } else if (indexOfMAPPED_DELIM >= 0) {
662                 bean = getMappedProperty(bean, next);
663             } else if (indexOfINDEXED_DELIM >= 0) {
664                 bean = getIndexedProperty(bean, next);
665             } else {
666                 bean = getSimpleProperty(bean, next);
667             }
668             if (bean == null) {
669                 throw new NestedNullException
670                         ("Null property value for '" +
671                         name.substring(0, indexOfNESTED_DELIM) + "'");
672             }
673             name = name.substring(indexOfNESTED_DELIM + 1);
674         }
675
676         indexOfINDEXED_DELIM = name.indexOf(PropertyUtils.INDEXED_DELIM);
677         indexOfMAPPED_DELIM = name.indexOf(PropertyUtils.MAPPED_DELIM);
678
679         if (bean instanceof Map JavaDoc) {
680             bean = ((Map JavaDoc) bean).get(name);
681         } else if (indexOfMAPPED_DELIM >= 0) {
682             bean = getMappedProperty(bean, name);
683         } else if (indexOfINDEXED_DELIM >= 0) {
684             bean = getIndexedProperty(bean, name);
685         } else {
686             bean = getSimpleProperty(bean, name);
687         }
688         return bean;
689
690     }
691
692
693     /**
694      * Return the value of the specified property of the specified bean,
695      * no matter which property reference format is used, with no
696      * type conversions.
697      *
698      * @param bean Bean whose property is to be extracted
699      * @param name Possibly indexed and/or nested name of the property
700      * to be extracted
701      *
702      * @exception IllegalAccessException if the caller does not have
703      * access to the property accessor method
704      * @exception IllegalArgumentException if <code>bean</code> or
705      * <code>name</code> is null
706      * @exception InvocationTargetException if the property accessor method
707      * throws an exception
708      * @exception NoSuchMethodException if an accessor method for this
709      * propety cannot be found
710      */

711     public Object JavaDoc getProperty(Object JavaDoc bean, String JavaDoc name)
712             throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc,
713             NoSuchMethodException JavaDoc {
714
715         return (getNestedProperty(bean, name));
716
717     }
718
719
720     /**
721      * <p>Retrieve the property descriptor for the specified property of the
722      * specified bean, or return <code>null</code> if there is no such
723      * descriptor. This method resolves indexed and nested property
724      * references in the same manner as other methods in this class, except
725      * that if the last (or only) name element is indexed, the descriptor
726      * for the last resolved property itself is returned.</p>
727      *
728      * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
729      *
730      * @param bean Bean for which a property descriptor is requested
731      * @param name Possibly indexed and/or nested name of the property for
732      * which a property descriptor is requested
733      *
734      * @exception IllegalAccessException if the caller does not have
735      * access to the property accessor method
736      * @exception IllegalArgumentException if <code>bean</code> or
737      * <code>name</code> is null
738      * @exception IllegalArgumentException if a nested reference to a
739      * property returns null
740      * @exception InvocationTargetException if the property accessor method
741      * throws an exception
742      * @exception NoSuchMethodException if an accessor method for this
743      * propety cannot be found
744      */

745     public PropertyDescriptor JavaDoc getPropertyDescriptor(Object JavaDoc bean,
746                                                            String JavaDoc name)
747             throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc,
748             NoSuchMethodException JavaDoc {
749
750         if (bean == null) {
751             throw new IllegalArgumentException JavaDoc("No bean specified");
752         }
753         if (name == null) {
754             throw new IllegalArgumentException JavaDoc("No name specified");
755         }
756
757         // Resolve nested references
758
while (true) {
759             int period = findNextNestedIndex(name);
760             if (period < 0) {
761                 break;
762             }
763             String JavaDoc next = name.substring(0, period);
764             int indexOfINDEXED_DELIM = next.indexOf(PropertyUtils.INDEXED_DELIM);
765             int indexOfMAPPED_DELIM = next.indexOf(PropertyUtils.MAPPED_DELIM);
766             if (indexOfMAPPED_DELIM >= 0 &&
767                     (indexOfINDEXED_DELIM < 0 ||
768                     indexOfMAPPED_DELIM < indexOfINDEXED_DELIM)) {
769                 bean = getMappedProperty(bean, next);
770             } else {
771                 if (indexOfINDEXED_DELIM >= 0) {
772                     bean = getIndexedProperty(bean, next);
773                 } else {
774                     bean = getSimpleProperty(bean, next);
775                 }
776             }
777             if (bean == null) {
778                 throw new IllegalArgumentException JavaDoc
779                         ("Null property value for '" +
780                         name.substring(0, period) + "'");
781             }
782             name = name.substring(period + 1);
783         }
784
785         // Remove any subscript from the final name value
786
int left = name.indexOf(PropertyUtils.INDEXED_DELIM);
787         if (left >= 0) {
788             name = name.substring(0, left);
789         }
790         left = name.indexOf(PropertyUtils.MAPPED_DELIM);
791         if (left >= 0) {
792             name = name.substring(0, left);
793         }
794
795         // Look up and return this property from our cache
796
// creating and adding it to the cache if not found.
797
if ((bean == null) || (name == null)) {
798             return (null);
799         }
800         
801         PropertyDescriptor JavaDoc descriptors[] = getPropertyDescriptors(bean);
802         if (descriptors != null) {
803             
804             for (int i = 0; i < descriptors.length; i++) {
805                 if (name.equals(descriptors[i].getName()))
806                     return (descriptors[i]);
807             }
808         }
809
810         PropertyDescriptor JavaDoc result = null;
811         FastHashMap mappedDescriptors =
812                 getMappedPropertyDescriptors(bean);
813         if (mappedDescriptors == null) {
814             mappedDescriptors = new FastHashMap();
815             mappedDescriptors.setFast(true);
816             mappedDescriptorsCache.put(bean.getClass(), mappedDescriptors);
817         }
818         result = (PropertyDescriptor JavaDoc) mappedDescriptors.get(name);
819         if (result == null) {
820             // not found, try to create it
821
try {
822                 result =
823                         new MappedPropertyDescriptor(name, bean.getClass());
824             } catch (IntrospectionException JavaDoc ie) {
825             }
826             if (result != null) {
827                 mappedDescriptors.put(name, result);
828             }
829         }
830         
831         return result;
832
833     }
834     
835     private int findNextNestedIndex(String JavaDoc expression)
836     {
837         // walk back from the end to the start
838
// and find the first index that
839
int bracketCount = 0;
840         for (int i=0, size=expression.length(); i<size ; i++) {
841             char at = expression.charAt(i);
842             switch (at) {
843                 case PropertyUtils.NESTED_DELIM:
844                     if (bracketCount < 1) {
845                         return i;
846                     }
847                     break;
848                     
849                 case PropertyUtils.MAPPED_DELIM:
850                 case PropertyUtils.INDEXED_DELIM:
851                     // not bothered which
852
++bracketCount;
853                     break;
854                 
855                 case PropertyUtils.MAPPED_DELIM2:
856                 case PropertyUtils.INDEXED_DELIM2:
857                     // not bothered which
858
--bracketCount;
859                     break;
860             }
861         }
862         // can't find any
863
return -1;
864     }
865
866
867     /**
868      * <p>Retrieve the property descriptors for the specified class,
869      * introspecting and caching them the first time a particular bean class
870      * is encountered.</p>
871      *
872      * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
873      *
874      * @param beanClass Bean class for which property descriptors are requested
875      *
876      * @exception IllegalArgumentException if <code>beanClass</code> is null
877      */

878     public PropertyDescriptor JavaDoc[]
879             getPropertyDescriptors(Class JavaDoc beanClass) {
880
881         if (beanClass == null) {
882             throw new IllegalArgumentException JavaDoc("No bean class specified");
883         }
884
885         // Look up any cached descriptors for this bean class
886
PropertyDescriptor JavaDoc descriptors[] = null;
887         descriptors =
888                 (PropertyDescriptor JavaDoc[]) descriptorsCache.get(beanClass);
889         if (descriptors != null) {
890             return (descriptors);
891         }
892
893         // Introspect the bean and cache the generated descriptors
894
BeanInfo JavaDoc beanInfo = null;
895         try {
896             beanInfo = Introspector.getBeanInfo(beanClass);
897         } catch (IntrospectionException JavaDoc e) {
898             return (new PropertyDescriptor JavaDoc[0]);
899         }
900         descriptors = beanInfo.getPropertyDescriptors();
901         if (descriptors == null) {
902             descriptors = new PropertyDescriptor JavaDoc[0];
903         }
904         descriptorsCache.put(beanClass, descriptors);
905         return (descriptors);
906
907     }
908
909
910     /**
911      * <p>Retrieve the property descriptors for the specified bean,
912      * introspecting and caching them the first time a particular bean class
913      * is encountered.</p>
914      *
915      * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
916      *
917      * @param bean Bean for which property descriptors are requested
918      *
919      * @exception IllegalArgumentException if <code>bean</code> is null
920      */

921     public PropertyDescriptor JavaDoc[] getPropertyDescriptors(Object JavaDoc bean) {
922
923         if (bean == null) {
924             throw new IllegalArgumentException JavaDoc("No bean specified");
925         }
926         return (getPropertyDescriptors(bean.getClass()));
927
928     }
929
930
931     /**
932      * <p>Return the Java Class repesenting the property editor class that has
933      * been registered for this property (if any). This method follows the
934      * same name resolution rules used by <code>getPropertyDescriptor()</code>,
935      * so if the last element of a name reference is indexed, the property
936      * editor for the underlying property's class is returned.</p>
937      *
938      * <p>Note that <code>null</code> will be returned if there is no property,
939      * or if there is no registered property editor class. Because this
940      * return value is ambiguous, you should determine the existence of the
941      * property itself by other means.</p>
942      *
943      * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
944      *
945      * @param bean Bean for which a property descriptor is requested
946      * @param name Possibly indexed and/or nested name of the property for
947      * which a property descriptor is requested
948      *
949      * @exception IllegalAccessException if the caller does not have
950      * access to the property accessor method
951      * @exception IllegalArgumentException if <code>bean</code> or
952      * <code>name</code> is null
953      * @exception IllegalArgumentException if a nested reference to a
954      * property returns null
955      * @exception InvocationTargetException if the property accessor method
956      * throws an exception
957      * @exception NoSuchMethodException if an accessor method for this
958      * propety cannot be found
959      */

960     public Class JavaDoc getPropertyEditorClass(Object JavaDoc bean, String JavaDoc name)
961             throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc,
962             NoSuchMethodException JavaDoc {
963
964         if (bean == null) {
965             throw new IllegalArgumentException JavaDoc("No bean specified");
966         }
967         if (name == null) {
968             throw new IllegalArgumentException JavaDoc("No name specified");
969         }
970
971         PropertyDescriptor JavaDoc descriptor =
972                 getPropertyDescriptor(bean, name);
973         if (descriptor != null) {
974             return (descriptor.getPropertyEditorClass());
975         } else {
976             return (null);
977         }
978
979     }
980
981
982     /**
983      * Return the Java Class representing the property type of the specified
984      * property, or <code>null</code> if there is no such property for the
985      * specified bean. This method follows the same name resolution rules
986      * used by <code>getPropertyDescriptor()</code>, so if the last element
987      * of a name reference is indexed, the type of the property itself will
988      * be returned. If the last (or only) element has no property with the
989      * specified name, <code>null</code> is returned.
990      *
991      * @param bean Bean for which a property descriptor is requested
992      * @param name Possibly indexed and/or nested name of the property for
993      * which a property descriptor is requested
994      *
995      * @exception IllegalAccessException if the caller does not have
996      * access to the property accessor method
997      * @exception IllegalArgumentException if <code>bean</code> or
998      * <code>name</code> is null
999      * @exception IllegalArgumentException if a nested reference to a
1000     * property returns null
1001     * @exception InvocationTargetException if the property accessor method
1002     * throws an exception
1003     * @exception NoSuchMethodException if an accessor method for this
1004     * propety cannot be found
1005     */

1006    public Class JavaDoc getPropertyType(Object JavaDoc bean, String JavaDoc name)
1007            throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc,
1008            NoSuchMethodException JavaDoc {
1009
1010        if (bean == null) {
1011            throw new IllegalArgumentException JavaDoc("No bean specified");
1012        }
1013        if (name == null) {
1014            throw new IllegalArgumentException JavaDoc("No name specified");
1015        }
1016
1017        // Special handling for DynaBeans
1018
if (bean instanceof DynaBean) {
1019            DynaProperty descriptor =
1020                    ((DynaBean) bean).getDynaClass().getDynaProperty(name);
1021            if (descriptor == null) {
1022                return (null);
1023            }
1024            Class JavaDoc type = descriptor.getType();
1025            if (type == null) {
1026                return (null);
1027            } else if (type.isArray()) {
1028                return (type.getComponentType());
1029            } else {
1030                return (type);
1031            }
1032        }
1033
1034        PropertyDescriptor JavaDoc descriptor =
1035                getPropertyDescriptor(bean, name);
1036        if (descriptor == null) {
1037            return (null);
1038        } else if (descriptor instanceof IndexedPropertyDescriptor JavaDoc) {
1039            return (((IndexedPropertyDescriptor JavaDoc) descriptor).
1040                    getIndexedPropertyType());
1041        } else if (descriptor instanceof MappedPropertyDescriptor) {
1042            return (((MappedPropertyDescriptor) descriptor).
1043                    getMappedPropertyType());
1044        } else {
1045            return (descriptor.getPropertyType());
1046        }
1047
1048    }
1049
1050
1051    /**
1052     * <p>Return an accessible property getter method for this property,
1053     * if there is one; otherwise return <code>null</code>.</p>
1054     *
1055     * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
1056     *
1057     * @param descriptor Property descriptor to return a getter for
1058     */

1059    public Method JavaDoc getReadMethod(PropertyDescriptor JavaDoc descriptor) {
1060
1061        return (MethodUtils.getAccessibleMethod(descriptor.getReadMethod()));
1062
1063    }
1064
1065
1066    /**
1067     * Return the value of the specified simple property of the specified
1068     * bean, with no type conversions.
1069     *
1070     * @param bean Bean whose property is to be extracted
1071     * @param name Name of the property to be extracted
1072     *
1073     * @exception IllegalAccessException if the caller does not have
1074     * access to the property accessor method
1075     * @exception IllegalArgumentException if <code>bean</code> or
1076     * <code>name</code> is null
1077     * @exception IllegalArgumentException if the property name
1078     * is nested or indexed
1079     * @exception InvocationTargetException if the property accessor method
1080     * throws an exception
1081     * @exception NoSuchMethodException if an accessor method for this
1082     * propety cannot be found
1083     */

1084    public Object JavaDoc getSimpleProperty(Object JavaDoc bean, String JavaDoc name)
1085            throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc,
1086            NoSuchMethodException JavaDoc {
1087
1088        if (bean == null) {
1089            throw new IllegalArgumentException JavaDoc("No bean specified");
1090        }
1091        if (name == null) {
1092            throw new IllegalArgumentException JavaDoc("No name specified");
1093        }
1094
1095        // Validate the syntax of the property name
1096
if (name.indexOf(PropertyUtils.NESTED_DELIM) >= 0) {
1097            throw new IllegalArgumentException JavaDoc
1098                    ("Nested property names are not allowed");
1099        } else if (name.indexOf(PropertyUtils.INDEXED_DELIM) >= 0) {
1100            throw new IllegalArgumentException JavaDoc
1101                    ("Indexed property names are not allowed");
1102        } else if (name.indexOf(PropertyUtils.MAPPED_DELIM) >= 0) {
1103            throw new IllegalArgumentException JavaDoc
1104                    ("Mapped property names are not allowed");
1105        }
1106
1107        // Handle DynaBean instances specially
1108
if (bean instanceof DynaBean) {
1109            DynaProperty descriptor =
1110                    ((DynaBean) bean).getDynaClass().getDynaProperty(name);
1111            if (descriptor == null) {
1112                throw new NoSuchMethodException JavaDoc("Unknown property '" +
1113                        name + "'");
1114            }
1115            return (((DynaBean) bean).get(name));
1116        }
1117
1118        // Retrieve the property getter method for the specified property
1119
PropertyDescriptor JavaDoc descriptor =
1120                getPropertyDescriptor(bean, name);
1121        if (descriptor == null) {
1122            throw new NoSuchMethodException JavaDoc("Unknown property '" +
1123                    name + "'");
1124        }
1125        Method JavaDoc readMethod = getReadMethod(descriptor);
1126        if (readMethod == null) {
1127            throw new NoSuchMethodException JavaDoc("Property '" + name +
1128                    "' has no getter method");
1129        }
1130
1131        // Call the property getter and return the value
1132
Object JavaDoc value = invokeMethod(readMethod, bean, new Object JavaDoc[0]);
1133        return (value);
1134
1135    }
1136
1137
1138    /**
1139     * <p>Return an accessible property setter method for this property,
1140     * if there is one; otherwise return <code>null</code>.</p>
1141     *
1142     * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
1143     *
1144     * @param descriptor Property descriptor to return a setter for
1145     */

1146    public Method JavaDoc getWriteMethod(PropertyDescriptor JavaDoc descriptor) {
1147
1148        return (MethodUtils.getAccessibleMethod(descriptor.getWriteMethod()));
1149
1150    }
1151
1152
1153    /**
1154     * <p>Return <code>true</code> if the specified property name identifies
1155     * a readable property on the specified bean; otherwise, return
1156     * <code>false</code>.
1157     *
1158     * @param bean Bean to be examined (may be a {@link DynaBean}
1159     * @param name Property name to be evaluated
1160     *
1161     * @exception IllegalArgumentException if <code>bean</code>
1162     * or <code>name</code> is <code>null</code>
1163     *
1164     * @since BeanUtils 1.6
1165     */

1166    public boolean isReadable(Object JavaDoc bean, String JavaDoc name) {
1167
1168        // Validate method parameters
1169
if (bean == null) {
1170            throw new IllegalArgumentException JavaDoc("No bean specified");
1171        }
1172        if (name == null) {
1173            throw new IllegalArgumentException JavaDoc("No name specified");
1174        }
1175
1176        // Return the requested result
1177
if (bean instanceof DynaBean) {
1178            // All DynaBean properties are readable
1179
return (((DynaBean) bean).getDynaClass().getDynaProperty(name) != null);
1180        } else {
1181            try {
1182                PropertyDescriptor JavaDoc desc =
1183                    getPropertyDescriptor(bean, name);
1184                if (desc != null) {
1185                    Method JavaDoc readMethod = desc.getReadMethod();
1186                    if ((readMethod == null) &&
1187                        (desc instanceof IndexedPropertyDescriptor JavaDoc)) {
1188                        readMethod = ((IndexedPropertyDescriptor JavaDoc) desc).getIndexedReadMethod();
1189                    }
1190                    return (readMethod != null);
1191                } else {
1192                    return (false);
1193                }
1194            } catch (IllegalAccessException JavaDoc e) {
1195                return (false);
1196            } catch (InvocationTargetException JavaDoc e) {
1197                return (false);
1198            } catch (NoSuchMethodException JavaDoc e) {
1199                return (false);
1200            }
1201        }
1202
1203    }
1204
1205
1206    /**
1207     * <p>Return <code>true</code> if the specified property name identifies
1208     * a writeable property on the specified bean; otherwise, return
1209     * <code>false</code>.
1210     *
1211     * @param bean Bean to be examined (may be a {@link DynaBean}
1212     * @param name Property name to be evaluated
1213     *
1214     * @exception IllegalPointerException if <code>bean</code>
1215     * or <code>name</code> is <code>null</code>
1216     *
1217     * @since BeanUtils 1.6
1218     */

1219    public boolean isWriteable(Object JavaDoc bean, String JavaDoc name) {
1220
1221        // Validate method parameters
1222
if (bean == null) {
1223            throw new IllegalArgumentException JavaDoc("No bean specified");
1224        }
1225        if (name == null) {
1226            throw new IllegalArgumentException JavaDoc("No name specified");
1227        }
1228
1229        // Return the requested result
1230
if (bean instanceof DynaBean) {
1231            // All DynaBean properties are writeable
1232
return (((DynaBean) bean).getDynaClass().getDynaProperty(name) != null);
1233        } else {
1234            try {
1235                PropertyDescriptor JavaDoc desc =
1236                    getPropertyDescriptor(bean, name);
1237                if (desc != null) {
1238                    Method JavaDoc writeMethod = desc.getWriteMethod();
1239                    if ((writeMethod == null) &&
1240                        (desc instanceof IndexedPropertyDescriptor JavaDoc)) {
1241                        writeMethod = ((IndexedPropertyDescriptor JavaDoc) desc).getIndexedWriteMethod();
1242                    }
1243                    return (writeMethod != null);
1244                } else {
1245                    return (false);
1246                }
1247            } catch (IllegalAccessException JavaDoc e) {
1248                return (false);
1249            } catch (InvocationTargetException JavaDoc e) {
1250                return (false);
1251            } catch (NoSuchMethodException JavaDoc e) {
1252                return (false);
1253            }
1254        }
1255
1256    }
1257
1258
1259    /**
1260     * Set the value of the specified indexed property of the specified
1261     * bean, with no type conversions. The zero-relative index of the
1262     * required value must be included (in square brackets) as a suffix to
1263     * the property name, or <code>IllegalArgumentException</code> will be
1264     * thrown. In addition to supporting the JavaBeans specification, this
1265     * method has been extended to support <code>List</code> objects as well.
1266     *
1267     * @param bean Bean whose property is to be modified
1268     * @param name <code>propertyname[index]</code> of the property value
1269     * to be modified
1270     * @param value Value to which the specified property element
1271     * should be set
1272     *
1273     * @exception ArrayIndexOutOfBoundsException if the specified index
1274     * is outside the valid range for the underlying array
1275     * @exception IllegalAccessException if the caller does not have
1276     * access to the property accessor method
1277     * @exception IllegalArgumentException if <code>bean</code> or
1278     * <code>name</code> is null
1279     * @exception InvocationTargetException if the property accessor method
1280     * throws an exception
1281     * @exception NoSuchMethodException if an accessor method for this
1282     * propety cannot be found
1283     */

1284    public void setIndexedProperty(Object JavaDoc bean, String JavaDoc name,
1285                                          Object JavaDoc value)
1286            throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc,
1287            NoSuchMethodException JavaDoc {
1288
1289        if (bean == null) {
1290            throw new IllegalArgumentException JavaDoc("No bean specified");
1291        }
1292        if (name == null) {
1293            throw new IllegalArgumentException JavaDoc("No name specified");
1294        }
1295
1296        // Identify the index of the requested individual property
1297
int delim = name.indexOf(PropertyUtils.INDEXED_DELIM);
1298        int delim2 = name.indexOf(PropertyUtils.INDEXED_DELIM2);
1299        if ((delim < 0) || (delim2 <= delim)) {
1300            throw new IllegalArgumentException JavaDoc("Invalid indexed property '" +
1301                    name + "'");
1302        }
1303        int index = -1;
1304        try {
1305            String JavaDoc subscript = name.substring(delim + 1, delim2);
1306            index = Integer.parseInt(subscript);
1307        } catch (NumberFormatException JavaDoc e) {
1308            throw new IllegalArgumentException JavaDoc("Invalid indexed property '" +
1309                    name + "'");
1310        }
1311        name = name.substring(0, delim);
1312
1313        // Set the specified indexed property value
1314
setIndexedProperty(bean, name, index, value);
1315
1316    }
1317
1318
1319    /**
1320     * Set the value of the specified indexed property of the specified
1321     * bean, with no type conversions. In addition to supporting the JavaBeans
1322     * specification, this method has been extended to support
1323     * <code>List</code> objects as well.
1324     *
1325     * @param bean Bean whose property is to be set
1326     * @param name Simple property name of the property value to be set
1327     * @param index Index of the property value to be set
1328     * @param value Value to which the indexed property element is to be set
1329     *
1330     * @exception ArrayIndexOutOfBoundsException if the specified index
1331     * is outside the valid range for the underlying array
1332     * @exception IllegalAccessException if the caller does not have
1333     * access to the property accessor method
1334     * @exception IllegalArgumentException if <code>bean</code> or
1335     * <code>name</code> is null
1336     * @exception InvocationTargetException if the property accessor method
1337     * throws an exception
1338     * @exception NoSuchMethodException if an accessor method for this
1339     * propety cannot be found
1340     */

1341    public void setIndexedProperty(Object JavaDoc bean, String JavaDoc name,
1342                                          int index, Object JavaDoc value)
1343            throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc,
1344            NoSuchMethodException JavaDoc {
1345
1346        if (bean == null) {
1347            throw new IllegalArgumentException JavaDoc("No bean specified");
1348        }
1349        if (name == null) {
1350            throw new IllegalArgumentException JavaDoc("No name specified");
1351        }
1352
1353        // Handle DynaBean instances specially
1354
if (bean instanceof DynaBean) {
1355            DynaProperty descriptor =
1356                    ((DynaBean) bean).getDynaClass().getDynaProperty(name);
1357            if (descriptor == null) {
1358                throw new NoSuchMethodException JavaDoc("Unknown property '" +
1359                        name + "'");
1360            }
1361            ((DynaBean) bean).set(name, index, value);
1362            return;
1363        }
1364
1365        // Retrieve the property descriptor for the specified property
1366
PropertyDescriptor JavaDoc descriptor =
1367                getPropertyDescriptor(bean, name);
1368        if (descriptor == null) {
1369            throw new NoSuchMethodException JavaDoc("Unknown property '" +
1370                    name + "'");
1371        }
1372
1373        // Call the indexed setter method if there is one
1374
if (descriptor instanceof IndexedPropertyDescriptor JavaDoc) {
1375            Method JavaDoc writeMethod = ((IndexedPropertyDescriptor JavaDoc) descriptor).
1376                    getIndexedWriteMethod();
1377            if (writeMethod != null) {
1378                Object JavaDoc subscript[] = new Object JavaDoc[2];
1379                subscript[0] = new Integer JavaDoc(index);
1380                subscript[1] = value;
1381                try {
1382                    if (log.isTraceEnabled()) {
1383                        String JavaDoc valueClassName =
1384                            value == null ? "<null>"
1385                                          : value.getClass().getName();
1386                        log.trace("setSimpleProperty: Invoking method "
1387                                  + writeMethod +" with index=" + index
1388                                  + ", value=" + value
1389                                  + " (class " + valueClassName+ ")");
1390                    }
1391                    invokeMethod(writeMethod, bean, subscript);
1392                } catch (InvocationTargetException JavaDoc e) {
1393                    if (e.getTargetException() instanceof
1394                            ArrayIndexOutOfBoundsException JavaDoc) {
1395                        throw (ArrayIndexOutOfBoundsException JavaDoc)
1396                                e.getTargetException();
1397                    } else {
1398                        throw e;
1399                    }
1400                }
1401                return;
1402            }
1403        }
1404
1405        // Otherwise, the underlying property must be an array or a list
1406
Method JavaDoc readMethod = descriptor.getReadMethod();
1407        if (readMethod == null) {
1408            throw new NoSuchMethodException JavaDoc("Property '" + name +
1409                    "' has no getter method");
1410        }
1411
1412        // Call the property getter to get the array or list
1413
Object JavaDoc array = invokeMethod(readMethod, bean, new Object JavaDoc[0]);
1414        if (!array.getClass().isArray()) {
1415            if (array instanceof List JavaDoc) {
1416                // Modify the specified value in the List
1417
((List JavaDoc) array).set(index, value);
1418            } else {
1419                throw new IllegalArgumentException JavaDoc("Property '" + name +
1420                        "' is not indexed");
1421            }
1422        } else {
1423            // Modify the specified value in the array
1424
Array.set(array, index, value);
1425        }
1426
1427    }
1428
1429
1430    /**
1431     * Set the value of the specified mapped property of the
1432     * specified bean, with no type conversions. The key of the
1433     * value to set must be included (in brackets) as a suffix to
1434     * the property name, or <code>IllegalArgumentException</code> will be
1435     * thrown.
1436     *
1437     * @param bean Bean whose property is to be set
1438     * @param name <code>propertyname(key)</code> of the property value
1439     * to be set
1440     * @param value The property value to be set
1441     *
1442     * @exception IllegalAccessException if the caller does not have
1443     * access to the property accessor method
1444     * @exception InvocationTargetException if the property accessor method
1445     * throws an exception
1446     * @exception NoSuchMethodException if an accessor method for this
1447     * propety cannot be found
1448     */

1449    public void setMappedProperty(Object JavaDoc bean, String JavaDoc name,
1450                                         Object JavaDoc value)
1451            throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc,
1452            NoSuchMethodException JavaDoc {
1453
1454        if (bean == null) {
1455            throw new IllegalArgumentException JavaDoc("No bean specified");
1456        }
1457        if (name == null) {
1458            throw new IllegalArgumentException JavaDoc("No name specified");
1459        }
1460
1461        // Identify the index of the requested individual property
1462
int delim = name.indexOf(PropertyUtils.MAPPED_DELIM);
1463        int delim2 = name.indexOf(PropertyUtils.MAPPED_DELIM2);
1464        if ((delim < 0) || (delim2 <= delim)) {
1465            throw new IllegalArgumentException JavaDoc
1466                    ("Invalid mapped property '" + name + "'");
1467        }
1468
1469        // Isolate the name and the key
1470
String JavaDoc key = name.substring(delim + 1, delim2);
1471        name = name.substring(0, delim);
1472
1473        // Request the specified indexed property value
1474
setMappedProperty(bean, name, key, value);
1475
1476    }
1477
1478
1479    /**
1480     * Set the value of the specified mapped property of the specified
1481     * bean, with no type conversions.
1482     *
1483     * @param bean Bean whose property is to be set
1484     * @param name Mapped property name of the property value to be set
1485     * @param key Key of the property value to be set
1486     * @param value The property value to be set
1487     *
1488     * @exception IllegalAccessException if the caller does not have
1489     * access to the property accessor method
1490     * @exception InvocationTargetException if the property accessor method
1491     * throws an exception
1492     * @exception NoSuchMethodException if an accessor method for this
1493     * propety cannot be found
1494     */

1495    public void setMappedProperty(Object JavaDoc bean, String JavaDoc name,
1496                                         String JavaDoc key, Object JavaDoc value)
1497            throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc,
1498            NoSuchMethodException JavaDoc {
1499
1500        if (bean == null) {
1501            throw new IllegalArgumentException JavaDoc("No bean specified");
1502        }
1503        if (name == null) {
1504            throw new IllegalArgumentException JavaDoc("No name specified");
1505        }
1506        if (key == null) {
1507            throw new IllegalArgumentException JavaDoc("No key specified");
1508        }
1509
1510        // Handle DynaBean instances specially
1511
if (bean instanceof DynaBean) {
1512            DynaProperty descriptor =
1513                    ((DynaBean) bean).getDynaClass().getDynaProperty(name);
1514            if (descriptor == null) {
1515                throw new NoSuchMethodException JavaDoc("Unknown property '" +
1516                        name + "'");
1517            }
1518            ((DynaBean) bean).set(name, key, value);
1519            return;
1520        }
1521
1522        // Retrieve the property descriptor for the specified property
1523
PropertyDescriptor JavaDoc descriptor =
1524                getPropertyDescriptor(bean, name);
1525        if (descriptor == null) {
1526            throw new NoSuchMethodException JavaDoc("Unknown property '" +
1527                    name + "'");
1528        }
1529
1530        if (descriptor instanceof MappedPropertyDescriptor) {
1531            // Call the keyed setter method if there is one
1532
Method JavaDoc mappedWriteMethod =
1533                    ((MappedPropertyDescriptor) descriptor).
1534                    getMappedWriteMethod();
1535            if (mappedWriteMethod != null) {
1536                Object JavaDoc params[] = new Object JavaDoc[2];
1537                params[0] = key;
1538                params[1] = value;
1539                if (log.isTraceEnabled()) {
1540                    String JavaDoc valueClassName =
1541                        value == null ? "<null>" : value.getClass().getName();
1542                    log.trace("setSimpleProperty: Invoking method "
1543                              + mappedWriteMethod + " with key=" + key
1544                              + ", value=" + value
1545                              + " (class " + valueClassName +")");
1546                }
1547                invokeMethod(mappedWriteMethod, bean, params);
1548            } else {
1549                throw new NoSuchMethodException JavaDoc
1550                        ("Property '" + name +
1551                        "' has no mapped setter method");
1552            }
1553        } else {
1554          /* means that the result has to be retrieved from a map */
1555          Method JavaDoc readMethod = descriptor.getReadMethod();
1556          if (readMethod != null) {
1557            Object JavaDoc invokeResult = invokeMethod(readMethod, bean, new Object JavaDoc[0]);
1558            /* test and fetch from the map */
1559            if (invokeResult instanceof java.util.Map JavaDoc) {
1560              ((java.util.Map JavaDoc)invokeResult).put(key, value);
1561            }
1562          } else {
1563            throw new NoSuchMethodException JavaDoc("Property '" + name +
1564                    "' has no mapped getter method");
1565          }
1566        }
1567
1568    }
1569
1570
1571    /**
1572     * Set the value of the (possibly nested) property of the specified
1573     * name, for the specified bean, with no type conversions.
1574     *
1575     * @param bean Bean whose property is to be modified
1576     * @param name Possibly nested name of the property to be modified
1577     * @param value Value to which the property is to be set
1578     *
1579     * @exception IllegalAccessException if the caller does not have
1580     * access to the property accessor method
1581     * @exception IllegalArgumentException if <code>bean</code> or
1582     * <code>name</code> is null
1583     * @exception IllegalArgumentException if a nested reference to a
1584     * property returns null
1585     * @exception InvocationTargetException if the property accessor method
1586     * throws an exception
1587     * @exception NoSuchMethodException if an accessor method for this
1588     * propety cannot be found
1589     */

1590    public void setNestedProperty(Object JavaDoc bean,
1591                                         String JavaDoc name, Object JavaDoc value)
1592            throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc,
1593            NoSuchMethodException JavaDoc {
1594
1595        if (bean == null) {
1596            throw new IllegalArgumentException JavaDoc("No bean specified");
1597        }
1598        if (name == null) {
1599            throw new IllegalArgumentException JavaDoc("No name specified");
1600        }
1601
1602        int indexOfINDEXED_DELIM = -1;
1603        int indexOfMAPPED_DELIM = -1;
1604        while (true) {
1605            int delim = name.indexOf(PropertyUtils.NESTED_DELIM);
1606            if (delim < 0) {
1607                break;
1608            }
1609            String JavaDoc next = name.substring(0, delim);
1610            indexOfINDEXED_DELIM = next.indexOf(PropertyUtils.INDEXED_DELIM);
1611            indexOfMAPPED_DELIM = next.indexOf(PropertyUtils.MAPPED_DELIM);
1612            if (bean instanceof Map JavaDoc) {
1613                bean = ((Map JavaDoc) bean).get(next);
1614            } else if (indexOfMAPPED_DELIM >= 0) {
1615                bean = getMappedProperty(bean, next);
1616            } else if (indexOfINDEXED_DELIM >= 0) {
1617                bean = getIndexedProperty(bean, next);
1618            } else {
1619                bean = getSimpleProperty(bean, next);
1620            }
1621            if (bean == null) {
1622                throw new IllegalArgumentException JavaDoc
1623                        ("Null property value for '" +
1624                        name.substring(0, delim) + "'");
1625            }
1626            name = name.substring(delim + 1);
1627        }
1628
1629        indexOfINDEXED_DELIM = name.indexOf(PropertyUtils.INDEXED_DELIM);
1630        indexOfMAPPED_DELIM = name.indexOf(PropertyUtils.MAPPED_DELIM);
1631
1632        if (bean instanceof Map JavaDoc) {
1633            // check to see if the class has a standard property
1634
PropertyDescriptor JavaDoc descriptor =
1635                getPropertyDescriptor(bean, name);
1636            if (descriptor == null) {
1637                // no - then put the value into the map
1638
((Map JavaDoc) bean).put(name, value);
1639            } else {
1640                // yes - use that instead
1641
setSimpleProperty(bean, name, value);
1642            }
1643        } else if (indexOfMAPPED_DELIM >= 0) {
1644            setMappedProperty(bean, name, value);
1645        } else if (indexOfINDEXED_DELIM >= 0) {
1646            setIndexedProperty(bean, name, value);
1647        } else {
1648            setSimpleProperty(bean, name, value);
1649        }
1650
1651    }
1652
1653
1654    /**
1655     * Set the value of the specified property of the specified bean,
1656     * no matter which property reference format is used, with no
1657     * type conversions.
1658     *
1659     * @param bean Bean whose property is to be modified
1660     * @param name Possibly indexed and/or nested name of the property
1661     * to be modified
1662     * @param value Value to which this property is to be set
1663     *
1664     * @exception IllegalAccessException if the caller does not have
1665     * access to the property accessor method
1666     * @exception IllegalArgumentException if <code>bean</code> or
1667     * <code>name</code> is null
1668     * @exception InvocationTargetException if the property accessor method
1669     * throws an exception
1670     * @exception NoSuchMethodException if an accessor method for this
1671     * propety cannot be found
1672     */

1673    public void setProperty(Object JavaDoc bean, String JavaDoc name, Object JavaDoc value)
1674            throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc,
1675            NoSuchMethodException JavaDoc {
1676
1677        setNestedProperty(bean, name, value);
1678
1679    }
1680
1681
1682    /**
1683     * Set the value of the specified simple property of the specified bean,
1684     * with no type conversions.
1685     *
1686     * @param bean Bean whose property is to be modified
1687     * @param name Name of the property to be modified
1688     * @param value Value to which the property should be set
1689     *
1690     * @exception IllegalAccessException if the caller does not have
1691     * access to the property accessor method
1692     * @exception IllegalArgumentException if <code>bean</code> or
1693     * <code>name</code> is null
1694     * @exception IllegalArgumentException if the property name is
1695     * nested or indexed
1696     * @exception InvocationTargetException if the property accessor method
1697     * throws an exception
1698     * @exception NoSuchMethodException if an accessor method for this
1699     * propety cannot be found
1700     */

1701    public void setSimpleProperty(Object JavaDoc bean,
1702                                         String JavaDoc name, Object JavaDoc value)
1703            throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc,
1704            NoSuchMethodException JavaDoc {
1705
1706        if (bean == null) {
1707            throw new IllegalArgumentException JavaDoc("No bean specified");
1708        }
1709        if (name == null) {
1710            throw new IllegalArgumentException JavaDoc("No name specified");
1711        }
1712
1713        // Validate the syntax of the property name
1714
if (name.indexOf(PropertyUtils.NESTED_DELIM) >= 0) {
1715            throw new IllegalArgumentException JavaDoc
1716                    ("Nested property names are not allowed");
1717        } else if (name.indexOf(PropertyUtils.INDEXED_DELIM) >= 0) {
1718            throw new IllegalArgumentException JavaDoc
1719                    ("Indexed property names are not allowed");
1720        } else if (name.indexOf(PropertyUtils.MAPPED_DELIM) >= 0) {
1721            throw new IllegalArgumentException JavaDoc
1722                    ("Mapped property names are not allowed");
1723        }
1724
1725        // Handle DynaBean instances specially
1726
if (bean instanceof DynaBean) {
1727            DynaProperty descriptor =
1728                    ((DynaBean) bean).getDynaClass().getDynaProperty(name);
1729            if (descriptor == null) {
1730                throw new NoSuchMethodException JavaDoc("Unknown property '" +
1731                        name + "'");
1732            }
1733            ((DynaBean) bean).set(name, value);
1734            return;
1735        }
1736
1737        // Retrieve the property setter method for the specified property
1738
PropertyDescriptor JavaDoc descriptor =
1739                getPropertyDescriptor(bean, name);
1740        if (descriptor == null) {
1741            throw new NoSuchMethodException JavaDoc("Unknown property '" +
1742                    name + "'");
1743        }
1744        Method JavaDoc writeMethod = getWriteMethod(descriptor);
1745        if (writeMethod == null) {
1746            throw new NoSuchMethodException JavaDoc("Property '" + name +
1747                    "' has no setter method");
1748        }
1749
1750        // Call the property setter method
1751
Object JavaDoc values[] = new Object JavaDoc[1];
1752        values[0] = value;
1753        if (log.isTraceEnabled()) {
1754            String JavaDoc valueClassName =
1755                value == null ? "<null>" : value.getClass().getName();
1756            log.trace("setSimpleProperty: Invoking method " + writeMethod
1757                      + " with value " + value + " (class " + valueClassName + ")");
1758        }
1759        invokeMethod(writeMethod, bean, values);
1760
1761    }
1762    
1763    /** This just catches and wraps IllegalArgumentException. */
1764    private Object JavaDoc invokeMethod(
1765                        Method JavaDoc method,
1766                        Object JavaDoc bean,
1767                        Object JavaDoc[] values)
1768                            throws
1769                                IllegalAccessException JavaDoc,
1770                                InvocationTargetException JavaDoc {
1771        try {
1772            
1773            return method.invoke(bean, values);
1774        
1775        } catch (IllegalArgumentException JavaDoc e) {
1776            
1777            log.error("Method invocation failed.", e);
1778            throw new IllegalArgumentException JavaDoc(
1779                "Cannot invoke " + method.getDeclaringClass().getName() + "."
1780                + method.getName() + " - " + e.getMessage());
1781            
1782        }
1783    }
1784}
1785
Popular Tags