2
* $Header: /home/cvs/jakarta-commons/beanutils/src/java/org/apache/commons/beanutils/PropertyUtils.java,v 1.15 2001/12/15 19:19:24 craigmcc Exp $
4
* $Date: 2001/12/15 19:19:24 $
6
* ====================================================================
8
* The Apache Software License, Version 1.1
10
* Copyright (c) 1999-2001 The Apache Software Foundation. All rights
13
* Redistribution and use in source and binary forms, with or without
14
* modification, are permitted provided that the following conditions
17
* 1. Redistributions of source code must retain the above copyright
18
* notice, this list of conditions and the following disclaimer.
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
25
* 3. The end-user documentation included with the redistribution, if
26
* any, must include the following acknowlegement:
27
* "This product includes software developed by the
28
* Apache Software Foundation (http://www.apache.org/)."
29
* Alternately, this acknowlegement may appear in the software itself,
30
* if and wherever such third-party acknowlegements normally appear.
32
* 4. The names "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.
37
* 5. Products derived from this software may not be called "Apache"
38
* nor may "Apache" appear in their names without prior written
39
* permission of the Apache Group.
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
53
* ====================================================================
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/>.
63
package org.apache.commons.beanutils;
66
import java.beans.BeanInfo;
67
import java.beans.IndexedPropertyDescriptor;
68
import java.beans.IntrospectionException;
69
import java.beans.Introspector;
70
import java.beans.PropertyDescriptor;
71
import java.lang.reflect.Array;
72
import java.lang.reflect.InvocationTargetException;
73
import java.lang.reflect.Method;
74
import java.lang.reflect.Modifier;
75
import java.util.Collections;
76
import java.util.HashMap;
78
import org.apache.commons.collections.FastHashMap;
82
* Utility methods for using Java Reflection APIs to facilitate generic
83
* property getter and setter operations on Java objects. Much of this
84
* code was originally included in <code>BeanUtils</code>, but has been
85
* separated because of the volume of code involved.
87
* In general, the objects that are examined and modified using these
88
* methods are expected to conform to the property getter and setter method
89
* naming conventions described in the JavaBeans Specification (Version 1.0.1).
90
* No data type conversions are performed, and there are no usage of any
91
* <code>PropertyEditor</code> classes that have been registered, although
92
* a convenient way to access the registered classes themselves is included.
94
* For the purposes of this class, five formats for referencing a particular
95
* property value of a bean are defined, with the layout of an identifying
96
* String in parentheses:
98
* <li><strong>Simple (<code>name</code>)</strong> - The specified
99
* <code>name</code> identifies an individual property of a particular
100
* JavaBean. The name of the actual getter or setter method to be used
101
* is determined using standard JavaBeans instrospection, so that (unless
102
* overridden by a <code>BeanInfo</code> class, a property named "xyz"
103
* will have a getter method named <code>getXyz()</code> or (for boolean
104
* properties only) <code>isXyz()</code>, and a setter method named
105
* <code>setXyz()</code>.</li>
106
* <li><strong>Nested (<code>name1.name2.name3</code>)</strong> The first
107
* name element is used to select a property getter, as for simple
108
* references above. The object returned for this property is then
109
* consulted, using the same approach, for a property getter for a
110
* property named <code>name2</code>, and so on. The property value that
111
* is ultimately retrieved or modified is the one identified by the
112
* last name element.</li>
113
* <li><strong>Indexed (<code>name[index]</code>)</strong> - The underlying
114
* property value is assumed to be an array, or this JavaBean is assumed
115
* to have indexed property getter and setter methods. The appropriate
116
* (zero-relative) entry in the array is selected.</li>
117
* <li><strong>Mapped (<code>name(key)</code>)</strong> - The JavaBean
118
* is assumed to have an property getter and setter methods with an
119
* additional attribute of type <code>java.lang.String</code>.</li>
120
* <li><strong>Combined (<code>name1.name2[index].name3(key)</code>)</strong> -
121
* Combining mapped, nested, and indexed references is also
125
* @author Craig R. McClanahan
126
* @author Ralph Schaer
127
* @author Chris Audley
128
* @author Rey Fran�ois
129
* @author Gregor Ra�man
130
* @author Jan Sorensen
131
* @version $Revision: 1.15 $ $Date: 2001/12/15 19:19:24 $
134
public class PropertyUtils {
137
// ----------------------------------------------------- Manifest Constants
141
* The delimiter that preceeds the zero-relative subscript for an
144
public static final char INDEXED_DELIM = '[';
148
* The delimiter that follows the zero-relative subscript for an
151
public static final char INDEXED_DELIM2 = ']';
155
* The delimiter that preceeds the key of a mapped property.
157
public static final char MAPPED_DELIM = '(';
161
* The delimiter that follows the key of a mapped property.
163
public static final char MAPPED_DELIM2 = ')';
167
* The delimiter that separates the components of a nested reference.
169
public static final char NESTED_DELIM = '.';
172
// ------------------------------------------------------- Static Variables
176
* The debugging detail level for this component.
178
private static int debug = 0;
180
public static int getDebug() {
184
public static void setDebug(int newDebug) {
190
* The cache of PropertyDescriptor arrays for beans we have already
191
* introspected, keyed by the java.lang.Class of this object.
193
private static FastHashMap descriptorsCache = null;
194
private static FastHashMap mappedDescriptorsCache = null;
196
descriptorsCache = new FastHashMap();
197
descriptorsCache.setFast(true);
198
mappedDescriptorsCache = new FastHashMap();
199
mappedDescriptorsCache.setFast(true);
203
// --------------------------------------------------------- Public Methods
207
* Clear any cached property descriptors information for all classes
208
* loaded by any class loaders. This is useful in cases where class
209
* loaders are thrown away to implement class reloading.
211
public static void clearDescriptors() {
213
descriptorsCache.clear();
214
mappedDescriptorsCache.clear();
215
Introspector.flushCaches();
221
* Copy property values from the "origin" bean to the "destination" bean
222
* for all cases where the property names are the same (even though the
223
* actual getter and setter methods might have been customized via
224
* <code>BeanInfo</code> classes). No conversions are performed on the
225
* actual property values -- it is assumed that the values retrieved from
226
* the origin bean are assignment-compatible with the types expected by
227
* the destination bean.
229
* @param dest Destination bean whose properties are modified
230
* @param orig Origin bean whose properties are retrieved
232
* @exception IllegalAccessException if the caller does not have
233
* access to the property accessor method
234
* @exception IllegalArgumentException if the <code>dest</code> or
235
* <code>orig</code> argument is null
236
* @exception InvocationTargetException if the property accessor method
237
* throws an exception
238
* @exception NoSuchMethodException if an accessor method for this
239
* propety cannot be found
241
public static void copyProperties(Object dest, Object orig)
242
throws IllegalAccessException, InvocationTargetException,
243
NoSuchMethodException {
246
throw new IllegalArgumentException
247
("No destination bean specified");
249
throw new IllegalArgumentException("No origin bean specified");
251
PropertyDescriptor origDescriptors[] = getPropertyDescriptors(orig);
252
for (int i = 0; i < origDescriptors.length; i++) {
253
String name = origDescriptors[i].getName();
254
if (getPropertyDescriptor(dest, name) != null) {
255
Object value = getSimpleProperty(orig, name);
257
setSimpleProperty(dest, name, value);
258
} catch (NoSuchMethodException e) {
259
; // Skip non-matching property
268
* Return the entire set of properties for which the specified bean
269
* provides a read method. This map contains the unconverted property
270
* values for all properties for which a read method is provided
271
* (i.e. where the <code>getReadMethod()</code> returns non-null).
273
* @param bean Bean whose properties are to be extracted
275
* @exception IllegalAccessException if the caller does not have
276
* access to the property accessor method
277
* @exception IllegalArgumentException if <code>bean</code> is null
278
* @exception InvocationTargetException if the property accessor method
279
* throws an exception
280
* @exception NoSuchMethodException if an accessor method for this
281
* propety cannot be found
283
// FIXME - does not account for mapped properties
284
public static Map describe(Object bean)
285
throws IllegalAccessException, InvocationTargetException,
286
NoSuchMethodException {
289
throw new IllegalArgumentException("No bean specified");
290
PropertyDescriptor descriptors[] =
291
PropertyUtils.getPropertyDescriptors(bean);
292
Map description = new HashMap(descriptors.length);
293
for (int i = 0; i < descriptors.length; i++) {
294
String name = descriptors[i].getName();
295
if (descriptors[i].getReadMethod() != null)
296
description.put(name, getProperty(bean, name));
298
return (description);
304
* Return the value of the specified indexed property of the specified
305
* bean, with no type conversions. The zero-relative index of the
306
* required value must be included (in square brackets) as a suffix to
307
* the property name, or <code>IllegalArgumentException</code> will be
310
* @param bean Bean whose property is to be extracted
311
* @param name <code>propertyname[index]</code> of the property value
314
* @exception ArrayIndexOutOfBoundsException if the specified index
315
* is outside the valid range for the underlying array
316
* @exception IllegalAccessException if the caller does not have
317
* access to the property accessor method
318
* @exception IllegalArgumentException if <code>bean</code> or
319
* <code>name</code> is null
320
* @exception InvocationTargetException if the property accessor method
321
* throws an exception
322
* @exception NoSuchMethodException if an accessor method for this
323
* propety cannot be found
325
public static Object getIndexedProperty(Object bean, String name)
326
throws IllegalAccessException, InvocationTargetException,
327
NoSuchMethodException {
330
throw new IllegalArgumentException("No bean specified");
332
throw new IllegalArgumentException("No name specified");
334
// Identify the index of the requested individual property
335
int delim = name.indexOf(INDEXED_DELIM);
336
int delim2 = name.indexOf(INDEXED_DELIM2);
337
if ((delim < 0) || (delim2 <= delim))
338
throw new IllegalArgumentException("Invalid indexed property '" +
342
String subscript = name.substring(delim + 1, delim2);
343
index = Integer.parseInt(subscript);
344
} catch (NumberFormatException e) {
345
throw new IllegalArgumentException("Invalid indexed property '" +
348
name = name.substring(0, delim);
350
// Request the specified indexed property value
351
return (getIndexedProperty(bean, name, index));
357
* Return the value of the specified indexed property of the specified
358
* bean, with no type conversions.
360
* @param bean Bean whose property is to be extracted
361
* @param name Simple property name of the property value to be extracted
362
* @param index Index of the property value to be extracted
364
* @exception ArrayIndexOutOfBoundsException if the specified index
365
* is outside the valid range for the underlying array
366
* @exception IllegalAccessException if the caller does not have
367
* access to the property accessor method
368
* @exception IllegalArgumentException if <code>bean</code> or
369
* <code>name</code> is null
370
* @exception InvocationTargetException if the property accessor method
371
* throws an exception
372
* @exception NoSuchMethodException if an accessor method for this
373
* propety cannot be found
375
public static Object getIndexedProperty(Object bean,
376
String name, int index)
377
throws IllegalAccessException, InvocationTargetException,
378
NoSuchMethodException {
381
throw new IllegalArgumentException("No bean specified");
383
throw new IllegalArgumentException("No name specified");
385
// Retrieve the property descriptor for the specified property
386
PropertyDescriptor descriptor =
387
getPropertyDescriptor(bean, name);
388
if (descriptor == null)
389
throw new NoSuchMethodException("Unknown property '" +
392
// Call the indexed getter method if there is one
393
if (descriptor instanceof IndexedPropertyDescriptor) {
394
Method readMethod = ((IndexedPropertyDescriptor) descriptor).
395
getIndexedReadMethod();
396
if (readMethod != null) {
397
Object subscript[] = new Object[1];
398
subscript[0] = new Integer(index);
400
return (readMethod.invoke(bean, subscript));
401
} catch (InvocationTargetException e) {
402
if (e.getTargetException() instanceof
403
ArrayIndexOutOfBoundsException)
404
throw (ArrayIndexOutOfBoundsException)
405
e.getTargetException();
412
// Otherwise, the underlying property must be an array
413
Method readMethod = getReadMethod(descriptor);
414
if (readMethod == null)
415
throw new NoSuchMethodException("Property '" + name +
416
"' has no getter method");
418
// Call the property getter and return the value
419
Object value = readMethod.invoke(bean, new Object[0]);
420
if (!value.getClass().isArray())
421
throw new IllegalArgumentException("Property '" + name +
423
return (Array.get(value, index));
429
* Return the value of the specified mapped property of the
430
* specified bean, with no type conversions. The key of the
431
* required value must be included (in brackets) as a suffix to
432
* the property name, or <code>IllegalArgumentException</code> will be
435
* @param bean Bean whose property is to be extracted
436
* @param name <code>propertyname(key)</code> of the property value
439
* @exception IllegalAccessException if the caller does not have
440
* access to the property accessor method
441
* @exception InvocationTargetException if the property accessor method
442
* throws an exception
443
* @exception NoSuchMethodException if an accessor method for this
444
* propety cannot be found
446
public static Object getMappedProperty(Object bean, String name)
447
throws IllegalAccessException, InvocationTargetException,
448
NoSuchMethodException {
451
throw new IllegalArgumentException("No bean specified");
453
throw new IllegalArgumentException("No name specified");
455
// Identify the index of the requested individual property
456
int delim = name.indexOf(MAPPED_DELIM);
457
int delim2 = name.indexOf(MAPPED_DELIM2);
458
if ((delim < 0) || (delim2 <= delim))
459
throw new IllegalArgumentException
460
("Invalid mapped property '" + name + "'");
461
// Isolate the name and the key
462
String key = name.substring(delim + 1, delim2);
463
name = name.substring(0, delim);
465
// Request the specified indexed property value
466
return (getMappedProperty(bean, name, key));
472
* Return the value of the specified mapped property of the specified
473
* bean, with no type conversions.
475
* @param bean Bean whose property is to be extracted
476
* @param name Mapped property name of the property value to be extracted
477
* @param key Key of the property value to be extracted
479
* @exception IllegalAccessException if the caller does not have
480
* access to the property accessor method
481
* @exception InvocationTargetException if the property accessor method
482
* throws an exception
483
* @exception NoSuchMethodException if an accessor method for this
484
* propety cannot be found
486
public static Object getMappedProperty(Object bean,
487
String name, String key)
488
throws IllegalAccessException, InvocationTargetException,
489
NoSuchMethodException {
492
throw new IllegalArgumentException("No bean specified");
494
throw new IllegalArgumentException("No name specified");
496
throw new IllegalArgumentException("No key specified");
498
Object result = null;
500
// Retrieve the property descriptor for the specified property
501
PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
502
if (descriptor == null)
503
throw new NoSuchMethodException("Unknown property '" +
506
if (descriptor instanceof MappedPropertyDescriptor) {
507
// Call the keyed getter method if there is one
508
Method readMethod = ((MappedPropertyDescriptor)descriptor).
509
getMappedReadMethod();
510
if (readMethod != null) {
511
Object keyArray[] = new Object[1];
513
result = readMethod.invoke(bean, keyArray);
515
throw new NoSuchMethodException("Property '" + name +
516
"' has no mapped getter method");
525
* Return the mapped property descriptors for this bean.
527
* @param bean Bean to be introspected
529
public static FastHashMap getMappedPropertyDescriptors(Object bean) {
534
// Look up any cached descriptors for this bean class
535
Class beanClass = bean.getClass();
536
return (FastHashMap) mappedDescriptorsCache.get(beanClass);
542
* Return the value of the (possibly nested) property of the specified
543
* name, for the specified bean, with no type conversions.
545
* @param bean Bean whose property is to be extracted
546
* @param name Possibly nested name of the property to be extracted
548
* @exception IllegalAccessException if the caller does not have
549
* access to the property accessor method
550
* @exception IllegalArgumentException if <code>bean</code> or
551
* <code>name</code> is null
552
* @exception IllegalArgumentException if a nested reference to a
553
* property returns null
554
* @exception InvocationTargetException if the property accessor method
555
* throws an exception
556
* @exception NoSuchMethodException if an accessor method for this
557
* propety cannot be found
559
public static Object getNestedProperty(Object bean, String name)
560
throws IllegalAccessException, InvocationTargetException,
561
NoSuchMethodException {
564
throw new IllegalArgumentException("No bean specified");
566
throw new IllegalArgumentException("No name specified");
568
int indexOfINDEXED_DELIM = -1;
569
int indexOfMAPPED_DELIM = -1;
572
int delim = name.indexOf(NESTED_DELIM);
575
String next = name.substring(0, delim);
576
if (next.indexOf(INDEXED_DELIM) >= 0)
577
bean = getIndexedProperty(bean, next);
579
bean = getSimpleProperty(bean, next);
581
throw new IllegalArgumentException
582
("Null property value for '" +
583
name.substring(0, delim) + "'");
584
name = name.substring(delim + 1);
586
int period = name.indexOf(NESTED_DELIM);
589
String next = name.substring(0, period);
590
indexOfINDEXED_DELIM = next.indexOf(INDEXED_DELIM);
591
indexOfMAPPED_DELIM = next.indexOf(MAPPED_DELIM);
592
if (indexOfMAPPED_DELIM >= 0 &&
593
(indexOfINDEXED_DELIM <0 ||
594
indexOfMAPPED_DELIM < indexOfINDEXED_DELIM))
595
bean = getMappedProperty(bean, next);
597
if (indexOfINDEXED_DELIM >= 0)
598
bean = getIndexedProperty(bean, next);
600
bean = getSimpleProperty(bean, next);
603
throw new IllegalArgumentException
604
("Null property value for '" +
605
name.substring(0, period) + "'");
606
name = name.substring(period + 1);
610
if (name.indexOf(INDEXED_DELIM) >= 0)
611
return (getIndexedProperty(bean, name));
613
return (getSimpleProperty(bean, name));
615
indexOfINDEXED_DELIM = name.indexOf(INDEXED_DELIM);
616
indexOfMAPPED_DELIM = name.indexOf(MAPPED_DELIM);
618
if (indexOfMAPPED_DELIM >= 0 && (indexOfINDEXED_DELIM <0 || indexOfMAPPED_DELIM < indexOfINDEXED_DELIM))
619
bean = getMappedProperty(bean, name);
621
if (indexOfINDEXED_DELIM >= 0)
622
bean = getIndexedProperty(bean, name);
624
bean = getSimpleProperty(bean, name);
632
* Return the value of the specified property of the specified bean,
633
* no matter which property reference format is used, with no
636
* @param bean Bean whose property is to be extracted
637
* @param name Possibly indexed and/or nested name of the property
640
* @exception IllegalAccessException if the caller does not have
641
* access to the property accessor method
642
* @exception IllegalArgumentException if <code>bean</code> or
643
* <code>name</code> is null
644
* @exception InvocationTargetException if the property accessor method
645
* throws an exception
646
* @exception NoSuchMethodException if an accessor method for this
647
* propety cannot be found
649
public static Object getProperty(Object bean, String name)
650
throws IllegalAccessException, InvocationTargetException,
651
NoSuchMethodException {
653
return (getNestedProperty(bean, name));
659
* Retrieve the property descriptor for the specified property of the
660
* specified bean, or return <code>null</code> if there is no such
661
* descriptor. This method resolves indexed and nested property
662
* references in the same manner as other methods in this class, except
663
* that if the last (or only) name element is indexed, the descriptor
664
* for the last resolved property itself is returned.
666
* @param bean Bean for which a property descriptor is requested
667
* @param name Possibly indexed and/or nested name of the property for
668
* which a property descriptor is requested
670
* @exception IllegalAccessException if the caller does not have
671
* access to the property accessor method
672
* @exception IllegalArgumentException if <code>bean</code> or
673
* <code>name</code> is null
674
* @exception IllegalArgumentException if a nested reference to a
675
* property returns null
676
* @exception InvocationTargetException if the property accessor method
677
* throws an exception
678
* @exception NoSuchMethodException if an accessor method for this
679
* propety cannot be found
681
public static PropertyDescriptor getPropertyDescriptor(Object bean,
683
throws IllegalAccessException, InvocationTargetException,
684
NoSuchMethodException {
687
throw new IllegalArgumentException("No bean specified");
689
throw new IllegalArgumentException("No name specified");
691
// Resolve nested references
693
int period = name.indexOf(NESTED_DELIM);
696
String next = name.substring(0, period);
698
if (next.indexOf(INDEXED_DELIM) >= 0)
699
bean = getIndexedProperty(bean, next);
701
bean = getSimpleProperty(bean, next);
703
throw new IllegalArgumentException
704
("Null property value for '" +
705
name.substring(0, period) + "'");
706
name = name.substring(period + 1);
708
int indexOfINDEXED_DELIM = next.indexOf(INDEXED_DELIM);
709
int indexOfMAPPED_DELIM = next.indexOf(MAPPED_DELIM);
710
if (indexOfMAPPED_DELIM >= 0 && (indexOfINDEXED_DELIM <0 || indexOfMAPPED_DELIM < indexOfINDEXED_DELIM))
711
bean = getMappedProperty(bean, next);
713
if (indexOfINDEXED_DELIM >= 0)
714
bean = getIndexedProperty(bean, next);
716
bean = getSimpleProperty(bean, next);
719
throw new IllegalArgumentException
720
("Null property value for '" +
721
name.substring(0, period) + "'");
722
name = name.substring(period + 1);
725
// Remove any subscript from the final name value
726
int left = name.indexOf(INDEXED_DELIM);
728
name = name.substring(0, left);
729
left = name.indexOf(MAPPED_DELIM);
731
name = name.substring(0, left);
733
// Look up and return this property from our cache
734
// creating and adding it to the cache if not found.
735
if ((bean == null) || (name == null))
738
PropertyDescriptor descriptors[] = getPropertyDescriptors(bean);
739
if (descriptors != null) {
740
for (int i = 0; i < descriptors.length; i++) {
741
if (name.equals(descriptors[i].getName()))
742
return (descriptors[i]);
746
PropertyDescriptor result = null;
747
FastHashMap mappedDescriptors =
748
getMappedPropertyDescriptors(bean);
749
if (mappedDescriptors != null) {
750
result = (PropertyDescriptor) mappedDescriptors.get(name);
753
// not found, try to create it
756
new MappedPropertyDescriptor(name, bean.getClass());
757
} catch (IntrospectionException ie) {
761
if (mappedDescriptors==null) {
762
mappedDescriptors = new FastHashMap();
763
mappedDescriptors.setFast(true);
764
mappedDescriptorsCache.put
765
(bean.getClass(), mappedDescriptors);
767
mappedDescriptors.put(name, result);
775
* Retrieve the property descriptors for the specified bean, introspecting
776
* and caching them the first time a particular bean class is encountered.
778
* @param bean Bean for which property descriptors are requested
780
* @exception IllegalArgumentException if <code>bean</code> is null
782
public static PropertyDescriptor[] getPropertyDescriptors(Object bean) {
785
throw new IllegalArgumentException("No bean specified");
787
// Look up any cached descriptors for this bean class
788
Class beanClass = bean.getClass();
789
PropertyDescriptor descriptors[] = null;
791
(PropertyDescriptor[]) descriptorsCache.get(beanClass);
792
if (descriptors != null)
793
return (descriptors);
795
// Introspect the bean and cache the generated descriptors
796
BeanInfo beanInfo = null;
798
beanInfo = Introspector.getBeanInfo(bean.getClass());
799
} catch (IntrospectionException e) {
800
return (new PropertyDescriptor[0]);
802
descriptors = beanInfo.getPropertyDescriptors();
803
if (descriptors == null)
804
descriptors = new PropertyDescriptor[0];
805
descriptorsCache.put(beanClass, descriptors);
806
return (descriptors);
812
* Return the Java Class repesenting the property editor class that has
813
* been registered for this property (if any). This method follows the
814
* same name resolution rules used by <code>getPropertyDescriptor()</code>,
815
* so if the last element of a name reference is indexed, the property
816
* editor for the underlying property's class is returned.
818
* Note that <code>null</code> will be returned if there is no property,
819
* or if there is no registered property editor class. Because this
820
* return value is ambiguous, you should determine the existence of the
821
* property itself by other means.
823
* @param bean Bean for which a property descriptor is requested
824
* @param name Possibly indexed and/or nested name of the property for
825
* which a property descriptor is requested
827
* @exception IllegalAccessException if the caller does not have
828
* access to the property accessor method
829
* @exception IllegalArgumentException if <code>bean</code> or
830
* <code>name</code> is null
831
* @exception IllegalArgumentException if a nested reference to a
832
* property returns null
833
* @exception InvocationTargetException if the property accessor method
834
* throws an exception
835
* @exception NoSuchMethodException if an accessor method for this
836
* propety cannot be found
838
public static Class getPropertyEditorClass(Object bean, String name)
839
throws IllegalAccessException, InvocationTargetException,
840
NoSuchMethodException {
843
throw new IllegalArgumentException("No bean specified");
845
throw new IllegalArgumentException("No name specified");
847
PropertyDescriptor descriptor =
848
getPropertyDescriptor(bean, name);
849
if (descriptor != null)
850
return (descriptor.getPropertyEditorClass());
858
* Return the Java Class representing the property type of the specified
859
* property, or <code>null</code> if there is no such property for the
860
* specified bean. This method follows the same name resolution rules
861
* used by <code>getPropertyDescriptor()</code>, so if the last element
862
* of a name reference is indexed, the type of the property itself will
863
* be returned. If the last (or only) element has no property with the
864
* specified name, <code>null</code> is returned.
866
* @param bean Bean for which a property descriptor is requested
867
* @param name Possibly indexed and/or nested name of the property for
868
* which a property descriptor is requested
870
* @exception IllegalAccessException if the caller does not have
871
* access to the property accessor method
872
* @exception IllegalArgumentException if <code>bean</code> or
873
* <code>name</code> is null
874
* @exception IllegalArgumentException if a nested reference to a
875
* property returns null
876
* @exception InvocationTargetException if the property accessor method
877
* throws an exception
878
* @exception NoSuchMethodException if an accessor method for this
879
* propety cannot be found
881
public static Class getPropertyType(Object bean, String name)
882
throws IllegalAccessException, InvocationTargetException,
883
NoSuchMethodException {
886
throw new IllegalArgumentException("No bean specified");
888
throw new IllegalArgumentException("No name specified");
890
PropertyDescriptor descriptor =
891
getPropertyDescriptor(bean, name);
892
if (descriptor == null)
894
else if (descriptor instanceof IndexedPropertyDescriptor)
895
return (((IndexedPropertyDescriptor) descriptor).
896
getIndexedPropertyType());
898
return (descriptor.getPropertyType());
904
* Return an accessible property getter method for this property,
905
* if there is one; otherwise return <code>null</code>.
907
* @param descriptor Property descriptor to return a getter for
909
public static Method getReadMethod(PropertyDescriptor descriptor) {
911
return (MethodUtils.getAccessibleMethod(descriptor.getReadMethod()));
917
* Return the value of the specified simple property of the specified
918
* bean, with no type conversions.
920
* @param bean Bean whose property is to be extracted
921
* @param name Name of the property to be extracted
923
* @exception IllegalAccessException if the caller does not have
924
* access to the property accessor method
925
* @exception IllegalArgumentException if <code>bean</code> or
926
* <code>name</code> is null
927
* @exception IllegalArgumentException if the property name
928
* is nested or indexed
929
* @exception InvocationTargetException if the property accessor method
930
* throws an exception
931
* @exception NoSuchMethodException if an accessor method for this
932
* propety cannot be found
934
public static Object getSimpleProperty(Object bean, String name)
935
throws IllegalAccessException, InvocationTargetException,
936
NoSuchMethodException {
939
throw new IllegalArgumentException("No bean specified");
941
throw new IllegalArgumentException("No name specified");
943
// Validate the syntax of the property name
944
if (name.indexOf(NESTED_DELIM) >= 0)
945
throw new IllegalArgumentException
946
("Nested property names are not allowed");
947
else if (name.indexOf(INDEXED_DELIM) >= 0)
948
throw new IllegalArgumentException
949
("Indexed property names are not allowed");
950
else if (name.indexOf(MAPPED_DELIM) >= 0)
951
throw new IllegalArgumentException
952
("Mapped property names are not allowed");
954
// Retrieve the property getter method for the specified property
955
PropertyDescriptor descriptor =
956
getPropertyDescriptor(bean, name);
957
if (descriptor == null)
958
throw new NoSuchMethodException("Unknown property '" +
960
Method readMethod = getReadMethod(descriptor);
961
if (readMethod == null)
962
throw new NoSuchMethodException("Property '" + name +
963
"' has no getter method");
965
// Call the property getter and return the value
966
Object value = readMethod.invoke(bean, new Object[0]);
973
* Return an accessible property setter method for this property,
974
* if there is one; otherwise return <code>null</code>.
976
* @param descriptor Property descriptor to return a setter for
978
public static Method getWriteMethod(PropertyDescriptor descriptor) {
980
return (MethodUtils.getAccessibleMethod(descriptor.getWriteMethod()));
986
* Set the value of the specified indexed property of the specified
987
* bean, with no type conversions. The zero-relative index of the
988
* required value must be included (in square brackets) as a suffix to
989
* the property name, or <code>IllegalArgumentException</code> will be
992
* @param bean Bean whose property is to be modified
993
* @param name <code>propertyname[index]</code> of the property value
995
* @param value Value to which the specified property element
998
* @exception ArrayIndexOutOfBoundsException if the specified index
999
* is outside the valid range for the underlying array
1000
* @exception IllegalAccessException if the caller does not have
1001
* access to the property accessor method
1002
* @exception IllegalArgumentException if <code>bean</code> or
1003
* <code>name</code> is null
1004
* @exception InvocationTargetException if the property accessor method
1005
* throws an exception
1006
* @exception NoSuchMethodException if an accessor method for this
1007
* propety cannot be found
1009
public static void setIndexedProperty(Object bean, String name,
1011
throws IllegalAccessException, InvocationTargetException,
1012
NoSuchMethodException {
1015
throw new IllegalArgumentException("No bean specified");
1017
throw new IllegalArgumentException("No name specified");
1019
// Identify the index of the requested individual property
1020
int delim = name.indexOf(INDEXED_DELIM);
1021
int delim2 = name.indexOf(INDEXED_DELIM2);
1022
if ((delim < 0) || (delim2 <= delim))
1023
throw new IllegalArgumentException("Invalid indexed property '" +
1027
String subscript = name.substring(delim + 1, delim2);
1028
index = Integer.parseInt(subscript);
1029
} catch (NumberFormatException e) {
1030
throw new IllegalArgumentException("Invalid indexed property '" +
1033
name = name.substring(0, delim);
1035
// Set the specified indexed property value
1036
setIndexedProperty(bean, name, index, value);
1042
* Set the value of the specified indexed property of the specified
1043
* bean, with no type conversions.
1045
* @param bean Bean whose property is to be set
1046
* @param name Simple property name of the property value to be set
1047
* @param index Index of the property value to be set
1048
* @param value Value to which the indexed property element is to be set
1050
* @exception ArrayIndexOutOfBoundsException if the specified index
1051
* is outside the valid range for the underlying array
1052
* @exception IllegalAccessException if the caller does not have
1053
* access to the property accessor method
1054
* @exception IllegalArgumentException if <code>bean</code> or
1055
* <code>name</code> is null
1056
* @exception InvocationTargetException if the property accessor method
1057
* throws an exception
1058
* @exception NoSuchMethodException if an accessor method for this
1059
* propety cannot be found
1061
public static void setIndexedProperty(Object bean, String name,
1062
int index, Object value)
1063
throws IllegalAccessException, InvocationTargetException,
1064
NoSuchMethodException {
1067
throw new IllegalArgumentException("No bean specified");
1069
throw new IllegalArgumentException("No name specified");
1071
// Retrieve the property descriptor for the specified property
1072
PropertyDescriptor descriptor =
1073
getPropertyDescriptor(bean, name);
1074
if (descriptor == null)
1075
throw new NoSuchMethodException("Unknown property '" +
1078
// Call the indexed setter method if there is one
1079
if (descriptor instanceof IndexedPropertyDescriptor) {
1080
Method writeMethod = ((IndexedPropertyDescriptor) descriptor).
1081
getIndexedWriteMethod();
1082
if (writeMethod != null) {
1083
Object subscript[] = new Object[2];
1084
subscript[0] = new Integer(index);
1085
subscript[1] = value;
1087
writeMethod.invoke(bean, subscript);
1088
} catch (InvocationTargetException e) {
1089
if (e.getTargetException() instanceof
1090
ArrayIndexOutOfBoundsException)
1091
throw (ArrayIndexOutOfBoundsException)
1092
e.getTargetException();
1100
// Otherwise, the underlying property must be an array
1101
Method readMethod = descriptor.getReadMethod();
1102
if (readMethod == null)
1103
throw new NoSuchMethodException("Property '" + name +
1104
"' has no getter method");
1106
// Call the property getter to get the array
1107
Object array = readMethod.invoke(bean, new Object[0]);
1108
if (!array.getClass().isArray())
1109
throw new IllegalArgumentException("Property '" + name +
1110
"' is not indexed");
1112
// Modify the specified value
1113
Array.set(array, index, value);
1119
* Set the value of the specified mapped property of the
1120
* specified bean, with no type conversions. The key of the
1121
* value to set must be included (in brackets) as a suffix to
1122
* the property name, or <code>IllegalArgumentException</code> will be
1125
* @param bean Bean whose property is to be set
1126
* @param name <code>propertyname(key)</code> of the property value
1128
* @param value The property value to be set
1130
* @exception IllegalAccessException if the caller does not have
1131
* access to the property accessor method
1132
* @exception InvocationTargetException if the property accessor method
1133
* throws an exception
1134
* @exception NoSuchMethodException if an accessor method for this
1135
* propety cannot be found
1137
public static void setMappedProperty(Object bean, String name,
1139
throws IllegalAccessException, InvocationTargetException,
1140
NoSuchMethodException {
1143
throw new IllegalArgumentException("No bean specified");
1145
throw new IllegalArgumentException("No name specified");
1147
// Identify the index of the requested individual property
1148
int delim = name.indexOf(MAPPED_DELIM);
1149
int delim2 = name.indexOf(MAPPED_DELIM2);
1150
if ((delim < 0) || (delim2 <= delim))
1151
throw new IllegalArgumentException
1152
("Invalid mapped property '" + name + "'");
1153
// Isolate the name and the key
1154
String key = name.substring(delim + 1, delim2);
1155
name = name.substring(0, delim);
1157
// Request the specified indexed property value
1158
setMappedProperty(bean, name, key, value);
1164
* Set the value of the specified mapped property of the specified
1165
* bean, with no type conversions.
1167
* @param bean Bean whose property is to be set
1168
* @param name Mapped property name of the property value to be set
1169
* @param key Key of the property value to be set
1170
* @param value The property value to be set
1172
* @exception IllegalAccessException if the caller does not have
1173
* access to the property accessor method
1174
* @exception InvocationTargetException if the property accessor method
1175
* throws an exception
1176
* @exception NoSuchMethodException if an accessor method for this
1177
* propety cannot be found
1179
public static void setMappedProperty(Object bean, String name,
1180
String key, Object value)
1181
throws IllegalAccessException, InvocationTargetException,
1182
NoSuchMethodException {
1185
throw new IllegalArgumentException("No bean specified");
1187
throw new IllegalArgumentException("No name specified");
1189
throw new IllegalArgumentException("No key specified");
1191
// Retrieve the property descriptor for the specified property
1192
PropertyDescriptor descriptor =
1193
getPropertyDescriptor(bean, name);
1194
if (descriptor == null)
1195
throw new NoSuchMethodException("Unknown property '" +
1198
if (descriptor instanceof MappedPropertyDescriptor) {
1199
// Call the keyed setter method if there is one
1200
Method mappedWriteMethod =
1201
((MappedPropertyDescriptor)descriptor).
1202
getMappedWriteMethod();
1203
if (mappedWriteMethod != null) {
1204
Object params[] = new Object[2];
1207
mappedWriteMethod.invoke(bean, params);
1209
throw new NoSuchMethodException
1210
("Property '" + name +
1211
"' has no mapped setter method");
1219
* Set the value of the (possibly nested) property of the specified
1220
* name, for the specified bean, with no type conversions.
1222
* @param bean Bean whose property is to be modified
1223
* @param name Possibly nested name of the property to be modified
1224
* @param value Value to which the property is to be set
1226
* @exception IllegalAccessException if the caller does not have
1227
* access to the property accessor method
1228
* @exception IllegalArgumentException if <code>bean</code> or
1229
* <code>name</code> is null
1230
* @exception IllegalArgumentException if a nested reference to a
1231
* property returns null
1232
* @exception InvocationTargetException if the property accessor method
1233
* throws an exception
1234
* @exception NoSuchMethodException if an accessor method for this
1235
* propety cannot be found
1237
public static void setNestedProperty(Object bean,
1238
String name, Object value)
1239
throws IllegalAccessException, InvocationTargetException,
1240
NoSuchMethodException {
1243
throw new IllegalArgumentException("No bean specified");
1245
throw new IllegalArgumentException("No name specified");
1247
int indexOfINDEXED_DELIM = -1;
1248
int indexOfMAPPED_DELIM = -1;
1250
int delim = name.indexOf(NESTED_DELIM);
1253
String next = name.substring(0, delim);
1255
if (next.indexOf(INDEXED_DELIM) >= 0)
1256
bean = getIndexedProperty(bean, next);
1258
bean = getSimpleProperty(bean, next);
1260
throw new IllegalArgumentException
1261
("Null property value for '" +
1262
name.substring(0, delim) + "'");
1263
name = name.substring(delim + 1);
1265
indexOfINDEXED_DELIM = next.indexOf(INDEXED_DELIM);
1266
indexOfMAPPED_DELIM = next.indexOf(MAPPED_DELIM);
1267
if (indexOfMAPPED_DELIM >= 0 && (indexOfINDEXED_DELIM <0 || indexOfMAPPED_DELIM < indexOfINDEXED_DELIM))
1268
bean = getMappedProperty(bean, next);
1270
if (indexOfINDEXED_DELIM >= 0)
1271
bean = getIndexedProperty(bean, next);
1273
bean = getSimpleProperty(bean, next);
1276
throw new IllegalArgumentException
1277
("Null property value for '" +
1278
name.substring(0, delim) + "'");
1279
name = name.substring(delim + 1);
1283
if (name.indexOf(INDEXED_DELIM) >= 0)
1284
setIndexedProperty(bean, name, value);
1286
setSimpleProperty(bean, name, value);
1288
indexOfINDEXED_DELIM = name.indexOf(INDEXED_DELIM);
1289
indexOfMAPPED_DELIM = name.indexOf(MAPPED_DELIM);
1291
if (indexOfMAPPED_DELIM >= 0 &&
1292
(indexOfINDEXED_DELIM <0 ||
1293
indexOfMAPPED_DELIM < indexOfINDEXED_DELIM))
1294
setMappedProperty(bean, name, value);
1296
if (indexOfINDEXED_DELIM >= 0)
1297
setIndexedProperty(bean, name, value);
1299
setSimpleProperty(bean, name, value);
1306
* Set the value of the specified property of the specified bean,
1307
* no matter which property reference format is used, with no
1310
* @param bean Bean whose property is to be modified
1311
* @param name Possibly indexed and/or nested name of the property
1313
* @param value Value to which this property is to be set
1315
* @exception IllegalAccessException if the caller does not have
1316
* access to the property accessor method
1317
* @exception IllegalArgumentException if <code>bean</code> or
1318
* <code>name</code> is null
1319
* @exception InvocationTargetException if the property accessor method
1320
* throws an exception
1321
* @exception NoSuchMethodException if an accessor method for this
1322
* propety cannot be found
1324
public static void setProperty(Object bean, String name, Object value)
1325
throws IllegalAccessException, InvocationTargetException,
1326
NoSuchMethodException {
1328
setNestedProperty(bean, name, value);
1334
* Set the value of the specified simple property of the specified bean,
1335
* with no type conversions.
1337
* @param bean Bean whose property is to be modified
1338
* @param name Name of the property to be modified
1339
* @param value Value to which the property should be set
1341
* @exception IllegalAccessException if the caller does not have
1342
* access to the property accessor method
1343
* @exception IllegalArgumentException if <code>bean</code> or
1344
* <code>name</code> is null
1345
* @exception IllegalArgumentException if the property name is
1347
* @exception InvocationTargetException if the property accessor method
1348
* throws an exception
1349
* @exception NoSuchMethodException if an accessor method for this
1350
* propety cannot be found
1352
public static void setSimpleProperty(Object bean,
1353
String name, Object value)
1354
throws IllegalAccessException, InvocationTargetException,
1355
NoSuchMethodException {
1358
throw new IllegalArgumentException("No bean specified");
1360
throw new IllegalArgumentException("No name specified");
1362
// Validate the syntax of the property name
1363
if (name.indexOf(NESTED_DELIM) >= 0)
1364
throw new IllegalArgumentException
1365
("Nested property names are not allowed");
1366
else if (name.indexOf(INDEXED_DELIM) >= 0)
1367
throw new IllegalArgumentException
1368
("Indexed property names are not allowed");
1369
else if (name.indexOf(MAPPED_DELIM) >= 0)
1370
throw new IllegalArgumentException
1371
("Mapped property names are not allowed");
1373
// Retrieve the property setter method for the specified property
1374
PropertyDescriptor descriptor =
1375
getPropertyDescriptor(bean, name);
1376
if (descriptor == null)
1377
throw new NoSuchMethodException("Unknown property '" +
1379
Method writeMethod = getWriteMethod(descriptor);
1380
if (writeMethod == null)
1381
throw new NoSuchMethodException("Property '" + name +
1382
"' has no setter method");
1384
// Call the property setter method
1385
Object values[] = new Object[1];
1387
writeMethod.invoke(bean, values);