~ubuntu-branches/ubuntu/trusty/commons-beanutils/trusty

« back to all changes in this revision

Viewing changes to src/java/org/apache/commons/beanutils/PropertyUtils.java

  • Committer: Bazaar Package Importer
  • Author(s): Takashi Okamoto
  • Date: 2002-02-16 23:17:39 UTC
  • Revision ID: james.westby@ubuntu.com-20020216231739-eha35b05fam940p6
Tags: upstream-1.2
Import upstream version 1.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
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 $
 
3
 * $Revision: 1.15 $
 
4
 * $Date: 2001/12/15 19:19:24 $
 
5
 *
 
6
 * ====================================================================
 
7
 *
 
8
 * The Apache Software License, Version 1.1
 
9
 *
 
10
 * Copyright (c) 1999-2001 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, 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.
 
31
 *
 
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.
 
36
 *
 
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.
 
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
 
 
63
package org.apache.commons.beanutils;
 
64
 
 
65
 
 
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;
 
77
import java.util.Map;
 
78
import org.apache.commons.collections.FastHashMap;
 
79
 
 
80
 
 
81
/**
 
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.
 
86
 * <p>
 
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.
 
93
 * <p>
 
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:
 
97
 * <ul>
 
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
 
122
 *     supported.</li>
 
123
 * </ul>
 
124
 *
 
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 $
 
132
 */
 
133
 
 
134
public class PropertyUtils {
 
135
 
 
136
 
 
137
    // ----------------------------------------------------- Manifest Constants
 
138
 
 
139
 
 
140
    /**
 
141
     * The delimiter that preceeds the zero-relative subscript for an
 
142
     * indexed reference.
 
143
     */
 
144
    public static final char INDEXED_DELIM = '[';
 
145
 
 
146
 
 
147
    /**
 
148
     * The delimiter that follows the zero-relative subscript for an
 
149
     * indexed reference.
 
150
     */
 
151
    public static final char INDEXED_DELIM2 = ']';
 
152
 
 
153
 
 
154
    /**
 
155
     * The delimiter that preceeds the key of a mapped property.
 
156
     */
 
157
    public static final char MAPPED_DELIM = '(';
 
158
 
 
159
 
 
160
    /**
 
161
     * The delimiter that follows the key of a mapped property.
 
162
     */
 
163
    public static final char MAPPED_DELIM2 = ')';
 
164
 
 
165
 
 
166
    /**
 
167
     * The delimiter that separates the components of a nested reference.
 
168
     */
 
169
    public static final char NESTED_DELIM = '.';
 
170
 
 
171
 
 
172
    // ------------------------------------------------------- Static Variables
 
173
 
 
174
 
 
175
    /**
 
176
     * The debugging detail level for this component.
 
177
     */
 
178
    private static int debug = 0;
 
179
 
 
180
    public static int getDebug() {
 
181
        return (debug);
 
182
    }
 
183
 
 
184
    public static void setDebug(int newDebug) {
 
185
        debug = newDebug;
 
186
    }
 
187
 
 
188
 
 
189
    /**
 
190
     * The cache of PropertyDescriptor arrays for beans we have already
 
191
     * introspected, keyed by the java.lang.Class of this object.
 
192
     */
 
193
    private static FastHashMap descriptorsCache = null;
 
194
    private static FastHashMap mappedDescriptorsCache = null;
 
195
    static {
 
196
        descriptorsCache = new FastHashMap();
 
197
        descriptorsCache.setFast(true);
 
198
        mappedDescriptorsCache = new FastHashMap();
 
199
        mappedDescriptorsCache.setFast(true);
 
200
    }
 
201
 
 
202
 
 
203
    // --------------------------------------------------------- Public Methods
 
204
 
 
205
 
 
206
    /**
 
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.
 
210
     */
 
211
    public static void clearDescriptors() {
 
212
 
 
213
        descriptorsCache.clear();
 
214
        mappedDescriptorsCache.clear();
 
215
        Introspector.flushCaches();
 
216
 
 
217
    }
 
218
 
 
219
 
 
220
    /**
 
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.
 
228
     *
 
229
     * @param dest Destination bean whose properties are modified
 
230
     * @param orig Origin bean whose properties are retrieved
 
231
     *
 
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
 
240
     */
 
241
    public static void copyProperties(Object dest, Object orig)
 
242
        throws IllegalAccessException, InvocationTargetException,
 
243
               NoSuchMethodException {
 
244
 
 
245
        if (dest == null)
 
246
            throw new IllegalArgumentException
 
247
                ("No destination bean specified");
 
248
        if (orig == null)
 
249
            throw new IllegalArgumentException("No origin bean specified");
 
250
 
 
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);
 
256
                try {
 
257
                    setSimpleProperty(dest, name, value);
 
258
                } catch (NoSuchMethodException e) {
 
259
                    ;   // Skip non-matching property
 
260
                }
 
261
            }
 
262
        }
 
263
 
 
264
    }
 
265
 
 
266
 
 
267
    /**
 
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).
 
272
     *
 
273
     * @param bean Bean whose properties are to be extracted
 
274
     *
 
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
 
282
     */
 
283
    // FIXME - does not account for mapped properties
 
284
    public static Map describe(Object bean)
 
285
        throws IllegalAccessException, InvocationTargetException,
 
286
               NoSuchMethodException {
 
287
 
 
288
        if (bean == null)
 
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));
 
297
        }
 
298
        return (description);
 
299
 
 
300
    }
 
301
 
 
302
 
 
303
    /**
 
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
 
308
     * thrown.
 
309
     *
 
310
     * @param bean Bean whose property is to be extracted
 
311
     * @param name <code>propertyname[index]</code> of the property value
 
312
     *  to be extracted
 
313
     *
 
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
 
324
     */
 
325
    public static Object getIndexedProperty(Object bean, String name)
 
326
        throws IllegalAccessException, InvocationTargetException,
 
327
               NoSuchMethodException {
 
328
 
 
329
        if (bean == null)
 
330
            throw new IllegalArgumentException("No bean specified");
 
331
        if (name == null)
 
332
            throw new IllegalArgumentException("No name specified");
 
333
 
 
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 '" +
 
339
                                               name + "'");
 
340
        int index = -1;
 
341
        try {
 
342
            String subscript = name.substring(delim + 1, delim2);
 
343
            index = Integer.parseInt(subscript);
 
344
        } catch (NumberFormatException e) {
 
345
            throw new IllegalArgumentException("Invalid indexed property '" +
 
346
                                               name + "'");
 
347
        }
 
348
        name = name.substring(0, delim);
 
349
 
 
350
        // Request the specified indexed property value
 
351
        return (getIndexedProperty(bean, name, index));
 
352
 
 
353
    }
 
354
 
 
355
 
 
356
    /**
 
357
     * Return the value of the specified indexed property of the specified
 
358
     * bean, with no type conversions.
 
359
     *
 
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
 
363
     *
 
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
 
374
     */
 
375
    public static Object getIndexedProperty(Object bean,
 
376
                                            String name, int index)
 
377
        throws IllegalAccessException, InvocationTargetException,
 
378
               NoSuchMethodException {
 
379
 
 
380
        if (bean == null)
 
381
            throw new IllegalArgumentException("No bean specified");
 
382
        if (name == null)
 
383
            throw new IllegalArgumentException("No name specified");
 
384
 
 
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 '" +
 
390
                                            name + "'");
 
391
 
 
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);
 
399
                try {
 
400
                    return (readMethod.invoke(bean, subscript));
 
401
                } catch (InvocationTargetException e) {
 
402
                    if (e.getTargetException() instanceof
 
403
                        ArrayIndexOutOfBoundsException)
 
404
                        throw (ArrayIndexOutOfBoundsException)
 
405
                            e.getTargetException();
 
406
                    else
 
407
                        throw e;
 
408
                }
 
409
            }
 
410
        }
 
411
 
 
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");
 
417
 
 
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 +
 
422
                                               "' is not indexed");
 
423
        return (Array.get(value, index));
 
424
 
 
425
    }
 
426
 
 
427
 
 
428
    /**
 
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
 
433
     * thrown.
 
434
     *
 
435
     * @param bean Bean whose property is to be extracted
 
436
     * @param name <code>propertyname(key)</code> of the property value
 
437
     *  to be extracted
 
438
     *
 
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
 
445
     */
 
446
    public static Object getMappedProperty(Object bean, String name)
 
447
        throws IllegalAccessException, InvocationTargetException,
 
448
               NoSuchMethodException {
 
449
 
 
450
        if (bean == null)
 
451
            throw new IllegalArgumentException("No bean specified");
 
452
        if (name == null)
 
453
            throw new IllegalArgumentException("No name specified");
 
454
 
 
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);
 
464
 
 
465
        // Request the specified indexed property value
 
466
        return (getMappedProperty(bean, name, key));
 
467
 
 
468
    }
 
469
 
 
470
 
 
471
    /**
 
472
     * Return the value of the specified mapped property of the specified
 
473
     * bean, with no type conversions.
 
474
     *
 
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
 
478
     *
 
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
 
485
     */
 
486
    public static Object getMappedProperty(Object bean,
 
487
                                            String name, String key)
 
488
        throws IllegalAccessException, InvocationTargetException,
 
489
               NoSuchMethodException {
 
490
 
 
491
        if (bean == null)
 
492
            throw new IllegalArgumentException("No bean specified");
 
493
        if (name == null)
 
494
            throw new IllegalArgumentException("No name specified");
 
495
        if (key == null)
 
496
            throw new IllegalArgumentException("No key specified");
 
497
 
 
498
        Object result = null;
 
499
 
 
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 '" +
 
504
                                            name + "'");
 
505
 
 
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];
 
512
                keyArray[0] = key;
 
513
                result = readMethod.invoke(bean, keyArray);
 
514
            } else {
 
515
                throw new NoSuchMethodException("Property '" + name +
 
516
                                "' has no mapped getter method");
 
517
            }
 
518
        }
 
519
        return result;
 
520
 
 
521
    }
 
522
 
 
523
 
 
524
    /**
 
525
     * Return the mapped property descriptors for this bean.
 
526
     *
 
527
     * @param bean Bean to be introspected
 
528
     */
 
529
    public static FastHashMap getMappedPropertyDescriptors(Object bean) {
 
530
 
 
531
        if (bean == null)
 
532
            return null;
 
533
 
 
534
        // Look up any cached descriptors for this bean class
 
535
        Class beanClass = bean.getClass();
 
536
        return (FastHashMap) mappedDescriptorsCache.get(beanClass);
 
537
 
 
538
    }
 
539
 
 
540
 
 
541
    /**
 
542
     * Return the value of the (possibly nested) property of the specified
 
543
     * name, for the specified bean, with no type conversions.
 
544
     *
 
545
     * @param bean Bean whose property is to be extracted
 
546
     * @param name Possibly nested name of the property to be extracted
 
547
     *
 
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
 
558
     */
 
559
    public static Object getNestedProperty(Object bean, String name)
 
560
        throws IllegalAccessException, InvocationTargetException,
 
561
               NoSuchMethodException {
 
562
 
 
563
        if (bean == null)
 
564
            throw new IllegalArgumentException("No bean specified");
 
565
        if (name == null)
 
566
            throw new IllegalArgumentException("No name specified");
 
567
 
 
568
        int indexOfINDEXED_DELIM = -1;
 
569
        int indexOfMAPPED_DELIM = -1;
 
570
        while (true) {
 
571
            /*
 
572
            int delim = name.indexOf(NESTED_DELIM);
 
573
            if (delim < 0)
 
574
                break;
 
575
            String next = name.substring(0, delim);
 
576
            if (next.indexOf(INDEXED_DELIM) >= 0)
 
577
                bean = getIndexedProperty(bean, next);
 
578
            else
 
579
                bean = getSimpleProperty(bean, next);
 
580
            if (bean == null)
 
581
                throw new IllegalArgumentException
 
582
                    ("Null property value for '" +
 
583
                     name.substring(0, delim) + "'");
 
584
            name = name.substring(delim + 1);
 
585
            */
 
586
            int period = name.indexOf(NESTED_DELIM);
 
587
            if (period < 0)
 
588
                break;
 
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);
 
596
            else {
 
597
                if (indexOfINDEXED_DELIM >= 0)
 
598
                    bean = getIndexedProperty(bean, next);
 
599
                else
 
600
                    bean = getSimpleProperty(bean, next);
 
601
            }
 
602
            if (bean == null)
 
603
                throw new IllegalArgumentException
 
604
                    ("Null property value for '" +
 
605
                     name.substring(0, period) + "'");
 
606
            name = name.substring(period + 1);
 
607
        }
 
608
 
 
609
        /*
 
610
        if (name.indexOf(INDEXED_DELIM) >= 0)
 
611
            return (getIndexedProperty(bean, name));
 
612
        else
 
613
            return (getSimpleProperty(bean, name));
 
614
        */
 
615
        indexOfINDEXED_DELIM = name.indexOf(INDEXED_DELIM);
 
616
        indexOfMAPPED_DELIM = name.indexOf(MAPPED_DELIM);
 
617
 
 
618
        if (indexOfMAPPED_DELIM >= 0 && (indexOfINDEXED_DELIM <0 || indexOfMAPPED_DELIM < indexOfINDEXED_DELIM))
 
619
            bean = getMappedProperty(bean, name);
 
620
        else {
 
621
            if (indexOfINDEXED_DELIM >= 0)
 
622
                bean = getIndexedProperty(bean, name);
 
623
            else
 
624
                bean = getSimpleProperty(bean, name);
 
625
        }
 
626
        return bean;
 
627
 
 
628
    }
 
629
 
 
630
 
 
631
    /**
 
632
     * Return the value of the specified property of the specified bean,
 
633
     * no matter which property reference format is used, with no
 
634
     * type conversions.
 
635
     *
 
636
     * @param bean Bean whose property is to be extracted
 
637
     * @param name Possibly indexed and/or nested name of the property
 
638
     *  to be extracted
 
639
     *
 
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
 
648
     */
 
649
    public static Object getProperty(Object bean, String name)
 
650
        throws IllegalAccessException, InvocationTargetException,
 
651
               NoSuchMethodException {
 
652
 
 
653
        return (getNestedProperty(bean, name));
 
654
 
 
655
    }
 
656
 
 
657
 
 
658
    /**
 
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.
 
665
     *
 
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
 
669
     *
 
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
 
680
     */
 
681
    public static PropertyDescriptor getPropertyDescriptor(Object bean,
 
682
                                                           String name)
 
683
        throws IllegalAccessException, InvocationTargetException,
 
684
               NoSuchMethodException {
 
685
 
 
686
        if (bean == null)
 
687
            throw new IllegalArgumentException("No bean specified");
 
688
        if (name == null)
 
689
            throw new IllegalArgumentException("No name specified");
 
690
 
 
691
        // Resolve nested references
 
692
        while (true) {
 
693
            int period = name.indexOf(NESTED_DELIM);
 
694
            if (period < 0)
 
695
                break;
 
696
            String next = name.substring(0, period);
 
697
            /*
 
698
            if (next.indexOf(INDEXED_DELIM) >= 0)
 
699
                bean = getIndexedProperty(bean, next);
 
700
            else
 
701
                bean = getSimpleProperty(bean, next);
 
702
            if (bean == null)
 
703
                throw new IllegalArgumentException
 
704
                    ("Null property value for '" +
 
705
                     name.substring(0, period) + "'");
 
706
            name = name.substring(period + 1);
 
707
            */
 
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);
 
712
            else {
 
713
                if (indexOfINDEXED_DELIM >= 0)
 
714
                    bean = getIndexedProperty(bean, next);
 
715
                else
 
716
                    bean = getSimpleProperty(bean, next);
 
717
                }
 
718
            if (bean == null)
 
719
                throw new IllegalArgumentException
 
720
                    ("Null property value for '" +
 
721
                     name.substring(0, period) + "'");
 
722
            name = name.substring(period + 1);
 
723
        }
 
724
 
 
725
        // Remove any subscript from the final name value
 
726
        int left = name.indexOf(INDEXED_DELIM);
 
727
        if (left >= 0)
 
728
            name = name.substring(0, left);
 
729
        left = name.indexOf(MAPPED_DELIM);
 
730
        if (left >= 0)
 
731
            name = name.substring(0, left);
 
732
 
 
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))
 
736
            return (null);
 
737
 
 
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]);
 
743
            }
 
744
        }
 
745
 
 
746
        PropertyDescriptor result = null;
 
747
        FastHashMap mappedDescriptors =
 
748
            getMappedPropertyDescriptors(bean);
 
749
        if (mappedDescriptors != null) {
 
750
            result = (PropertyDescriptor) mappedDescriptors.get(name);
 
751
        }
 
752
        if (result==null) {
 
753
            // not found, try to create it
 
754
            try {
 
755
                result =
 
756
                    new MappedPropertyDescriptor(name, bean.getClass());
 
757
            } catch (IntrospectionException ie) {
 
758
            }
 
759
        }
 
760
        if (result!=null) {
 
761
            if (mappedDescriptors==null) {
 
762
                mappedDescriptors = new FastHashMap();
 
763
                mappedDescriptors.setFast(true);
 
764
                mappedDescriptorsCache.put
 
765
                    (bean.getClass(), mappedDescriptors);
 
766
            }
 
767
            mappedDescriptors.put(name, result);
 
768
        }
 
769
        return result;
 
770
 
 
771
    }
 
772
 
 
773
 
 
774
    /**
 
775
     * Retrieve the property descriptors for the specified bean, introspecting
 
776
     * and caching them the first time a particular bean class is encountered.
 
777
     *
 
778
     * @param bean Bean for which property descriptors are requested
 
779
     *
 
780
     * @exception IllegalArgumentException if <code>bean</code> is null
 
781
     */
 
782
    public static PropertyDescriptor[] getPropertyDescriptors(Object bean) {
 
783
 
 
784
        if (bean == null)
 
785
            throw new IllegalArgumentException("No bean specified");
 
786
 
 
787
        // Look up any cached descriptors for this bean class
 
788
        Class beanClass = bean.getClass();
 
789
        PropertyDescriptor descriptors[] = null;
 
790
        descriptors =
 
791
            (PropertyDescriptor[]) descriptorsCache.get(beanClass);
 
792
        if (descriptors != null)
 
793
            return (descriptors);
 
794
 
 
795
        // Introspect the bean and cache the generated descriptors
 
796
        BeanInfo beanInfo = null;
 
797
        try {
 
798
            beanInfo = Introspector.getBeanInfo(bean.getClass());
 
799
        } catch (IntrospectionException e) {
 
800
            return (new PropertyDescriptor[0]);
 
801
        }
 
802
        descriptors = beanInfo.getPropertyDescriptors();
 
803
        if (descriptors == null)
 
804
            descriptors = new PropertyDescriptor[0];
 
805
        descriptorsCache.put(beanClass, descriptors);
 
806
        return (descriptors);
 
807
 
 
808
    }
 
809
 
 
810
 
 
811
    /**
 
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.
 
817
     * <p>
 
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.
 
822
     *
 
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
 
826
     *
 
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
 
837
     */
 
838
    public static Class getPropertyEditorClass(Object bean, String name)
 
839
        throws IllegalAccessException, InvocationTargetException,
 
840
               NoSuchMethodException {
 
841
 
 
842
        if (bean == null)
 
843
            throw new IllegalArgumentException("No bean specified");
 
844
        if (name == null)
 
845
            throw new IllegalArgumentException("No name specified");
 
846
 
 
847
        PropertyDescriptor descriptor =
 
848
            getPropertyDescriptor(bean, name);
 
849
        if (descriptor != null)
 
850
            return (descriptor.getPropertyEditorClass());
 
851
        else
 
852
            return (null);
 
853
 
 
854
    }
 
855
 
 
856
 
 
857
    /**
 
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.
 
865
     *
 
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
 
869
     *
 
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
 
880
     */
 
881
    public static Class getPropertyType(Object bean, String name)
 
882
        throws IllegalAccessException, InvocationTargetException,
 
883
               NoSuchMethodException {
 
884
 
 
885
        if (bean == null)
 
886
            throw new IllegalArgumentException("No bean specified");
 
887
        if (name == null)
 
888
            throw new IllegalArgumentException("No name specified");
 
889
 
 
890
        PropertyDescriptor descriptor =
 
891
            getPropertyDescriptor(bean, name);
 
892
        if (descriptor == null)
 
893
            return (null);
 
894
        else if (descriptor instanceof IndexedPropertyDescriptor)
 
895
            return (((IndexedPropertyDescriptor) descriptor).
 
896
                    getIndexedPropertyType());
 
897
        else
 
898
            return (descriptor.getPropertyType());
 
899
 
 
900
    }
 
901
 
 
902
 
 
903
    /**
 
904
     * Return an accessible property getter method for this property,
 
905
     * if there is one; otherwise return <code>null</code>.
 
906
     *
 
907
     * @param descriptor Property descriptor to return a getter for
 
908
     */
 
909
    public static Method getReadMethod(PropertyDescriptor descriptor) {
 
910
 
 
911
        return (MethodUtils.getAccessibleMethod(descriptor.getReadMethod()));
 
912
 
 
913
    }
 
914
 
 
915
 
 
916
    /**
 
917
     * Return the value of the specified simple property of the specified
 
918
     * bean, with no type conversions.
 
919
     *
 
920
     * @param bean Bean whose property is to be extracted
 
921
     * @param name Name of the property to be extracted
 
922
     *
 
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
 
933
     */
 
934
    public static Object getSimpleProperty(Object bean, String name)
 
935
        throws IllegalAccessException, InvocationTargetException,
 
936
               NoSuchMethodException {
 
937
 
 
938
        if (bean == null)
 
939
            throw new IllegalArgumentException("No bean specified");
 
940
        if (name == null)
 
941
            throw new IllegalArgumentException("No name specified");
 
942
 
 
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");
 
953
 
 
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 '" +
 
959
                                            name + "'");
 
960
        Method readMethod = getReadMethod(descriptor);
 
961
        if (readMethod == null)
 
962
            throw new NoSuchMethodException("Property '" + name +
 
963
                                            "' has no getter method");
 
964
 
 
965
        // Call the property getter and return the value
 
966
        Object value = readMethod.invoke(bean, new Object[0]);
 
967
        return (value);
 
968
 
 
969
    }
 
970
 
 
971
 
 
972
    /**
 
973
     * Return an accessible property setter method for this property,
 
974
     * if there is one; otherwise return <code>null</code>.
 
975
     *
 
976
     * @param descriptor Property descriptor to return a setter for
 
977
     */
 
978
    public static Method getWriteMethod(PropertyDescriptor descriptor) {
 
979
 
 
980
        return (MethodUtils.getAccessibleMethod(descriptor.getWriteMethod()));
 
981
 
 
982
    }
 
983
 
 
984
 
 
985
    /**
 
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
 
990
     * thrown.
 
991
     *
 
992
     * @param bean Bean whose property is to be modified
 
993
     * @param name <code>propertyname[index]</code> of the property value
 
994
     *  to be modified
 
995
     * @param value Value to which the specified property element
 
996
     *  should be set
 
997
     *
 
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
 
1008
     */
 
1009
    public static void setIndexedProperty(Object bean, String name,
 
1010
                                          Object value)
 
1011
        throws IllegalAccessException, InvocationTargetException,
 
1012
               NoSuchMethodException {
 
1013
 
 
1014
        if (bean == null)
 
1015
            throw new IllegalArgumentException("No bean specified");
 
1016
        if (name == null)
 
1017
            throw new IllegalArgumentException("No name specified");
 
1018
 
 
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 '" +
 
1024
                                               name + "'");
 
1025
        int index = -1;
 
1026
        try {
 
1027
            String subscript = name.substring(delim + 1, delim2);
 
1028
            index = Integer.parseInt(subscript);
 
1029
        } catch (NumberFormatException e) {
 
1030
            throw new IllegalArgumentException("Invalid indexed property '" +
 
1031
                                               name + "'");
 
1032
        }
 
1033
        name = name.substring(0, delim);
 
1034
 
 
1035
        // Set the specified indexed property value
 
1036
        setIndexedProperty(bean, name, index, value);
 
1037
 
 
1038
    }
 
1039
 
 
1040
 
 
1041
    /**
 
1042
     * Set the value of the specified indexed property of the specified
 
1043
     * bean, with no type conversions.
 
1044
     *
 
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
 
1049
     *
 
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
 
1060
     */
 
1061
    public static void setIndexedProperty(Object bean, String name,
 
1062
                                          int index, Object value)
 
1063
        throws IllegalAccessException, InvocationTargetException,
 
1064
               NoSuchMethodException {
 
1065
 
 
1066
        if (bean == null)
 
1067
            throw new IllegalArgumentException("No bean specified");
 
1068
        if (name == null)
 
1069
            throw new IllegalArgumentException("No name specified");
 
1070
 
 
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 '" +
 
1076
                                            name + "'");
 
1077
 
 
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;
 
1086
                try {
 
1087
                    writeMethod.invoke(bean, subscript);
 
1088
                } catch (InvocationTargetException e) {
 
1089
                    if (e.getTargetException() instanceof
 
1090
                        ArrayIndexOutOfBoundsException)
 
1091
                        throw (ArrayIndexOutOfBoundsException)
 
1092
                            e.getTargetException();
 
1093
                    else
 
1094
                        throw e;
 
1095
                }
 
1096
                return;
 
1097
            }
 
1098
        }
 
1099
 
 
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");
 
1105
 
 
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");
 
1111
 
 
1112
        // Modify the specified value
 
1113
        Array.set(array, index, value);
 
1114
 
 
1115
    }
 
1116
 
 
1117
 
 
1118
    /**
 
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
 
1123
     * thrown.
 
1124
     *
 
1125
     * @param bean Bean whose property is to be set
 
1126
     * @param name <code>propertyname(key)</code> of the property value
 
1127
     *  to be set
 
1128
     * @param value The property value to be set
 
1129
     *
 
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
 
1136
     */
 
1137
    public static void setMappedProperty(Object bean, String name,
 
1138
                                              Object value)
 
1139
        throws IllegalAccessException, InvocationTargetException,
 
1140
               NoSuchMethodException {
 
1141
 
 
1142
        if (bean == null)
 
1143
            throw new IllegalArgumentException("No bean specified");
 
1144
        if (name == null)
 
1145
            throw new IllegalArgumentException("No name specified");
 
1146
 
 
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);
 
1156
        
 
1157
        // Request the specified indexed property value
 
1158
        setMappedProperty(bean, name, key, value);
 
1159
 
 
1160
    }
 
1161
 
 
1162
 
 
1163
    /**
 
1164
     * Set the value of the specified mapped property of the specified
 
1165
     * bean, with no type conversions.
 
1166
     *
 
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
 
1171
     *
 
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
 
1178
     */
 
1179
    public static void setMappedProperty(Object bean, String name,
 
1180
                                              String key, Object value)
 
1181
        throws IllegalAccessException, InvocationTargetException,
 
1182
               NoSuchMethodException {
 
1183
 
 
1184
        if (bean == null)
 
1185
            throw new IllegalArgumentException("No bean specified");
 
1186
        if (name == null)
 
1187
            throw new IllegalArgumentException("No name specified");
 
1188
        if (key == null)
 
1189
            throw new IllegalArgumentException("No key specified");
 
1190
 
 
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 '" +
 
1196
                                            name + "'");
 
1197
 
 
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];
 
1205
                params[0] = key;
 
1206
                params[1] = value;
 
1207
                mappedWriteMethod.invoke(bean, params);
 
1208
            } else {
 
1209
                throw new NoSuchMethodException
 
1210
                    ("Property '" + name +
 
1211
                     "' has no mapped setter method");
 
1212
            }
 
1213
        }
 
1214
 
 
1215
    }
 
1216
 
 
1217
 
 
1218
    /**
 
1219
     * Set the value of the (possibly nested) property of the specified
 
1220
     * name, for the specified bean, with no type conversions.
 
1221
     *
 
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
 
1225
     *
 
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
 
1236
     */
 
1237
    public static void setNestedProperty(Object bean,
 
1238
                                         String name, Object value)
 
1239
        throws IllegalAccessException, InvocationTargetException,
 
1240
               NoSuchMethodException {
 
1241
 
 
1242
        if (bean == null)
 
1243
            throw new IllegalArgumentException("No bean specified");
 
1244
        if (name == null)
 
1245
            throw new IllegalArgumentException("No name specified");
 
1246
 
 
1247
        int indexOfINDEXED_DELIM = -1;
 
1248
        int indexOfMAPPED_DELIM = -1;
 
1249
        while (true) {
 
1250
            int delim = name.indexOf(NESTED_DELIM);
 
1251
            if (delim < 0)
 
1252
                break;
 
1253
            String next = name.substring(0, delim);
 
1254
            /*
 
1255
            if (next.indexOf(INDEXED_DELIM) >= 0)
 
1256
                bean = getIndexedProperty(bean, next);
 
1257
            else
 
1258
                bean = getSimpleProperty(bean, next);
 
1259
            if (bean == null)
 
1260
                throw new IllegalArgumentException
 
1261
                    ("Null property value for '" +
 
1262
                     name.substring(0, delim) + "'");
 
1263
            name = name.substring(delim + 1);
 
1264
            */
 
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);
 
1269
            else {
 
1270
                if (indexOfINDEXED_DELIM >= 0)
 
1271
                    bean = getIndexedProperty(bean, next);
 
1272
                else
 
1273
                    bean = getSimpleProperty(bean, next);
 
1274
            }
 
1275
            if (bean == null)
 
1276
                throw new IllegalArgumentException
 
1277
                    ("Null property value for '" +
 
1278
                     name.substring(0, delim) + "'");
 
1279
            name = name.substring(delim + 1);
 
1280
        }
 
1281
 
 
1282
        /*
 
1283
        if (name.indexOf(INDEXED_DELIM) >= 0)
 
1284
            setIndexedProperty(bean, name, value);
 
1285
        else
 
1286
            setSimpleProperty(bean, name, value);
 
1287
        */
 
1288
        indexOfINDEXED_DELIM = name.indexOf(INDEXED_DELIM);
 
1289
        indexOfMAPPED_DELIM = name.indexOf(MAPPED_DELIM);
 
1290
 
 
1291
        if (indexOfMAPPED_DELIM >= 0 &&
 
1292
            (indexOfINDEXED_DELIM <0 ||
 
1293
             indexOfMAPPED_DELIM < indexOfINDEXED_DELIM))
 
1294
            setMappedProperty(bean, name, value);
 
1295
        else {
 
1296
            if (indexOfINDEXED_DELIM >= 0)
 
1297
                setIndexedProperty(bean, name, value);
 
1298
            else
 
1299
                setSimpleProperty(bean, name, value);
 
1300
        }
 
1301
 
 
1302
    }
 
1303
 
 
1304
 
 
1305
    /**
 
1306
     * Set the value of the specified property of the specified bean,
 
1307
     * no matter which property reference format is used, with no
 
1308
     * type conversions.
 
1309
     *
 
1310
     * @param bean Bean whose property is to be modified
 
1311
     * @param name Possibly indexed and/or nested name of the property
 
1312
     *  to be modified
 
1313
     * @param value Value to which this property is to be set
 
1314
     *
 
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
 
1323
     */
 
1324
    public static void setProperty(Object bean, String name, Object value)
 
1325
        throws IllegalAccessException, InvocationTargetException,
 
1326
               NoSuchMethodException {
 
1327
 
 
1328
        setNestedProperty(bean, name, value);
 
1329
 
 
1330
    }
 
1331
 
 
1332
 
 
1333
    /**
 
1334
     * Set the value of the specified simple property of the specified bean,
 
1335
     * with no type conversions.
 
1336
     *
 
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
 
1340
     *
 
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
 
1346
     *  nested or indexed
 
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
 
1351
     */
 
1352
    public static void setSimpleProperty(Object bean,
 
1353
                                         String name, Object value)
 
1354
        throws IllegalAccessException, InvocationTargetException,
 
1355
               NoSuchMethodException {
 
1356
 
 
1357
        if (bean == null)
 
1358
            throw new IllegalArgumentException("No bean specified");
 
1359
        if (name == null)
 
1360
            throw new IllegalArgumentException("No name specified");
 
1361
 
 
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");
 
1372
 
 
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 '" +
 
1378
                                            name + "'");
 
1379
        Method writeMethod = getWriteMethod(descriptor);
 
1380
        if (writeMethod == null)
 
1381
            throw new NoSuchMethodException("Property '" + name +
 
1382
                                            "' has no setter method");
 
1383
 
 
1384
        // Call the property setter method
 
1385
        Object values[] = new Object[1];
 
1386
        values[0] = value;
 
1387
        writeMethod.invoke(bean, values);
 
1388
 
 
1389
    }
 
1390
 
 
1391
 
 
1392
}