KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > opensymphony > workflow > designer > beanutils > PropertyUtilsBean


1 /*
2  * $Header: /cvs/osworkflow/src/designer/com/opensymphony/workflow/designer/beanutils/PropertyUtilsBean.java,v 1.3 2004/04/16 10:36:29 hani Exp $
3  * $Revision: 1.3 $
4  * $Date: 2004/04/16 10:36:29 $
5  *
6  * ====================================================================
7  *
8  * The Apache Software License, Version 1.1
9  *
10  * Copyright (c) 2001-2003 The Apache Software Foundation. All rights
11  * reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  *
17  * 1. Redistributions of source code must retain the above copyright
18  * notice, this list of conditions and the following disclaimer.
19  *
20  * 2. Redistributions in binary form must reproduce the above copyright
21  * notice, this list of conditions and the following disclaimer in
22  * the documentation and/or other materials provided with the
23  * distribution.
24  *
25  * 3. The end-user documentation included with the redistribution,
26  * if any, must include the following acknowledgement:
27  * "This product includes software developed by the
28  * Apache Software Foundation (http://www.apache.org/)."
29  * Alternately, this acknowledgement may appear in the software itself,
30  * if and wherever such third-party acknowledgements normally appear.
31  *
32  * 4. The names "Apache", "The Jakarta Project", "Commons", and "Apache Software
33  * Foundation" must not be used to endorse or promote products derived
34  * from this software without prior written permission. For written
35  * permission, please contact apache@apache.org.
36  *
37  * 5. Products derived from this software may not be called "Apache",
38  * "Apache" nor may "Apache" appear in their names without prior
39  * written permission of the Apache Software Foundation.
40  *
41  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
42  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
43  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
45  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
46  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
47  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
48  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
49  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
50  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
51  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52  * SUCH DAMAGE.
53  * ====================================================================
54  *
55  * This software consists of voluntary contributions made by many
56  * individuals on behalf of the Apache Software Foundation. For more
57  * information on the Apache Software Foundation, please see
58  * <http://www.apache.org/>.
59  *
60  */

61
62 package com.opensymphony.workflow.designer.beanutils;
63
64 import java.beans.*;
65 import java.lang.reflect.Array JavaDoc;
66 import java.lang.reflect.InvocationTargetException JavaDoc;
67 import java.lang.reflect.Method JavaDoc;
68 import java.util.*;
69
70 /**
71  * Utility methods for using Java Reflection APIs to facilitate generic
72  * property getter and setter operations on Java objects. Much of this
73  * code was originally included in <code>BeanUtils</code>, but has been
74  * separated because of the volume of code involved.
75  * <p/>
76  * In general, the objects that are examined and modified using these
77  * methods are expected to conform to the property getter and setter method
78  * naming conventions described in the JavaBeans Specification (Version 1.0.1).
79  * No data type conversions are performed, and there are no usage of any
80  * <code>PropertyEditor</code> classes that have been registered, although
81  * a convenient way to access the registered classes themselves is included.
82  * <p/>
83  * For the purposes of this class, five formats for referencing a particular
84  * property value of a bean are defined, with the layout of an identifying
85  * String in parentheses:
86  * <ul>
87  * <li><strong>Simple (<code>name</code>)</strong> - The specified
88  * <code>name</code> identifies an individual property of a particular
89  * JavaBean. The name of the actual getter or setter method to be used
90  * is determined using standard JavaBeans instrospection, so that (unless
91  * overridden by a <code>BeanInfo</code> class, a property named "xyz"
92  * will have a getter method named <code>getXyz()</code> or (for boolean
93  * properties only) <code>isXyz()</code>, and a setter method named
94  * <code>setXyz()</code>.</li>
95  * <li><strong>Nested (<code>name1.name2.name3</code>)</strong> The first
96  * name element is used to select a property getter, as for simple
97  * references above. The object returned for this property is then
98  * consulted, using the same approach, for a property getter for a
99  * property named <code>name2</code>, and so on. The property value that
100  * is ultimately retrieved or modified is the one identified by the
101  * last name element.</li>
102  * <li><strong>Indexed (<code>name[index]</code>)</strong> - The underlying
103  * property value is assumed to be an array, or this JavaBean is assumed
104  * to have indexed property getter and setter methods. The appropriate
105  * (zero-relative) entry in the array is selected. <code>List</code>
106  * objects are now also supported for read/write. You simply need to define
107  * a getter that returns the <code>List</code></li>
108  * <li><strong>Mapped (<code>name(key)</code>)</strong> - The JavaBean
109  * is assumed to have an property getter and setter methods with an
110  * additional attribute of type <code>java.lang.String</code>.</li>
111  * <li><strong>Combined (<code>name1.name2[index].name3(key)</code>)</strong> -
112  * Combining mapped, nested, and indexed references is also
113  * supported.</li>
114  * </ul>
115  *
116  * @author Craig R. McClanahan
117  * @author Ralph Schaer
118  * @author Chris Audley
119  * @author Rey François
120  * @author Gregor Raıman
121  * @author Jan Sorensen
122  * @author Scott Sanders
123  * @version $Revision: 1.3 $ $Date: 2004/04/16 10:36:29 $
124  * @see PropertyUtils
125  * @since 1.7
126  */

127
128 public class PropertyUtilsBean
129 {
130
131   private static final PropertyUtilsBean instance = new PropertyUtilsBean();
132
133   // --------------------------------------------------------- Class Methods
134

135   protected static PropertyUtilsBean getInstance()
136   {
137     return instance;
138   }
139
140   // --------------------------------------------------------- Variables
141

142   /**
143    * The cache of PropertyDescriptor arrays for beans we have already
144    * introspected, keyed by the java.lang.Class of this object.
145    */

146   private Map descriptorsCache = null;
147   private Map mappedDescriptorsCache = null;
148
149   // ---------------------------------------------------------- Constructors
150

151   /**
152    * Base constructor
153    */

154   public PropertyUtilsBean()
155   {
156     descriptorsCache = new HashMap();
157     mappedDescriptorsCache = new HashMap();
158   }
159
160
161   // --------------------------------------------------------- Public Methods
162

163
164   /**
165    * Clear any cached property descriptors information for all classes
166    * loaded by any class loaders. This is useful in cases where class
167    * loaders are thrown away to implement class reloading.
168    */

169   public void clearDescriptors()
170   {
171
172     descriptorsCache.clear();
173     mappedDescriptorsCache.clear();
174     Introspector.flushCaches();
175
176   }
177
178   /**
179    * <p>Copy property values from the "origin" bean to the "destination" bean
180    * for all cases where the property names are the same (even though the
181    * actual getter and setter methods might have been customized via
182    * <code>BeanInfo</code> classes). No conversions are performed on the
183    * actual property values -- it is assumed that the values retrieved from
184    * the origin bean are assignment-compatible with the types expected by
185    * the destination bean.</p>
186    * <p/>
187    * <p>If the origin "bean" is actually a <code>Map</code>, it is assumed
188    * to contain String-valued <strong>simple</strong> property names as the keys, pointing
189    * at the corresponding property values that will be set in the destination
190    * bean.<strong>Note</strong> that this method is intended to perform
191    * a "shallow copy" of the properties and so complex properties
192    * (for example, nested ones) will not be copied.</p>
193    *
194    * @param dest Destination bean whose properties are modified
195    * @param orig Origin bean whose properties are retrieved
196    * @throws IllegalAccessException if the caller does not have
197    * access to the property accessor method
198    * @throws IllegalArgumentException if the <code>dest</code> or
199    * <code>orig</code> argument is null
200    * @throws InvocationTargetException if the property accessor method
201    * throws an exception
202    * @throws NoSuchMethodException if an accessor method for this
203    * propety cannot be found
204    */

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

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

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

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

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

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

566   public Map getMappedPropertyDescriptors(Class JavaDoc beanClass)
567   {
568
569     if(beanClass == null)
570     {
571       return null;
572     }
573
574     // Look up any cached descriptors for this bean class
575
return (Map)mappedDescriptorsCache.get(beanClass);
576
577   }
578
579   /**
580    * <p>Return the mapped property descriptors for this bean.</p>
581    * <p/>
582    * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
583    *
584    * @param bean Bean to be introspected
585    */

586   public Map getMappedPropertyDescriptors(Object JavaDoc bean)
587   {
588
589     if(bean == null)
590     {
591       return null;
592     }
593     return (getMappedPropertyDescriptors(bean.getClass()));
594
595   }
596
597   /**
598    * Return the value of the (possibly nested) property of the specified
599    * name, for the specified bean, with no type conversions.
600    *
601    * @param bean Bean whose property is to be extracted
602    * @param name Possibly nested name of the property to be extracted
603    * @throws IllegalAccessException if the caller does not have
604    * access to the property accessor method
605    * @throws IllegalArgumentException if <code>bean</code> or
606    * <code>name</code> is null
607    * @throws IllegalArgumentException if a nested reference to a
608    * property returns null
609    * @throws InvocationTargetException if the property accessor method throws an exception
610    * @throws NoSuchMethodException if an accessor method for this
611    * propety cannot be found
612    */

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

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

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

890   public PropertyDescriptor[] getPropertyDescriptors(Class JavaDoc beanClass)
891   {
892
893     if(beanClass == null)
894     {
895       throw new IllegalArgumentException JavaDoc("No bean class specified");
896     }
897
898     // Look up any cached descriptors for this bean class
899
PropertyDescriptor descriptors[] = null;
900     descriptors = (PropertyDescriptor[])descriptorsCache.get(beanClass);
901     if(descriptors != null)
902     {
903       return (descriptors);
904     }
905
906     // Introspect the bean and cache the generated descriptors
907
BeanInfo beanInfo = null;
908     try
909     {
910       beanInfo = Introspector.getBeanInfo(beanClass);
911     }
912     catch(IntrospectionException e)
913     {
914       return (new PropertyDescriptor[0]);
915     }
916     descriptors = beanInfo.getPropertyDescriptors();
917     if(descriptors == null)
918     {
919       descriptors = new PropertyDescriptor[0];
920     }
921     descriptorsCache.put(beanClass, descriptors);
922     return (descriptors);
923
924   }
925
926   /**
927    * <p>Retrieve the property descriptors for the specified bean,
928    * introspecting and caching them the first time a particular bean class
929    * is encountered.</p>
930    * <p/>
931    * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
932    *
933    * @param bean Bean for which property descriptors are requested
934    * @throws IllegalArgumentException if <code>bean</code> is null
935    */

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

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

1022  public Class JavaDoc getPropertyType(Object JavaDoc bean, String JavaDoc name) throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc, NoSuchMethodException JavaDoc
1023  {
1024
1025    if(bean == null)
1026    {
1027      throw new IllegalArgumentException JavaDoc("No bean specified");
1028    }
1029    if(name == null)
1030    {
1031      throw new IllegalArgumentException JavaDoc("No name specified");
1032    }
1033
1034    PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
1035    if(descriptor == null)
1036    {
1037      return (null);
1038    }
1039    else if(descriptor instanceof IndexedPropertyDescriptor)
1040    {
1041      return (((IndexedPropertyDescriptor)descriptor).getIndexedPropertyType());
1042    }
1043    else if(descriptor instanceof MappedPropertyDescriptor)
1044    {
1045      return (((MappedPropertyDescriptor)descriptor).getMappedPropertyType());
1046    }
1047    else
1048    {
1049      return (descriptor.getPropertyType());
1050    }
1051
1052  }
1053
1054  /**
1055   * <p>Return an accessible property getter method for this property,
1056   * if there is one; otherwise return <code>null</code>.</p>
1057   * <p/>
1058   * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
1059   *
1060   * @param descriptor Property descriptor to return a getter for
1061   */

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

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

1138  public Method JavaDoc getWriteMethod(PropertyDescriptor descriptor)
1139  {
1140
1141    return (MethodUtils.getAccessibleMethod(descriptor.getWriteMethod()));
1142
1143  }
1144
1145  /**
1146   * <p>Return <code>true</code> if the specified property name identifies
1147   * a readable property on the specified bean; otherwise, return
1148   * <code>false</code>.
1149   *
1150   * @param bean Bean to be examined
1151   * @param name Property name to be evaluated
1152   * @throws IllegalArgumentException if <code>bean</code>
1153   * or <code>name</code> is <code>null</code>
1154   * @since BeanUtils 1.6
1155   */

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

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

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

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

1433  public void setMappedProperty(Object JavaDoc bean, String JavaDoc name, Object JavaDoc value) throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc, NoSuchMethodException JavaDoc
1434  {
1435
1436    if(bean == null)
1437    {
1438      throw new IllegalArgumentException JavaDoc("No bean specified");
1439    }
1440    if(name == null)
1441    {
1442      throw new IllegalArgumentException JavaDoc("No name specified");
1443    }
1444
1445    // Identify the index of the requested individual property
1446
int delim = name.indexOf(PropertyUtils.MAPPED_DELIM);
1447    int delim2 = name.indexOf(PropertyUtils.MAPPED_DELIM2);
1448    if((delim < 0) || (delim2 <= delim))
1449    {
1450      throw new IllegalArgumentException JavaDoc("Invalid mapped property '" + name + "'");
1451    }
1452
1453    // Isolate the name and the key
1454
String JavaDoc key = name.substring(delim + 1, delim2);
1455    name = name.substring(0, delim);
1456
1457    // Request the specified indexed property value
1458
setMappedProperty(bean, name, key, value);
1459
1460  }
1461
1462  /**
1463   * Set the value of the specified mapped property of the specified
1464   * bean, with no type conversions.
1465   *
1466   * @param bean Bean whose property is to be set
1467   * @param name Mapped property name of the property value to be set
1468   * @param key Key of the property value to be set
1469   * @param value The property value to be set
1470   * @throws IllegalAccessException if the caller does not have
1471   * access to the property accessor method
1472   * @throws InvocationTargetException if the property accessor method
1473   * throws an exception
1474   * @throws NoSuchMethodException if an accessor method for this
1475   * propety cannot be found
1476   */

1477  public void setMappedProperty(Object JavaDoc bean, String JavaDoc name, String JavaDoc key, Object JavaDoc value) throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc, NoSuchMethodException JavaDoc
1478  {
1479
1480    if(bean == null)
1481    {
1482      throw new IllegalArgumentException JavaDoc("No bean specified");
1483    }
1484    if(name == null)
1485    {
1486      throw new IllegalArgumentException JavaDoc("No name specified");
1487    }
1488    if(key == null)
1489    {
1490      throw new IllegalArgumentException JavaDoc("No key specified");
1491    }
1492
1493    // Retrieve the property descriptor for the specified property
1494
PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
1495    if(descriptor == null)
1496    {
1497      throw new NoSuchMethodException JavaDoc("Unknown property '" + name + "'");
1498    }
1499
1500    if(descriptor instanceof MappedPropertyDescriptor)
1501    {
1502      // Call the keyed setter method if there is one
1503
Method JavaDoc mappedWriteMethod = ((MappedPropertyDescriptor)descriptor).getMappedWriteMethod();
1504      if(mappedWriteMethod != null)
1505      {
1506        Object JavaDoc params[] = new Object JavaDoc[2];
1507        params[0] = key;
1508        params[1] = value;
1509        invokeMethod(mappedWriteMethod, bean, params);
1510      }
1511      else
1512      {
1513        throw new NoSuchMethodException JavaDoc("Property '" + name + "' has no mapped setter method");
1514      }
1515    }
1516    else
1517    {
1518      /* means that the result has to be retrieved from a map */
1519      Method JavaDoc readMethod = descriptor.getReadMethod();
1520      if(readMethod != null)
1521      {
1522        Object JavaDoc invokeResult = invokeMethod(readMethod, bean, new Object JavaDoc[0]);
1523        /* test and fetch from the map */
1524        if(invokeResult instanceof java.util.Map JavaDoc)
1525        {
1526          ((java.util.Map JavaDoc)invokeResult).put(key, value);
1527        }
1528      }
1529      else
1530      {
1531        throw new NoSuchMethodException JavaDoc("Property '" + name + "' has no mapped getter method");
1532      }
1533    }
1534
1535  }
1536
1537  /**
1538   * Set the value of the (possibly nested) property of the specified
1539   * name, for the specified bean, with no type conversions.
1540   *
1541   * @param bean Bean whose property is to be modified
1542   * @param name Possibly nested name of the property to be modified
1543   * @param value Value to which the property is to be set
1544   * @throws IllegalAccessException if the caller does not have
1545   * access to the property accessor method
1546   * @throws IllegalArgumentException if <code>bean</code> or
1547   * <code>name</code> is null
1548   * @throws IllegalArgumentException if a nested reference to a
1549   * property returns null
1550   * @throws InvocationTargetException if the property accessor method
1551   * throws an exception
1552   * @throws NoSuchMethodException if an accessor method for this
1553   * propety cannot be found
1554   */

1555  public void setNestedProperty(Object JavaDoc bean, String JavaDoc name, Object JavaDoc value) throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc, NoSuchMethodException JavaDoc
1556  {
1557
1558    if(bean == null)
1559    {
1560      throw new IllegalArgumentException JavaDoc("No bean specified");
1561    }
1562    if(name == null)
1563    {
1564      throw new IllegalArgumentException JavaDoc("No name specified");
1565    }
1566
1567    int indexOfINDEXED_DELIM = -1;
1568    int indexOfMAPPED_DELIM = -1;
1569    while(true)
1570    {
1571      int delim = name.indexOf(PropertyUtils.NESTED_DELIM);
1572      if(delim < 0)
1573      {
1574        break;
1575      }
1576      String JavaDoc next = name.substring(0, delim);
1577      indexOfINDEXED_DELIM = next.indexOf(PropertyUtils.INDEXED_DELIM);
1578      indexOfMAPPED_DELIM = next.indexOf(PropertyUtils.MAPPED_DELIM);
1579      if(bean instanceof Map)
1580      {
1581        bean = ((Map)bean).get(next);
1582      }
1583      else if(indexOfMAPPED_DELIM >= 0)
1584      {
1585        bean = getMappedProperty(bean, next);
1586      }
1587      else if(indexOfINDEXED_DELIM >= 0)
1588      {
1589        bean = getIndexedProperty(bean, next);
1590      }
1591      else
1592      {
1593        bean = getSimpleProperty(bean, next);
1594      }
1595      if(bean == null)
1596      {
1597        throw new IllegalArgumentException JavaDoc("Null property value for '" + name.substring(0, delim) + "'");
1598      }
1599      name = name.substring(delim + 1);
1600    }
1601
1602    indexOfINDEXED_DELIM = name.indexOf(PropertyUtils.INDEXED_DELIM);
1603    indexOfMAPPED_DELIM = name.indexOf(PropertyUtils.MAPPED_DELIM);
1604
1605    if(bean instanceof Map)
1606    {
1607      // check to see if the class has a standard property
1608
PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
1609      if(descriptor == null)
1610      {
1611        // no - then put the value into the map
1612
((Map)bean).put(name, value);
1613      }
1614      else
1615      {
1616        // yes - use that instead
1617
setSimpleProperty(bean, name, value);
1618      }
1619    }
1620    else if(indexOfMAPPED_DELIM >= 0)
1621    {
1622      setMappedProperty(bean, name, value);
1623    }
1624    else if(indexOfINDEXED_DELIM >= 0)
1625    {
1626      setIndexedProperty(bean, name, value);
1627    }
1628    else
1629    {
1630      setSimpleProperty(bean, name, value);
1631    }
1632
1633  }
1634
1635  /**
1636   * Set the value of the specified property of the specified bean,
1637   * no matter which property reference format is used, with no
1638   * type conversions.
1639   *
1640   * @param bean Bean whose property is to be modified
1641   * @param name Possibly indexed and/or nested name of the property
1642   * to be modified
1643   * @param value Value to which this property is to be set
1644   * @throws IllegalAccessException if the caller does not have
1645   * access to the property accessor method
1646   * @throws IllegalArgumentException if <code>bean</code> or
1647   * <code>name</code> is null
1648   * @throws InvocationTargetException if the property accessor method
1649   * throws an exception
1650   * @throws NoSuchMethodException if an accessor method for this
1651   * propety cannot be found
1652   */

1653  public void setProperty(Object JavaDoc bean, String JavaDoc name, Object JavaDoc value) throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc, NoSuchMethodException JavaDoc
1654  {
1655
1656    setNestedProperty(bean, name, value);
1657
1658  }
1659
1660  /**
1661   * Set the value of the specified simple property of the specified bean,
1662   * with no type conversions.
1663   *
1664   * @param bean Bean whose property is to be modified
1665   * @param name Name of the property to be modified
1666   * @param value Value to which the property should be set
1667   * @throws IllegalAccessException if the caller does not have
1668   * access to the property accessor method
1669   * @throws IllegalArgumentException if <code>bean</code> or
1670   * <code>name</code> is null
1671   * @throws IllegalArgumentException if the property name is
1672   * nested or indexed
1673   * @throws InvocationTargetException if the property accessor method
1674   * throws an exception
1675   * @throws NoSuchMethodException if an accessor method for this
1676   * propety cannot be found
1677   */

1678  public void setSimpleProperty(Object JavaDoc bean, String JavaDoc name, Object JavaDoc value) throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc, NoSuchMethodException JavaDoc
1679  {
1680
1681    if(bean == null)
1682    {
1683      throw new IllegalArgumentException JavaDoc("No bean specified");
1684    }
1685    if(name == null)
1686    {
1687      throw new IllegalArgumentException JavaDoc("No name specified");
1688    }
1689
1690    // Validate the syntax of the property name
1691
if(name.indexOf(PropertyUtils.NESTED_DELIM) >= 0)
1692    {
1693      throw new IllegalArgumentException JavaDoc("Nested property names are not allowed");
1694    }
1695    else if(name.indexOf(PropertyUtils.INDEXED_DELIM) >= 0)
1696    {
1697      throw new IllegalArgumentException JavaDoc("Indexed property names are not allowed");
1698    }
1699    else if(name.indexOf(PropertyUtils.MAPPED_DELIM) >= 0)
1700    {
1701      throw new IllegalArgumentException JavaDoc("Mapped property names are not allowed");
1702    }
1703
1704    // Retrieve the property setter method for the specified property
1705
PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
1706    if(descriptor == null)
1707    {
1708      throw new NoSuchMethodException JavaDoc("Unknown property '" + name + "'");
1709    }
1710    Method JavaDoc writeMethod = getWriteMethod(descriptor);
1711    if(writeMethod == null)
1712    {
1713      throw new NoSuchMethodException JavaDoc("Property '" + name + "' has no setter method");
1714    }
1715
1716    // Call the property setter method
1717
Object JavaDoc values[] = new Object JavaDoc[1];
1718    values[0] = value;
1719    invokeMethod(writeMethod, bean, values);
1720
1721  }
1722
1723  /**
1724   * This just catches and wraps IllegalArgumentException.
1725   */

1726  private Object JavaDoc invokeMethod(Method JavaDoc method, Object JavaDoc bean, Object JavaDoc[] values) throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc
1727  {
1728    try
1729    {
1730      return method.invoke(bean, values);
1731    }
1732    catch(IllegalArgumentException JavaDoc e)
1733    {
1734      throw new IllegalArgumentException JavaDoc("Cannot invoke " + method.getDeclaringClass().getName() + "." + method.getName() + " - " + e.getMessage());
1735    }
1736  }
1737}
1738
Popular Tags