2
* $Header: /home/cvs/jakarta-commons/beanutils/src/java/org/apache/commons/beanutils/BeanUtils.java,v 1.6 2001/09/19 02:04:10 craigmcc Exp $
4
* $Date: 2001/09/19 02:04:10 $
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.util.ArrayList;
75
import java.util.Collection;
76
import java.util.Collections;
77
import java.util.HashMap;
78
import java.util.Iterator;
83
* Utility methods for populating JavaBeans properties via reflection.
85
* @author Craig R. McClanahan
86
* @author Ralph Schaer
87
* @author Chris Audley
88
* @author Rey Fran�ois
89
* @author Gregor Ra�man
90
* @version $Revision: 1.6 $ $Date: 2001/09/19 02:04:10 $
93
public class BeanUtils {
96
// ------------------------------------------------------ Private Variables
100
* The debugging detail level for this component.
102
private static int debug = 0;
104
public static int getDebug() {
108
public static void setDebug(int newDebug) {
113
// --------------------------------------------------------- Public Classes
117
* Clone a bean based on the available property getters and setters,
118
* even if the bean class itself does not implement Cloneable.
120
* @param bean Bean to be cloned
122
* @exception IllegalAccessException if the caller does not have
123
* access to the property accessor method
124
* @exception InstantiationException if a new instance of the bean's
125
* class cannot be instantiated
126
* @exception InvocationTargetException if the property accessor method
127
* throws an exception
128
* @exception NoSuchMethodException if an accessor method for this
129
* propety cannot be found
131
public static Object cloneBean(Object bean)
132
throws IllegalAccessException, InstantiationException,
133
InvocationTargetException, NoSuchMethodException {
135
Class clazz = bean.getClass();
136
Object newBean = clazz.newInstance();
137
PropertyUtils.copyProperties(newBean, bean);
144
* Return the entire set of properties for which the specified bean
145
* provides a read method. This map can be fed back to a call to
146
* <code>BeanUtils.populate()</code> to reconsitute the same set of
147
* properties, modulo differences for read-only and write-only
150
* @param bean Bean whose properties are to be extracted
152
* @exception IllegalAccessException if the caller does not have
153
* access to the property accessor method
154
* @exception InvocationTargetException if the property accessor method
155
* throws an exception
156
* @exception NoSuchMethodException if an accessor method for this
157
* propety cannot be found
159
public static Map describe(Object bean)
160
throws IllegalAccessException, InvocationTargetException,
161
NoSuchMethodException {
164
// return (Collections.EMPTY_MAP);
165
return (new java.util.HashMap());
166
PropertyDescriptor descriptors[] =
167
PropertyUtils.getPropertyDescriptors(bean);
168
Map description = new HashMap(descriptors.length);
169
for (int i = 0; i < descriptors.length; i++) {
170
String name = descriptors[i].getName();
171
if (descriptors[i].getReadMethod() != null)
172
description.put(name, getProperty(bean, name));
174
return (description);
180
* Return the value of the specified array property of the specified
181
* bean, as a String array.
183
* @param bean Bean whose property is to be extracted
184
* @param name Name of the property to be extracted
186
* @exception IllegalAccessException if the caller does not have
187
* access to the property accessor method
188
* @exception InvocationTargetException if the property accessor method
189
* throws an exception
190
* @exception NoSuchMethodException if an accessor method for this
191
* propety cannot be found
193
public static String[] getArrayProperty(Object bean, String name)
194
throws IllegalAccessException, InvocationTargetException,
195
NoSuchMethodException {
197
Object value = PropertyUtils.getProperty(bean, name);
200
} else if (value instanceof Collection) {
201
ArrayList values = new ArrayList();
202
Iterator items = ((Collection) value).iterator();
203
while (items.hasNext()) {
204
Object item = items.next();
206
values.add((String) null);
208
values.add(item.toString());
210
return ((String[]) values.toArray(new String[values.size()]));
211
} else if (value.getClass().isArray()) {
212
ArrayList values = new ArrayList();
214
int n = Array.getLength(value);
215
for (int i = 0; i < n; i++) {
216
values.add(Array.get(value, i).toString());
218
} catch (ArrayIndexOutOfBoundsException e) {
221
return ((String[]) values.toArray(new String[values.size()]));
223
String results[] = new String[1];
224
results[0] = value.toString();
232
* Return the value of the specified indexed property of the specified
233
* bean, as a String. The zero-relative index of the
234
* required value must be included (in square brackets) as a suffix to
235
* the property name, or <code>IllegalArgumentException</code> will be
238
* @param bean Bean whose property is to be extracted
239
* @param name <code>propertyname[index]</code> of the property value
242
* @exception IllegalAccessException if the caller does not have
243
* access to the property accessor method
244
* @exception InvocationTargetException if the property accessor method
245
* throws an exception
246
* @exception NoSuchMethodException if an accessor method for this
247
* propety cannot be found
249
public static String getIndexedProperty(Object bean, String name)
250
throws IllegalAccessException, InvocationTargetException,
251
NoSuchMethodException {
253
Object value = PropertyUtils.getIndexedProperty(bean, name);
254
return (ConvertUtils.convert(value));
260
* Return the value of the specified indexed property of the specified
261
* bean, as a String. The index is specified as a method parameter and
262
* must *not* be included in the property name expression
264
* @param bean Bean whose property is to be extracted
265
* @param name Simple property name of the property value to be extracted
266
* @param index Index of the property value to be extracted
268
* @exception IllegalAccessException if the caller does not have
269
* access to the property accessor method
270
* @exception InvocationTargetException if the property accessor method
271
* throws an exception
272
* @exception NoSuchMethodException if an accessor method for this
273
* propety cannot be found
275
public static String getIndexedProperty(Object bean,
276
String name, int index)
277
throws IllegalAccessException, InvocationTargetException,
278
NoSuchMethodException {
280
Object value = PropertyUtils.getIndexedProperty(bean, name, index);
281
return (ConvertUtils.convert(value));
287
* Return the value of the specified indexed property of the specified
288
* bean, as a String. The String-valued key of the required value
289
* must be included (in parentheses) as a suffix to
290
* the property name, or <code>IllegalArgumentException</code> will be
293
* @param bean Bean whose property is to be extracted
294
* @param name <code>propertyname(index)</code> of the property value
297
* @exception IllegalAccessException if the caller does not have
298
* access to the property accessor method
299
* @exception InvocationTargetException if the property accessor method
300
* throws an exception
301
* @exception NoSuchMethodException if an accessor method for this
302
* propety cannot be found
304
public static String getMappedProperty(Object bean, String name)
305
throws IllegalAccessException, InvocationTargetException,
306
NoSuchMethodException {
308
Object value = PropertyUtils.getMappedProperty(bean, name);
309
return (ConvertUtils.convert(value));
315
* Return the value of the specified mapped property of the specified
316
* bean, as a String. The key is specified as a method parameter and
317
* must *not* be included in the property name expression
319
* @param bean Bean whose property is to be extracted
320
* @param name Simple property name of the property value to be extracted
321
* @param key Lookup key of the property value to be extracted
323
* @exception IllegalAccessException if the caller does not have
324
* access to the property accessor method
325
* @exception InvocationTargetException if the property accessor method
326
* throws an exception
327
* @exception NoSuchMethodException if an accessor method for this
328
* propety cannot be found
330
public static String getMappedProperty(Object bean,
331
String name, String key)
332
throws IllegalAccessException, InvocationTargetException,
333
NoSuchMethodException {
335
Object value = PropertyUtils.getMappedProperty(bean, name, key);
336
return (ConvertUtils.convert(value));
342
* Return the value of the (possibly nested) property of the specified
343
* name, for the specified bean, as a String.
345
* @param bean Bean whose property is to be extracted
346
* @param name Possibly nested name of the property to be extracted
348
* @exception IllegalAccessException if the caller does not have
349
* access to the property accessor method
350
* @exception IllegalArgumentException if a nested reference to a
351
* property returns null
352
* @exception InvocationTargetException if the property accessor method
353
* throws an exception
354
* @exception NoSuchMethodException if an accessor method for this
355
* propety cannot be found
357
public static String getNestedProperty(Object bean, String name)
358
throws IllegalAccessException, InvocationTargetException,
359
NoSuchMethodException {
361
Object value = PropertyUtils.getNestedProperty(bean, name);
362
return (ConvertUtils.convert(value));
368
* Return the value of the specified property of the specified bean,
369
* no matter which property reference format is used, as a String.
371
* @param bean Bean whose property is to be extracted
372
* @param name Possibly indexed and/or nested name of the property
375
* @exception IllegalAccessException if the caller does not have
376
* access to the property accessor method
377
* @exception InvocationTargetException if the property accessor method
378
* throws an exception
379
* @exception NoSuchMethodException if an accessor method for this
380
* propety cannot be found
382
public static String getProperty(Object bean, String name)
383
throws IllegalAccessException, InvocationTargetException,
384
NoSuchMethodException {
386
return (getNestedProperty(bean, name));
392
* Return the value of the specified simple property of the specified
393
* bean, converted to a String.
395
* @param bean Bean whose property is to be extracted
396
* @param name Name of the property to be extracted
398
* @exception IllegalAccessException if the caller does not have
399
* access to the property accessor method
400
* @exception InvocationTargetException if the property accessor method
401
* throws an exception
402
* @exception NoSuchMethodException if an accessor method for this
403
* propety cannot be found
405
public static String getSimpleProperty(Object bean, String name)
406
throws IllegalAccessException, InvocationTargetException,
407
NoSuchMethodException {
409
Object value = PropertyUtils.getSimpleProperty(bean, name);
410
return (ConvertUtils.convert(value));
416
* Populate the JavaBeans properties of the specified bean, based on
417
* the specified name/value pairs. This method uses Java reflection APIs
418
* to identify corresponding "property setter" method names, and deals
419
* with setter arguments of type <code>String</code>, <code>boolean</code>,
420
* <code>int</code>, <code>long</code>, <code>float</code>, and
421
* <code>double</code>. In addition, array setters for these types (or the
422
* corresponding primitive types) can also be identified.
424
* The particular setter method to be called for each property is
425
* determined using the usual JavaBeans introspection mechanisms. Thus,
426
* you may identify custom setter methods using a BeanInfo class that is
427
* associated with the class of the bean itself. If no such BeanInfo
428
* class is available, the standard method name conversion ("set" plus
429
* the capitalized name of the property in question) is used.
431
* <strong>NOTE</strong>: It is contrary to the JavaBeans Specification
432
* to have more than one setter method (with different argument
433
* signatures) for the same property.
435
* @param bean JavaBean whose properties are being populated
436
* @param properties Map keyed by property name, with the
437
* corresponding (String or String[]) value(s) to be set
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
444
public static void populate(Object bean, Map properties)
445
throws IllegalAccessException, InvocationTargetException {
447
if ((bean == null) || (properties == null))
452
System.out.println("BeanUtils.populate(" + bean + ", " +
456
// Loop through the property name/value pairs to be set
457
Iterator names = properties.keySet().iterator();
458
while (names.hasNext()) {
460
// Identify the property name and value(s) to be assigned
461
String name = (String) names.next();
464
Object value = properties.get(name); // String or String[]
468
System.out.println(" name='" + name + "', value.class='" +
469
(value == null ? "NONE" :
470
value.getClass().getName()) + "'");
473
// Get the property descriptor of the requested property (if any)
474
PropertyDescriptor descriptor = null;
476
descriptor = PropertyUtils.getPropertyDescriptor(bean, name);
477
} catch (Throwable t) {
480
System.out.println(" getPropertyDescriptor: " + t);
484
if (descriptor == null) {
487
System.out.println(" No such property, skipping");
493
System.out.println(" Property descriptor is '" +
497
// Identify the relevant setter method (if there is one)
498
Method setter = null;
499
if (descriptor instanceof IndexedPropertyDescriptor)
500
setter = ((IndexedPropertyDescriptor) descriptor).
501
getIndexedWriteMethod();
502
else if (descriptor instanceof MappedPropertyDescriptor)
503
setter =((MappedPropertyDescriptor) descriptor).getMappedWriteMethod();
506
setter = descriptor.getWriteMethod();
507
if (setter == null) {
510
System.out.println(" No setter method, skipping");
514
Class parameterTypes[] = setter.getParameterTypes();
517
System.out.println(" Setter method is '" +
518
setter.getName() + "(" +
519
parameterTypes[0].getName() +
520
(parameterTypes.length > 1 ?
521
", " + parameterTypes[1].getName() : "" )
524
Class parameterType = parameterTypes[0];
525
if (parameterTypes.length > 1)
526
parameterType = parameterTypes[1]; // Indexed or mapped setter
528
// Convert the parameter value as required for this setter method
529
Object parameters[] = new Object[1];
530
if (parameterTypes[0].isArray()) {
531
if (value instanceof String) {
532
String values[] = new String[1];
533
values[0] = (String) value;
534
parameters[0] = ConvertUtils.convert((String[]) values,
536
} else if (value instanceof String[]) {
537
parameters[0] = ConvertUtils.convert((String[]) value,
540
parameters[0] = value;
543
if (value instanceof String) {
544
parameters[0] = ConvertUtils.convert((String) value,
546
} else if (value instanceof String[]) {
547
parameters[0] = ConvertUtils.convert(((String[]) value)[0],
550
parameters[0] = value;
554
// Invoke the setter method
557
System.out.println(" Setting to " +
558
(parameters[0] == null ? "NULL" :
559
"'" + parameters[0] + "'"));
562
PropertyUtils.setProperty(bean, name, parameters[0]);
563
} catch (NoSuchMethodException e) {
566
System.out.println(" CANNOT HAPPEN: " + e);
567
e.printStackTrace(System.out);
576
System.out.println("============================================");