Coverage Report - org.apache.commons.beanutils.PropertyUtilsBean
 
Classes in this File Line Coverage Branch Coverage Complexity
PropertyUtilsBean
81%
554/680
80%
362/450
11.514
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 package org.apache.commons.beanutils;
 19  
 
 20  
 
 21  
 import java.beans.BeanInfo;
 22  
 import java.beans.IndexedPropertyDescriptor;
 23  
 import java.beans.IntrospectionException;
 24  
 import java.beans.Introspector;
 25  
 import java.beans.PropertyDescriptor;
 26  
 import java.lang.reflect.Array;
 27  
 import java.lang.reflect.InvocationTargetException;
 28  
 import java.lang.reflect.Method;
 29  
 import java.util.HashMap;
 30  
 import java.util.Iterator;
 31  
 import java.util.List;
 32  
 import java.util.Map;
 33  
 
 34  
 import org.apache.commons.beanutils.expression.DefaultResolver;
 35  
 import org.apache.commons.beanutils.expression.Resolver;
 36  
 import org.apache.commons.collections.FastHashMap;
 37  
 import org.apache.commons.logging.Log;
 38  
 import org.apache.commons.logging.LogFactory;
 39  
 
 40  
 
 41  
 /**
 42  
  * Utility methods for using Java Reflection APIs to facilitate generic
 43  
  * property getter and setter operations on Java objects.  Much of this
 44  
  * code was originally included in <code>BeanUtils</code>, but has been
 45  
  * separated because of the volume of code involved.
 46  
  * <p>
 47  
  * In general, the objects that are examined and modified using these
 48  
  * methods are expected to conform to the property getter and setter method
 49  
  * naming conventions described in the JavaBeans Specification (Version 1.0.1).
 50  
  * No data type conversions are performed, and there are no usage of any
 51  
  * <code>PropertyEditor</code> classes that have been registered, although
 52  
  * a convenient way to access the registered classes themselves is included.
 53  
  * <p>
 54  
  * For the purposes of this class, five formats for referencing a particular
 55  
  * property value of a bean are defined, with the <i>default</i> layout of an
 56  
  * identifying String in parentheses. However the notation for these formats
 57  
  * and how they are resolved is now (since BeanUtils 1.8.0) controlled by
 58  
  * the configured {@link Resolver} implementation:
 59  
  * <ul>
 60  
  * <li><strong>Simple (<code>name</code>)</strong> - The specified
 61  
  *     <code>name</code> identifies an individual property of a particular
 62  
  *     JavaBean.  The name of the actual getter or setter method to be used
 63  
  *     is determined using standard JavaBeans instrospection, so that (unless
 64  
  *     overridden by a <code>BeanInfo</code> class, a property named "xyz"
 65  
  *     will have a getter method named <code>getXyz()</code> or (for boolean
 66  
  *     properties only) <code>isXyz()</code>, and a setter method named
 67  
  *     <code>setXyz()</code>.</li>
 68  
  * <li><strong>Nested (<code>name1.name2.name3</code>)</strong> The first
 69  
  *     name element is used to select a property getter, as for simple
 70  
  *     references above.  The object returned for this property is then
 71  
  *     consulted, using the same approach, for a property getter for a
 72  
  *     property named <code>name2</code>, and so on.  The property value that
 73  
  *     is ultimately retrieved or modified is the one identified by the
 74  
  *     last name element.</li>
 75  
  * <li><strong>Indexed (<code>name[index]</code>)</strong> - The underlying
 76  
  *     property value is assumed to be an array, or this JavaBean is assumed
 77  
  *     to have indexed property getter and setter methods.  The appropriate
 78  
  *     (zero-relative) entry in the array is selected.  <code>List</code>
 79  
  *     objects are now also supported for read/write.  You simply need to define
 80  
  *     a getter that returns the <code>List</code></li>
 81  
  * <li><strong>Mapped (<code>name(key)</code>)</strong> - The JavaBean
 82  
  *     is assumed to have an property getter and setter methods with an
 83  
  *     additional attribute of type <code>java.lang.String</code>.</li>
 84  
  * <li><strong>Combined (<code>name1.name2[index].name3(key)</code>)</strong> -
 85  
  *     Combining mapped, nested, and indexed references is also
 86  
  *     supported.</li>
 87  
  * </ul>
 88  
  *
 89  
  * @author Craig R. McClanahan
 90  
  * @author Ralph Schaer
 91  
  * @author Chris Audley
 92  
  * @author Rey Francois
 93  
  * @author Gregor Rayman
 94  
  * @author Jan Sorensen
 95  
  * @author Scott Sanders
 96  
  * @author Erik Meade
 97  
  * @version $Revision: 822777 $ $Date: 2009-10-07 11:23:23 -0400 (Wed, 07 Oct 2009) $
 98  
  * @see Resolver
 99  
  * @see PropertyUtils
 100  
  * @since 1.7
 101  
  */
 102  
 
 103  
 public class PropertyUtilsBean {
 104  
 
 105  193
     private Resolver resolver = new DefaultResolver();
 106  
 
 107  
     // --------------------------------------------------------- Class Methods
 108  
 
 109  
     /**
 110  
      * Return the PropertyUtils bean instance.
 111  
      * @return The PropertyUtils bean instance
 112  
      */
 113  
     protected static PropertyUtilsBean getInstance() {
 114  1003
         return BeanUtilsBean.getInstance().getPropertyUtils();
 115  
     }
 116  
 
 117  
     // --------------------------------------------------------- Variables
 118  
 
 119  
     /**
 120  
      * The cache of PropertyDescriptor arrays for beans we have already
 121  
      * introspected, keyed by the java.lang.Class of this object.
 122  
      */
 123  193
     private WeakFastHashMap descriptorsCache = null;
 124  193
     private WeakFastHashMap mappedDescriptorsCache = null;
 125  1
     private static final Class[] EMPTY_CLASS_PARAMETERS = new Class[0];
 126  1
     private static final Class[] LIST_CLASS_PARAMETER = new Class[] {java.util.List.class};
 127  
     
 128  
     /** An empty object array */
 129  1
     private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
 130  
 
 131  
     /** Log instance */
 132  193
     private Log log = LogFactory.getLog(PropertyUtils.class);
 133  
     
 134  
     // ---------------------------------------------------------- Constructors
 135  
     
 136  
     /** Base constructor */
 137  193
     public PropertyUtilsBean() {
 138  193
         descriptorsCache = new WeakFastHashMap();
 139  193
         descriptorsCache.setFast(true);
 140  193
         mappedDescriptorsCache = new WeakFastHashMap();
 141  193
         mappedDescriptorsCache.setFast(true);
 142  193
     }
 143  
 
 144  
 
 145  
     // --------------------------------------------------------- Public Methods
 146  
 
 147  
 
 148  
     /**
 149  
      * Return the configured {@link Resolver} implementation used by BeanUtils.
 150  
      * <p>
 151  
      * The {@link Resolver} handles the <i>property name</i>
 152  
      * expressions and the implementation in use effectively
 153  
      * controls the dialect of the <i>expression language</i>
 154  
      * that BeanUtils recongnises.
 155  
      * <p>
 156  
      * {@link DefaultResolver} is the default implementation used.
 157  
      *
 158  
      * @return resolver The property expression resolver.
 159  
      * @since 1.8.0
 160  
      */
 161  
     public Resolver getResolver() {
 162  474
         return resolver;
 163  
     }
 164  
 
 165  
     /**
 166  
      * Configure the {@link Resolver} implementation used by BeanUtils.
 167  
      * <p>
 168  
      * The {@link Resolver} handles the <i>property name</i>
 169  
      * expressions and the implementation in use effectively
 170  
      * controls the dialect of the <i>expression language</i>
 171  
      * that BeanUtils recongnises.
 172  
      * <p>
 173  
      * {@link DefaultResolver} is the default implementation used.
 174  
      *
 175  
      * @param resolver The property expression resolver.
 176  
      * @since 1.8.0
 177  
      */
 178  
     public void setResolver(Resolver resolver) {
 179  0
         if (resolver == null) {
 180  0
             this.resolver = new DefaultResolver();
 181  
         } else {
 182  0
             this.resolver = resolver;
 183  
         }
 184  0
     }
 185  
 
 186  
     /**
 187  
      * Clear any cached property descriptors information for all classes
 188  
      * loaded by any class loaders.  This is useful in cases where class
 189  
      * loaders are thrown away to implement class reloading.
 190  
      */
 191  
     public void clearDescriptors() {
 192  
 
 193  32
         descriptorsCache.clear();
 194  32
         mappedDescriptorsCache.clear();
 195  32
         Introspector.flushCaches();
 196  
 
 197  32
     }
 198  
 
 199  
 
 200  
     /**
 201  
      * <p>Copy property values from the "origin" bean to the "destination" bean
 202  
      * for all cases where the property names are the same (even though the
 203  
      * actual getter and setter methods might have been customized via
 204  
      * <code>BeanInfo</code> classes).  No conversions are performed on the
 205  
      * actual property values -- it is assumed that the values retrieved from
 206  
      * the origin bean are assignment-compatible with the types expected by
 207  
      * the destination bean.</p>
 208  
      *
 209  
      * <p>If the origin "bean" is actually a <code>Map</code>, it is assumed
 210  
      * to contain String-valued <strong>simple</strong> property names as the keys, pointing
 211  
      * at the corresponding property values that will be set in the destination
 212  
      * bean.<strong>Note</strong> that this method is intended to perform 
 213  
      * a "shallow copy" of the properties and so complex properties 
 214  
      * (for example, nested ones) will not be copied.</p>
 215  
      * 
 216  
      * <p>Note, that this method will not copy a List to a List, or an Object[] 
 217  
      * to an Object[]. It's specifically for copying JavaBean properties. </p>
 218  
      *
 219  
      * @param dest Destination bean whose properties are modified
 220  
      * @param orig Origin bean whose properties are retrieved
 221  
      *
 222  
      * @exception IllegalAccessException if the caller does not have
 223  
      *  access to the property accessor method
 224  
      * @exception IllegalArgumentException if the <code>dest</code> or
 225  
      *  <code>orig</code> argument is null
 226  
      * @exception InvocationTargetException if the property accessor method
 227  
      *  throws an exception
 228  
      * @exception NoSuchMethodException if an accessor method for this
 229  
      *  propety cannot be found
 230  
      */
 231  
     public void copyProperties(Object dest, Object orig)
 232  
             throws IllegalAccessException, InvocationTargetException,
 233  
             NoSuchMethodException {
 234  
 
 235  7
         if (dest == null) {
 236  0
             throw new IllegalArgumentException
 237  
                     ("No destination bean specified");
 238  
         }
 239  7
         if (orig == null) {
 240  0
             throw new IllegalArgumentException("No origin bean specified");
 241  
         }
 242  
 
 243  7
         if (orig instanceof DynaBean) {
 244  2
             DynaProperty[] origDescriptors =
 245  
                 ((DynaBean) orig).getDynaClass().getDynaProperties();
 246  29
             for (int i = 0; i < origDescriptors.length; i++) {
 247  27
                 String name = origDescriptors[i].getName();
 248  27
                 if (isReadable(orig, name) && isWriteable(dest, name)) {
 249  
                     try {
 250  20
                         Object value = ((DynaBean) orig).get(name);
 251  20
                         if (dest instanceof DynaBean) {
 252  20
                             ((DynaBean) dest).set(name, value);
 253  
                         } else {
 254  0
                                 setSimpleProperty(dest, name, value);
 255  
                         }
 256  0
                     } catch (NoSuchMethodException e) {
 257  0
                         if (log.isDebugEnabled()) {
 258  0
                             log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", e);
 259  
                         }
 260  20
                     }
 261  
                 }
 262  
             }
 263  2
         } else if (orig instanceof Map) {
 264  3
             Iterator entries = ((Map) orig).entrySet().iterator();
 265  22
             while (entries.hasNext()) {
 266  19
                 Map.Entry entry = (Map.Entry) entries.next();
 267  19
                 String name = (String)entry.getKey();
 268  19
                 if (isWriteable(dest, name)) {
 269  
                     try {
 270  18
                         if (dest instanceof DynaBean) {
 271  9
                             ((DynaBean) dest).set(name, entry.getValue());
 272  
                         } else {
 273  9
                             setSimpleProperty(dest, name, entry.getValue());
 274  
                         }
 275  0
                     } catch (NoSuchMethodException e) {
 276  0
                         if (log.isDebugEnabled()) {
 277  0
                             log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", e);
 278  
                         }
 279  18
                     }
 280  
                 }
 281  19
             }
 282  3
         } else /* if (orig is a standard JavaBean) */ {
 283  2
             PropertyDescriptor[] origDescriptors =
 284  
                 getPropertyDescriptors(orig);
 285  32
             for (int i = 0; i < origDescriptors.length; i++) {
 286  31
                 String name = origDescriptors[i].getName();
 287  31
                 if (isReadable(orig, name) && isWriteable(dest, name)) {
 288  
                     try {
 289  2
                         Object value = getSimpleProperty(orig, name);
 290  2
                         if (dest instanceof DynaBean) {
 291  0
                             ((DynaBean) dest).set(name, value);
 292  
                         } else {
 293  2
                                 setSimpleProperty(dest, name, value);
 294  
                         }
 295  1
                     } catch (NoSuchMethodException e) {
 296  1
                         if (log.isDebugEnabled()) {
 297  0
                             log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", e);
 298  
                         }
 299  0
                     }
 300  
                 }
 301  
             }
 302  
         }
 303  
 
 304  6
     }
 305  
 
 306  
 
 307  
     /**
 308  
      * <p>Return the entire set of properties for which the specified bean
 309  
      * provides a read method.  This map contains the unconverted property
 310  
      * values for all properties for which a read method is provided
 311  
      * (i.e. where the <code>getReadMethod()</code> returns non-null).</p>
 312  
      *
 313  
      * <p><strong>FIXME</strong> - Does not account for mapped properties.</p>
 314  
      *
 315  
      * @param bean Bean whose properties are to be extracted
 316  
      * @return The set of properties for the bean
 317  
      *
 318  
      * @exception IllegalAccessException if the caller does not have
 319  
      *  access to the property accessor method
 320  
      * @exception IllegalArgumentException if <code>bean</code> is null
 321  
      * @exception InvocationTargetException if the property accessor method
 322  
      *  throws an exception
 323  
      * @exception NoSuchMethodException if an accessor method for this
 324  
      *  propety cannot be found
 325  
      */
 326  
     public Map describe(Object bean)
 327  
             throws IllegalAccessException, InvocationTargetException,
 328  
             NoSuchMethodException {
 329  
 
 330  3
         if (bean == null) {
 331  0
             throw new IllegalArgumentException("No bean specified");
 332  
         }
 333  3
         Map description = new HashMap();
 334  3
         if (bean instanceof DynaBean) {
 335  2
             DynaProperty[] descriptors =
 336  
                 ((DynaBean) bean).getDynaClass().getDynaProperties();
 337  42
             for (int i = 0; i < descriptors.length; i++) {
 338  40
                 String name = descriptors[i].getName();
 339  40
                 description.put(name, getProperty(bean, name));
 340  
             }
 341  2
         } else {
 342  1
             PropertyDescriptor[] descriptors =
 343  
                 getPropertyDescriptors(bean);
 344  30
             for (int i = 0; i < descriptors.length; i++) {
 345  29
                 String name = descriptors[i].getName();
 346  29
                 if (descriptors[i].getReadMethod() != null) {
 347  25
                     description.put(name, getProperty(bean, name));
 348  
                 }
 349  
             }
 350  
         }
 351  3
         return (description);
 352  
 
 353  
     }
 354  
 
 355  
 
 356  
     /**
 357  
      * Return the value of the specified indexed property of the specified
 358  
      * bean, with no type conversions.  The zero-relative index of the
 359  
      * required value must be included (in square brackets) as a suffix to
 360  
      * the property name, or <code>IllegalArgumentException</code> will be
 361  
      * thrown.  In addition to supporting the JavaBeans specification, this
 362  
      * method has been extended to support <code>List</code> objects as well.
 363  
      *
 364  
      * @param bean Bean whose property is to be extracted
 365  
      * @param name <code>propertyname[index]</code> of the property value
 366  
      *  to be extracted
 367  
      * @return the indexed property value
 368  
      *
 369  
      * @exception IndexOutOfBoundsException if the specified index
 370  
      *  is outside the valid range for the underlying array or List
 371  
      * @exception IllegalAccessException if the caller does not have
 372  
      *  access to the property accessor method
 373  
      * @exception IllegalArgumentException if <code>bean</code> or
 374  
      *  <code>name</code> is null
 375  
      * @exception InvocationTargetException if the property accessor method
 376  
      *  throws an exception
 377  
      * @exception NoSuchMethodException if an accessor method for this
 378  
      *  propety cannot be found
 379  
      */
 380  
     public Object getIndexedProperty(Object bean, String name)
 381  
             throws IllegalAccessException, InvocationTargetException,
 382  
             NoSuchMethodException {
 383  
 
 384  153
         if (bean == null) {
 385  4
             throw new IllegalArgumentException("No bean specified");
 386  
         }
 387  149
         if (name == null) {
 388  0
             throw new IllegalArgumentException("No name specified for bean class '" +
 389  
                     bean.getClass() + "'");
 390  
         }
 391  
 
 392  
         // Identify the index of the requested individual property
 393  149
         int index = -1;
 394  
         try {
 395  149
             index = resolver.getIndex(name);
 396  0
         } catch (IllegalArgumentException e) {
 397  0
             throw new IllegalArgumentException("Invalid indexed property '" +
 398  
                     name + "' on bean class '" + bean.getClass() + "' " +
 399  
                     e.getMessage());
 400  149
         }
 401  149
         if (index < 0) {
 402  4
             throw new IllegalArgumentException("Invalid indexed property '" +
 403  
                     name + "' on bean class '" + bean.getClass() + "'");
 404  
         }
 405  
 
 406  
         // Isolate the name
 407  145
         name = resolver.getProperty(name);
 408  
 
 409  
         // Request the specified indexed property value
 410  145
         return (getIndexedProperty(bean, name, index));
 411  
 
 412  
     }
 413  
 
 414  
 
 415  
     /**
 416  
      * Return the value of the specified indexed property of the specified
 417  
      * bean, with no type conversions.  In addition to supporting the JavaBeans
 418  
      * specification, this method has been extended to support
 419  
      * <code>List</code> objects as well.
 420  
      *
 421  
      * @param bean Bean whose property is to be extracted
 422  
      * @param name Simple property name of the property value to be extracted
 423  
      * @param index Index of the property value to be extracted
 424  
      * @return the indexed property value
 425  
      *
 426  
      * @exception IndexOutOfBoundsException if the specified index
 427  
      *  is outside the valid range for the underlying property
 428  
      * @exception IllegalAccessException if the caller does not have
 429  
      *  access to the property accessor method
 430  
      * @exception IllegalArgumentException if <code>bean</code> or
 431  
      *  <code>name</code> is null
 432  
      * @exception InvocationTargetException if the property accessor method
 433  
      *  throws an exception
 434  
      * @exception NoSuchMethodException if an accessor method for this
 435  
      *  propety cannot be found
 436  
      */
 437  
     public Object getIndexedProperty(Object bean,
 438  
                                             String name, int index)
 439  
             throws IllegalAccessException, InvocationTargetException,
 440  
             NoSuchMethodException {
 441  
 
 442  286
         if (bean == null) {
 443  4
             throw new IllegalArgumentException("No bean specified");
 444  
         }
 445  282
         if (name == null || name.length() == 0) {
 446  29
             if (bean.getClass().isArray()) {
 447  11
                 return Array.get(bean, index);
 448  18
             } else if (bean instanceof List) {
 449  10
                 return ((List)bean).get(index);   
 450  
             }
 451  
         }
 452  261
         if (name == null) {
 453  4
             throw new IllegalArgumentException("No name specified for bean class '" +
 454  
                     bean.getClass() + "'");
 455  
         }
 456  
 
 457  
         // Handle DynaBean instances specially
 458  257
         if (bean instanceof DynaBean) {
 459  78
             DynaProperty descriptor =
 460  
                     ((DynaBean) bean).getDynaClass().getDynaProperty(name);
 461  78
             if (descriptor == null) {
 462  2
                 throw new NoSuchMethodException("Unknown property '" +
 463  
                     name + "' on bean class '" + bean.getClass() + "'");
 464  
             }
 465  76
             return (((DynaBean) bean).get(name, index));
 466  
         }
 467  
 
 468  
         // Retrieve the property descriptor for the specified property
 469  179
         PropertyDescriptor descriptor =
 470  
                 getPropertyDescriptor(bean, name);
 471  179
         if (descriptor == null) {
 472  3
             throw new NoSuchMethodException("Unknown property '" +
 473  
                     name + "' on bean class '" + bean.getClass() + "'");
 474  
         }
 475  
 
 476  
         // Call the indexed getter method if there is one
 477  176
         if (descriptor instanceof IndexedPropertyDescriptor) {
 478  93
             Method readMethod = ((IndexedPropertyDescriptor) descriptor).
 479  
                     getIndexedReadMethod();
 480  93
             readMethod = MethodUtils.getAccessibleMethod(bean.getClass(), readMethod);
 481  93
             if (readMethod != null) {
 482  91
                 Object[] subscript = new Object[1];
 483  91
                 subscript[0] = new Integer(index);
 484  
                 try {
 485  91
                     return (invokeMethod(readMethod,bean, subscript));
 486  8
                 } catch (InvocationTargetException e) {
 487  8
                     if (e.getTargetException() instanceof
 488  
                             IndexOutOfBoundsException) {
 489  8
                         throw (IndexOutOfBoundsException)
 490  
                                 e.getTargetException();
 491  
                     } else {
 492  0
                         throw e;
 493  
                     }
 494  
                 }
 495  
             }
 496  
         }
 497  
 
 498  
         // Otherwise, the underlying property must be an array
 499  85
         Method readMethod = getReadMethod(bean.getClass(), descriptor);
 500  85
         if (readMethod == null) {
 501  2
             throw new NoSuchMethodException("Property '" + name + "' has no " +
 502  
                     "getter method on bean class '" + bean.getClass() + "'");
 503  
         }
 504  
 
 505  
         // Call the property getter and return the value
 506  83
         Object value = invokeMethod(readMethod, bean, EMPTY_OBJECT_ARRAY);
 507  83
         if (!value.getClass().isArray()) {
 508  35
             if (!(value instanceof java.util.List)) {
 509  0
                 throw new IllegalArgumentException("Property '" + name +
 510  
                         "' is not indexed on bean class '" + bean.getClass() + "'");
 511  
             } else {
 512  
                 //get the List's value
 513  35
                 return ((java.util.List) value).get(index);
 514  
             }
 515  
         } else {
 516  
             //get the array's value
 517  
             try {
 518  48
                 return (Array.get(value, index));
 519  6
             } catch (ArrayIndexOutOfBoundsException e) {
 520  6
                 throw new ArrayIndexOutOfBoundsException("Index: " +
 521  
                         index + ", Size: " + Array.getLength(value) +
 522  
                         " for property '" + name + "'");
 523  
             }
 524  
         }
 525  
 
 526  
     }
 527  
 
 528  
 
 529  
     /**
 530  
      * Return the value of the specified mapped property of the
 531  
      * specified bean, with no type conversions.  The key of the
 532  
      * required value must be included (in brackets) as a suffix to
 533  
      * the property name, or <code>IllegalArgumentException</code> will be
 534  
      * thrown.
 535  
      *
 536  
      * @param bean Bean whose property is to be extracted
 537  
      * @param name <code>propertyname(key)</code> of the property value
 538  
      *  to be extracted
 539  
      * @return the mapped property value
 540  
      *
 541  
      * @exception IllegalAccessException if the caller does not have
 542  
      *  access to the property accessor method
 543  
      * @exception InvocationTargetException if the property accessor method
 544  
      *  throws an exception
 545  
      * @exception NoSuchMethodException if an accessor method for this
 546  
      *  propety cannot be found
 547  
      */
 548  
     public Object getMappedProperty(Object bean, String name)
 549  
             throws IllegalAccessException, InvocationTargetException,
 550  
             NoSuchMethodException {
 551  
 
 552  58
         if (bean == null) {
 553  2
             throw new IllegalArgumentException("No bean specified");
 554  
         }
 555  56
         if (name == null) {
 556  0
             throw new IllegalArgumentException("No name specified for bean class '" +
 557  
                     bean.getClass() + "'");
 558  
         }
 559  
 
 560  
         // Identify the key of the requested individual property
 561  56
         String key  = null;
 562  
         try {
 563  56
             key = resolver.getKey(name);
 564  0
         } catch (IllegalArgumentException e) {
 565  0
             throw new IllegalArgumentException
 566  
                     ("Invalid mapped property '" + name +
 567  
                     "' on bean class '" + bean.getClass() + "' " + e.getMessage());
 568  56
         }
 569  56
         if (key == null) {
 570  2
             throw new IllegalArgumentException("Invalid mapped property '" +
 571  
                     name + "' on bean class '" + bean.getClass() + "'");
 572  
         }
 573  
 
 574  
         // Isolate the name
 575  54
         name = resolver.getProperty(name);
 576  
 
 577  
         // Request the specified indexed property value
 578  54
         return (getMappedProperty(bean, name, key));
 579  
 
 580  
     }
 581  
 
 582  
 
 583  
     /**
 584  
      * Return the value of the specified mapped property of the specified
 585  
      * bean, with no type conversions.
 586  
      *
 587  
      * @param bean Bean whose property is to be extracted
 588  
      * @param name Mapped property name of the property value to be extracted
 589  
      * @param key Key of the property value to be extracted
 590  
      * @return the mapped property value
 591  
      *
 592  
      * @exception IllegalAccessException if the caller does not have
 593  
      *  access to the property accessor method
 594  
      * @exception InvocationTargetException if the property accessor method
 595  
      *  throws an exception
 596  
      * @exception NoSuchMethodException if an accessor method for this
 597  
      *  propety cannot be found
 598  
      */
 599  
     public Object getMappedProperty(Object bean,
 600  
                                            String name, String key)
 601  
             throws IllegalAccessException, InvocationTargetException,
 602  
             NoSuchMethodException {
 603  
 
 604  82
         if (bean == null) {
 605  2
             throw new IllegalArgumentException("No bean specified");
 606  
         }
 607  80
         if (name == null) {
 608  2
             throw new IllegalArgumentException("No name specified for bean class '" +
 609  
                     bean.getClass() + "'");
 610  
         }
 611  78
         if (key == null) {
 612  2
             throw new IllegalArgumentException("No key specified for property '" +
 613  
                     name + "' on bean class " + bean.getClass() + "'");
 614  
         }
 615  
 
 616  
         // Handle DynaBean instances specially
 617  76
         if (bean instanceof DynaBean) {
 618  19
             DynaProperty descriptor =
 619  
                     ((DynaBean) bean).getDynaClass().getDynaProperty(name);
 620  19
             if (descriptor == null) {
 621  1
                 throw new NoSuchMethodException("Unknown property '" +
 622  
                         name + "'+ on bean class '" + bean.getClass() + "'");
 623  
             }
 624  18
             return (((DynaBean) bean).get(name, key));
 625  
         }
 626  
 
 627  57
         Object result = null;
 628  
 
 629  
         // Retrieve the property descriptor for the specified property
 630  57
         PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
 631  57
         if (descriptor == null) {
 632  1
             throw new NoSuchMethodException("Unknown property '" +
 633  
                     name + "'+ on bean class '" + bean.getClass() + "'");
 634  
         }
 635  
 
 636  56
         if (descriptor instanceof MappedPropertyDescriptor) {
 637  
             // Call the keyed getter method if there is one
 638  42
             Method readMethod = ((MappedPropertyDescriptor) descriptor).
 639  
                     getMappedReadMethod();
 640  42
             readMethod = MethodUtils.getAccessibleMethod(bean.getClass(), readMethod);
 641  42
             if (readMethod != null) {
 642  40
                 Object[] keyArray = new Object[1];
 643  40
                 keyArray[0] = key;
 644  40
                 result = invokeMethod(readMethod, bean, keyArray);
 645  40
             } else {
 646  2
                 throw new NoSuchMethodException("Property '" + name +
 647  
                         "' has no mapped getter method on bean class '" +
 648  
                         bean.getClass() + "'");
 649  
             }
 650  40
         } else {
 651  
           /* means that the result has to be retrieved from a map */
 652  14
           Method readMethod = getReadMethod(bean.getClass(), descriptor);
 653  14
           if (readMethod != null) {
 654  14
             Object invokeResult = invokeMethod(readMethod, bean, EMPTY_OBJECT_ARRAY);
 655  
             /* test and fetch from the map */
 656  14
             if (invokeResult instanceof java.util.Map) {
 657  14
               result = ((java.util.Map)invokeResult).get(key);
 658  
             }
 659  14
           } else {
 660  0
             throw new NoSuchMethodException("Property '" + name +
 661  
                     "' has no mapped getter method on bean class '" +
 662  
                     bean.getClass() + "'");
 663  
           }
 664  
         }
 665  54
         return result;
 666  
 
 667  
     }
 668  
 
 669  
 
 670  
     /**
 671  
      * <p>Return the mapped property descriptors for this bean class.</p>
 672  
      *
 673  
      * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
 674  
      *
 675  
      * @param beanClass Bean class to be introspected
 676  
      * @return the mapped property descriptors
 677  
      * @deprecated This method should not be exposed
 678  
      */
 679  
     public FastHashMap getMappedPropertyDescriptors(Class beanClass) {
 680  
 
 681  189
         if (beanClass == null) {
 682  0
             return null;
 683  
         }
 684  
 
 685  
         // Look up any cached descriptors for this bean class
 686  189
         return (FastHashMap) mappedDescriptorsCache.get(beanClass);
 687  
 
 688  
     }
 689  
 
 690  
 
 691  
     /**
 692  
      * <p>Return the mapped property descriptors for this bean.</p>
 693  
      *
 694  
      * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
 695  
      *
 696  
      * @param bean Bean to be introspected
 697  
      * @return the mapped property descriptors
 698  
      * @deprecated This method should not be exposed
 699  
      */
 700  
     public FastHashMap getMappedPropertyDescriptors(Object bean) {
 701  
 
 702  183
         if (bean == null) {
 703  0
             return null;
 704  
         }
 705  183
         return (getMappedPropertyDescriptors(bean.getClass()));
 706  
 
 707  
     }
 708  
 
 709  
 
 710  
     /**
 711  
      * Return the value of the (possibly nested) property of the specified
 712  
      * name, for the specified bean, with no type conversions.
 713  
      *
 714  
      * @param bean Bean whose property is to be extracted
 715  
      * @param name Possibly nested name of the property to be extracted
 716  
      * @return the nested property value
 717  
      *
 718  
      * @exception IllegalAccessException if the caller does not have
 719  
      *  access to the property accessor method
 720  
      * @exception IllegalArgumentException if <code>bean</code> or
 721  
      *  <code>name</code> is null
 722  
      * @exception NestedNullException if a nested reference to a
 723  
      *  property returns null
 724  
      * @exception InvocationTargetException 
 725  
      * if the property accessor method throws an exception
 726  
      * @exception NoSuchMethodException if an accessor method for this
 727  
      *  propety cannot be found
 728  
      */
 729  
     public Object getNestedProperty(Object bean, String name)
 730  
             throws IllegalAccessException, InvocationTargetException,
 731  
             NoSuchMethodException {
 732  
 
 733  440
         if (bean == null) {
 734  3
             throw new IllegalArgumentException("No bean specified");
 735  
         }
 736  437
         if (name == null) {
 737  2
             throw new IllegalArgumentException("No name specified for bean class '" +
 738  
                     bean.getClass() + "'");
 739  
         }
 740  
 
 741  
         // Resolve nested references
 742  514
         while (resolver.hasNested(name)) {
 743  84
             String next = resolver.next(name);
 744  84
             Object nestedBean = null;
 745  84
             if (bean instanceof Map) {
 746  1
                 nestedBean = getPropertyOfMapBean((Map) bean, next);
 747  83
             } else if (resolver.isMapped(next)) {
 748  13
                 nestedBean = getMappedProperty(bean, next);
 749  70
             } else if (resolver.isIndexed(next)) {
 750  20
                 nestedBean = getIndexedProperty(bean, next);
 751  
             } else {
 752  50
                 nestedBean = getSimpleProperty(bean, next);
 753  
             }
 754  84
             if (nestedBean == null) {
 755  5
                 throw new NestedNullException
 756  
                         ("Null property value for '" + name +
 757  
                         "' on bean class '" + bean.getClass() + "'");
 758  
             }
 759  79
             bean = nestedBean;
 760  79
             name = resolver.remove(name);
 761  79
         }
 762  
 
 763  430
         if (bean instanceof Map) {
 764  25
             bean = getPropertyOfMapBean((Map) bean, name);
 765  405
         } else if (resolver.isMapped(name)) {
 766  24
             bean = getMappedProperty(bean, name);
 767  381
         } else if (resolver.isIndexed(name)) {
 768  40
             bean = getIndexedProperty(bean, name);
 769  
         } else {
 770  341
             bean = getSimpleProperty(bean, name);
 771  
         }
 772  412
         return bean;
 773  
 
 774  
     }
 775  
 
 776  
     /**
 777  
      * This method is called by getNestedProperty and setNestedProperty to
 778  
      * define what it means to get a property from an object which implements
 779  
      * Map. See setPropertyOfMapBean for more information.
 780  
      *
 781  
      * @param bean Map bean
 782  
      * @param propertyName The property name
 783  
      * @return the property value
 784  
      * 
 785  
      * @throws IllegalArgumentException when the propertyName is regarded as
 786  
      * being invalid.
 787  
      * 
 788  
      * @throws IllegalAccessException just in case subclasses override this
 789  
      * method to try to access real getter methods and find permission is denied.
 790  
      * 
 791  
      * @throws InvocationTargetException just in case subclasses override this
 792  
      * method to try to access real getter methods, and find it throws an
 793  
      * exception when invoked.
 794  
      * 
 795  
      * @throws NoSuchMethodException just in case subclasses override this
 796  
      * method to try to access real getter methods, and want to fail if
 797  
      * no simple method is available.
 798  
      * @since 1.8.0
 799  
      */
 800  
     protected Object getPropertyOfMapBean(Map bean, String propertyName) 
 801  
         throws IllegalArgumentException, IllegalAccessException, 
 802  
         InvocationTargetException, NoSuchMethodException {
 803  
 
 804  22
         if (resolver.isMapped(propertyName)) {
 805  7
             String name = resolver.getProperty(propertyName);
 806  7
             if (name == null || name.length() == 0) {
 807  7
                 propertyName = resolver.getKey(propertyName);
 808  
             }
 809  
         }
 810  
 
 811  22
         if (resolver.isIndexed(propertyName) ||
 812  
             resolver.isMapped(propertyName)) {
 813  1
             throw new IllegalArgumentException(
 814  
                     "Indexed or mapped properties are not supported on"
 815  
                     + " objects of type Map: " + propertyName);
 816  
         }
 817  
 
 818  21
         return bean.get(propertyName);
 819  
     }
 820  
 
 821  
 
 822  
 
 823  
     /**
 824  
      * Return the value of the specified property of the specified bean,
 825  
      * no matter which property reference format is used, with no
 826  
      * type conversions.
 827  
      *
 828  
      * @param bean Bean whose property is to be extracted
 829  
      * @param name Possibly indexed and/or nested name of the property
 830  
      *  to be extracted
 831  
      * @return the property value
 832  
      *
 833  
      * @exception IllegalAccessException if the caller does not have
 834  
      *  access to the property accessor method
 835  
      * @exception IllegalArgumentException if <code>bean</code> or
 836  
      *  <code>name</code> is null
 837  
      * @exception InvocationTargetException if the property accessor method
 838  
      *  throws an exception
 839  
      * @exception NoSuchMethodException if an accessor method for this
 840  
      *  propety cannot be found
 841  
      */
 842  
     public Object getProperty(Object bean, String name)
 843  
             throws IllegalAccessException, InvocationTargetException,
 844  
             NoSuchMethodException {
 845  
 
 846  316
         return (getNestedProperty(bean, name));
 847  
 
 848  
     }
 849  
 
 850  
 
 851  
     /**
 852  
      * <p>Retrieve the property descriptor for the specified property of the
 853  
      * specified bean, or return <code>null</code> if there is no such
 854  
      * descriptor.  This method resolves indexed and nested property
 855  
      * references in the same manner as other methods in this class, except
 856  
      * that if the last (or only) name element is indexed, the descriptor
 857  
      * for the last resolved property itself is returned.</p>
 858  
      *
 859  
      * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
 860  
      *
 861  
      * @param bean Bean for which a property descriptor is requested
 862  
      * @param name Possibly indexed and/or nested name of the property for
 863  
      *  which a property descriptor is requested
 864  
      * @return the property descriptor
 865  
      *
 866  
      * @exception IllegalAccessException if the caller does not have
 867  
      *  access to the property accessor method
 868  
      * @exception IllegalArgumentException if <code>bean</code> or
 869  
      *  <code>name</code> is null
 870  
      * @exception IllegalArgumentException if a nested reference to a
 871  
      *  property returns null
 872  
      * @exception InvocationTargetException if the property accessor method
 873  
      *  throws an exception
 874  
      * @exception NoSuchMethodException if an accessor method for this
 875  
      *  propety cannot be found
 876  
      */
 877  
     public PropertyDescriptor getPropertyDescriptor(Object bean,
 878  
                                                            String name)
 879  
             throws IllegalAccessException, InvocationTargetException,
 880  
             NoSuchMethodException {
 881  
 
 882  1991
         if (bean == null) {
 883  2
             throw new IllegalArgumentException("No bean specified");
 884  
         }
 885  1989
         if (name == null) {
 886  1
             throw new IllegalArgumentException("No name specified for bean class '" +
 887  
                     bean.getClass() + "'");
 888  
         }
 889  
 
 890  
         // Resolve nested references
 891  1989
         while (resolver.hasNested(name)) {
 892  1
             String next = resolver.next(name);
 893  1
             Object nestedBean = getProperty(bean, next);
 894  1
             if (nestedBean == null) {
 895  0
                 throw new NestedNullException
 896  
                         ("Null property value for '" + next +
 897  
                         "' on bean class '" + bean.getClass() + "'");
 898  
             }
 899  1
             bean = nestedBean;
 900  1
             name = resolver.remove(name);
 901  1
         }
 902  
 
 903  
         // Remove any subscript from the final name value
 904  1988
         name = resolver.getProperty(name);
 905  
 
 906  
         // Look up and return this property from our cache
 907  
         // creating and adding it to the cache if not found.
 908  1988
         if (name == null) {
 909  0
             return (null);
 910  
         }
 911  
         
 912  1988
         PropertyDescriptor[] descriptors = getPropertyDescriptors(bean);
 913  1988
         if (descriptors != null) {
 914  
             
 915  28075
             for (int i = 0; i < descriptors.length; i++) {
 916  27892
                 if (name.equals(descriptors[i].getName())) {
 917  1805
                     return (descriptors[i]);
 918  
                 }
 919  
             }
 920  
         }
 921  
 
 922  183
         PropertyDescriptor result = null;
 923  183
         FastHashMap mappedDescriptors =
 924  
                 getMappedPropertyDescriptors(bean);
 925  183
         if (mappedDescriptors == null) {
 926  21
             mappedDescriptors = new FastHashMap();
 927  21
             mappedDescriptors.setFast(true);
 928  21
             mappedDescriptorsCache.put(bean.getClass(), mappedDescriptors);
 929  
         }
 930  183
         result = (PropertyDescriptor) mappedDescriptors.get(name);
 931  183
         if (result == null) {
 932  
             // not found, try to create it
 933  
             try {
 934  73
                 result = new MappedPropertyDescriptor(name, bean.getClass());
 935  53
             } catch (IntrospectionException ie) {
 936  
                 /* Swallow IntrospectionException
 937  
                  * TODO: Why?
 938  
                  */
 939  20
             }
 940  73
             if (result != null) {
 941  20
                 mappedDescriptors.put(name, result);
 942  
             }
 943  
         }
 944  
         
 945  183
         return result;
 946  
 
 947  
     }
 948  
 
 949  
 
 950  
     /**
 951  
      * <p>Retrieve the property descriptors for the specified class,
 952  
      * introspecting and caching them the first time a particular bean class
 953  
      * is encountered.</p>
 954  
      *
 955  
      * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
 956  
      *
 957  
      * @param beanClass Bean class for which property descriptors are requested
 958  
      * @return the property descriptors
 959  
      *
 960  
      * @exception IllegalArgumentException if <code>beanClass</code> is null
 961  
      */
 962  
     public PropertyDescriptor[]
 963  
             getPropertyDescriptors(Class beanClass) {
 964  
 
 965  2020
         if (beanClass == null) {
 966  1
             throw new IllegalArgumentException("No bean class specified");
 967  
         }
 968  
 
 969  
         // Look up any cached descriptors for this bean class
 970  2019
         PropertyDescriptor[] descriptors = null;
 971  2019
         descriptors =
 972  
                 (PropertyDescriptor[]) descriptorsCache.get(beanClass);
 973  2019
         if (descriptors != null) {
 974  1825
             return (descriptors);
 975  
         }
 976  
 
 977  
         // Introspect the bean and cache the generated descriptors
 978  194
         BeanInfo beanInfo = null;
 979  
         try {
 980  194
             beanInfo = Introspector.getBeanInfo(beanClass);
 981  0
         } catch (IntrospectionException e) {
 982  0
             return (new PropertyDescriptor[0]);
 983  194
         }
 984  194
         descriptors = beanInfo.getPropertyDescriptors();
 985  194
         if (descriptors == null) {
 986  0
             descriptors = new PropertyDescriptor[0];
 987  
         }
 988  
 
 989  
         // ----------------- Workaround for Bug 28358 --------- START ------------------
 990  
         //
 991  
         // The following code fixes an issue where IndexedPropertyDescriptor behaves
 992  
         // Differently in different versions of the JDK for 'indexed' properties which
 993  
         // use java.util.List (rather than an array).
 994  
         //
 995  
         // If you have a Bean with the following getters/setters for an indexed property:
 996  
         //
 997  
         //     public List getFoo()
 998  
         //     public Object getFoo(int index)
 999  
         //     public void setFoo(List foo)
 1000  
         //     public void setFoo(int index, Object foo)
 1001  
         //
 1002  
         // then the IndexedPropertyDescriptor's getReadMethod() and getWriteMethod()
 1003  
         // behave as follows:
 1004  
         //
 1005  
         //     JDK 1.3.1_04: returns valid Method objects from these methods.
 1006  
         //     JDK 1.4.2_05: returns null from these methods.
 1007  
         //
 1008  3802
         for (int i = 0; i < descriptors.length; i++) {
 1009  3608
             if (descriptors[i] instanceof IndexedPropertyDescriptor) {
 1010  555
                 IndexedPropertyDescriptor descriptor =  (IndexedPropertyDescriptor)descriptors[i];
 1011  555
                 String propName = descriptor.getName().substring(0, 1).toUpperCase() +
 1012  
                                   descriptor.getName().substring(1);
 1013  
 
 1014  555
                 if (descriptor.getReadMethod() == null) {
 1015  355
                     String methodName = descriptor.getIndexedReadMethod() != null
 1016  
                                         ? descriptor.getIndexedReadMethod().getName()
 1017  
                                         : "get" + propName;
 1018  355
                     Method readMethod = MethodUtils.getMatchingAccessibleMethod(beanClass,
 1019  
                                                             methodName,
 1020  
                                                             EMPTY_CLASS_PARAMETERS);
 1021  355
                     if (readMethod != null) {
 1022  
                         try {
 1023  2
                             descriptor.setReadMethod(readMethod);
 1024  0
                         } catch(Exception e) {
 1025  0
                             log.error("Error setting indexed property read method", e);
 1026  2
                         }
 1027  
                     }
 1028  
                 }
 1029  555
                 if (descriptor.getWriteMethod() == null) {
 1030  355
                     String methodName = descriptor.getIndexedWriteMethod() != null
 1031  
                                       ? descriptor.getIndexedWriteMethod().getName()
 1032  
                                       : "set" + propName;
 1033  355
                     Method writeMethod = MethodUtils.getMatchingAccessibleMethod(beanClass,
 1034  
                                                             methodName,
 1035  
                                                             LIST_CLASS_PARAMETER);
 1036  355
                     if (writeMethod == null) {
 1037  354
                         Method[] methods = beanClass.getMethods();
 1038  25002
                         for (int j = 0; j < methods.length; j++) {
 1039  24649
                             if (methods[j].getName().equals(methodName)) {
 1040  239
                                 Class[] parameterTypes = methods[j].getParameterTypes();
 1041  239
                                 if (parameterTypes.length == 1 &&
 1042  
                                     List.class.isAssignableFrom(parameterTypes[0])) {
 1043  1
                                     writeMethod = methods[j];
 1044  1
                                     break; 
 1045  
                                 }
 1046  
                             }
 1047  
                         }
 1048  
                     }
 1049  355
                     if (writeMethod != null) {
 1050  
                         try {
 1051  2
                             descriptor.setWriteMethod(writeMethod);
 1052  0
                         } catch(Exception e) {
 1053  0
                             log.error("Error setting indexed property write method", e);
 1054  2
                         }
 1055  
                     }
 1056  
                 }
 1057  
             }
 1058  
         }
 1059  
         // ----------------- Workaround for Bug 28358 ---------- END -------------------
 1060  
         try {
 1061  194
             IntrospectorUtils.setNonSyntheticReadWriteMethods(beanClass, descriptors);
 1062  0
         } catch (NoSuchMethodException e) {
 1063  0
             log.warn("unable to read/write methods for synthetic methods", e);
 1064  0
         } catch (IntrospectionException e) {
 1065  0
             log.warn("unable to read/write methods for synthetic methods", e);
 1066  194
         }
 1067  
 
 1068  194
         descriptorsCache.put(beanClass, descriptors);
 1069  194
         return (descriptors);
 1070  
 
 1071  
     }
 1072  
 
 1073  
 
 1074  
     /**
 1075  
      * <p>Retrieve the property descriptors for the specified bean,
 1076  
      * introspecting and caching them the first time a particular bean class
 1077  
      * is encountered.</p>
 1078  
      *
 1079  
      * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
 1080  
      *
 1081  
      * @param bean Bean for which property descriptors are requested
 1082  
      * @return the property descriptors
 1083  
      *
 1084  
      * @exception IllegalArgumentException if <code>bean</code> is null
 1085  
      */
 1086  
     public PropertyDescriptor[] getPropertyDescriptors(Object bean) {
 1087  
 
 1088  2011
         if (bean == null) {
 1089  0
             throw new IllegalArgumentException("No bean specified");
 1090  
         }
 1091  2011
         return (getPropertyDescriptors(bean.getClass()));
 1092  
 
 1093  
     }
 1094  
 
 1095  
 
 1096  
     /**
 1097  
      * <p>Return the Java Class repesenting the property editor class that has
 1098  
      * been registered for this property (if any).  This method follows the
 1099  
      * same name resolution rules used by <code>getPropertyDescriptor()</code>,
 1100  
      * so if the last element of a name reference is indexed, the property
 1101  
      * editor for the underlying property's class is returned.</p>
 1102  
      *
 1103  
      * <p>Note that <code>null</code> will be returned if there is no property,
 1104  
      * or if there is no registered property editor class.  Because this
 1105  
      * return value is ambiguous, you should determine the existence of the
 1106  
      * property itself by other means.</p>
 1107  
      *
 1108  
      * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
 1109  
      *
 1110  
      * @param bean Bean for which a property descriptor is requested
 1111  
      * @param name Possibly indexed and/or nested name of the property for
 1112  
      *  which a property descriptor is requested
 1113  
      * @return the property editor class
 1114  
      *
 1115  
      * @exception IllegalAccessException if the caller does not have
 1116  
      *  access to the property accessor method
 1117  
      * @exception IllegalArgumentException if <code>bean</code> or
 1118  
      *  <code>name</code> is null
 1119  
      * @exception IllegalArgumentException if a nested reference to a
 1120  
      *  property returns null
 1121  
      * @exception InvocationTargetException if the property accessor method
 1122  
      *  throws an exception
 1123  
      * @exception NoSuchMethodException if an accessor method for this
 1124  
      *  propety cannot be found
 1125  
      */
 1126  
     public Class getPropertyEditorClass(Object bean, String name)
 1127  
             throws IllegalAccessException, InvocationTargetException,
 1128  
             NoSuchMethodException {
 1129  
 
 1130  0
         if (bean == null) {
 1131  0
             throw new IllegalArgumentException("No bean specified");
 1132  
         }
 1133  0
         if (name == null) {
 1134  0
             throw new IllegalArgumentException("No name specified for bean class '" +
 1135  
                     bean.getClass() + "'");
 1136  
         }
 1137  
 
 1138  0
         PropertyDescriptor descriptor =
 1139  
                 getPropertyDescriptor(bean, name);
 1140  0
         if (descriptor != null) {
 1141  0
             return (descriptor.getPropertyEditorClass());
 1142  
         } else {
 1143  0
             return (null);
 1144  
         }
 1145  
 
 1146  
     }
 1147  
 
 1148  
 
 1149  
     /**
 1150  
      * Return the Java Class representing the property type of the specified
 1151  
      * property, or <code>null</code> if there is no such property for the
 1152  
      * specified bean.  This method follows the same name resolution rules
 1153  
      * used by <code>getPropertyDescriptor()</code>, so if the last element
 1154  
      * of a name reference is indexed, the type of the property itself will
 1155  
      * be returned.  If the last (or only) element has no property with the
 1156  
      * specified name, <code>null</code> is returned.
 1157  
      *
 1158  
      * @param bean Bean for which a property descriptor is requested
 1159  
      * @param name Possibly indexed and/or nested name of the property for
 1160  
      *  which a property descriptor is requested
 1161  
      * @return The property type
 1162  
      *
 1163  
      * @exception IllegalAccessException if the caller does not have
 1164  
      *  access to the property accessor method
 1165  
      * @exception IllegalArgumentException if <code>bean</code> or
 1166  
      *  <code>name</code> is null
 1167  
      * @exception IllegalArgumentException if a nested reference to a
 1168  
      *  property returns null
 1169  
      * @exception InvocationTargetException if the property accessor method
 1170  
      *  throws an exception
 1171  
      * @exception NoSuchMethodException if an accessor method for this
 1172  
      *  propety cannot be found
 1173  
      */
 1174  
     public Class getPropertyType(Object bean, String name)
 1175  
             throws IllegalAccessException, InvocationTargetException,
 1176  
             NoSuchMethodException {
 1177  
 
 1178  43
         if (bean == null) {
 1179  0
             throw new IllegalArgumentException("No bean specified");
 1180  
         }
 1181  43
         if (name == null) {
 1182  0
             throw new IllegalArgumentException("No name specified for bean class '" +
 1183  
                     bean.getClass() + "'");
 1184  
         }
 1185  
 
 1186  
         // Resolve nested references
 1187  70
         while (resolver.hasNested(name)) {
 1188  28
             String next = resolver.next(name);
 1189  28
             Object nestedBean = getProperty(bean, next);
 1190  28
             if (nestedBean == null) {
 1191  1
                 throw new NestedNullException
 1192  
                         ("Null property value for '" + next +
 1193  
                         "' on bean class '" + bean.getClass() + "'");
 1194  
             }
 1195  27
             bean = nestedBean;
 1196  27
             name = resolver.remove(name);
 1197  27
         }
 1198  
 
 1199  
         // Remove any subscript from the final name value
 1200  42
         name = resolver.getProperty(name);
 1201  
 
 1202  
         // Special handling for DynaBeans
 1203  42
         if (bean instanceof DynaBean) {
 1204  4
             DynaProperty descriptor =
 1205  
                     ((DynaBean) bean).getDynaClass().getDynaProperty(name);
 1206  4
             if (descriptor == null) {
 1207  0
                 return (null);
 1208  
             }
 1209  4
             Class type = descriptor.getType();
 1210  4
             if (type == null) {
 1211  0
                 return (null);
 1212  4
             } else if (type.isArray()) {
 1213  0
                 return (type.getComponentType());
 1214  
             } else {
 1215  4
                 return (type);
 1216  
             }
 1217  
         }
 1218  
 
 1219  38
         PropertyDescriptor descriptor =
 1220  
                 getPropertyDescriptor(bean, name);
 1221  38
         if (descriptor == null) {
 1222  0
             return (null);
 1223  38
         } else if (descriptor instanceof IndexedPropertyDescriptor) {
 1224  6
             return (((IndexedPropertyDescriptor) descriptor).
 1225  
                     getIndexedPropertyType());
 1226  32
         } else if (descriptor instanceof MappedPropertyDescriptor) {
 1227  4
             return (((MappedPropertyDescriptor) descriptor).
 1228  
                     getMappedPropertyType());
 1229  
         } else {
 1230  28
             return (descriptor.getPropertyType());
 1231  
         }
 1232  
 
 1233  
     }
 1234  
 
 1235  
 
 1236  
     /**
 1237  
      * <p>Return an accessible property getter method for this property,
 1238  
      * if there is one; otherwise return <code>null</code>.</p>
 1239  
      *
 1240  
      * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
 1241  
      *
 1242  
      * @param descriptor Property descriptor to return a getter for
 1243  
      * @return The read method
 1244  
      */
 1245  
     public Method getReadMethod(PropertyDescriptor descriptor) {
 1246  
 
 1247  49
         return (MethodUtils.getAccessibleMethod(descriptor.getReadMethod()));
 1248  
 
 1249  
     }
 1250  
 
 1251  
 
 1252  
     /**
 1253  
      * <p>Return an accessible property getter method for this property,
 1254  
      * if there is one; otherwise return <code>null</code>.</p>
 1255  
      *
 1256  
      * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
 1257  
      *
 1258  
      * @param clazz The class of the read method will be invoked on
 1259  
      * @param descriptor Property descriptor to return a getter for
 1260  
      * @return The read method
 1261  
      */
 1262  
     Method getReadMethod(Class clazz, PropertyDescriptor descriptor) {
 1263  782
         return (MethodUtils.getAccessibleMethod(clazz, descriptor.getReadMethod()));
 1264  
     }
 1265  
 
 1266  
 
 1267  
     /**
 1268  
      * Return the value of the specified simple property of the specified
 1269  
      * bean, with no type conversions.
 1270  
      *
 1271  
      * @param bean Bean whose property is to be extracted
 1272  
      * @param name Name of the property to be extracted
 1273  
      * @return The property value
 1274  
      *
 1275  
      * @exception IllegalAccessException if the caller does not have
 1276  
      *  access to the property accessor method
 1277  
      * @exception IllegalArgumentException if <code>bean</code> or
 1278  
      *  <code>name</code> is null
 1279  
      * @exception IllegalArgumentException if the property name
 1280  
      *  is nested or indexed
 1281  
      * @exception InvocationTargetException if the property accessor method
 1282  
      *  throws an exception
 1283  
      * @exception NoSuchMethodException if an accessor method for this
 1284  
      *  propety cannot be found
 1285  
      */
 1286  
     public Object getSimpleProperty(Object bean, String name)
 1287  
             throws IllegalAccessException, InvocationTargetException,
 1288  
             NoSuchMethodException {
 1289  
 
 1290  556
         if (bean == null) {
 1291  2
             throw new IllegalArgumentException("No bean specified");
 1292  
         }
 1293  554
         if (name == null) {
 1294  3
             throw new IllegalArgumentException("No name specified for bean class '" +
 1295  
                     bean.getClass() + "'");
 1296  
         }
 1297  
 
 1298  
         // Validate the syntax of the property name
 1299  551
         if (resolver.hasNested(name)) {
 1300  2
             throw new IllegalArgumentException
 1301  
                     ("Nested property names are not allowed: Property '" +
 1302  
                     name + "' on bean class '" + bean.getClass() + "'");
 1303  549
         } else if (resolver.isIndexed(name)) {
 1304  2
             throw new IllegalArgumentException
 1305  
                     ("Indexed property names are not allowed: Property '" +
 1306  
                     name + "' on bean class '" + bean.getClass() + "'");
 1307  547
         } else if (resolver.isMapped(name)) {
 1308  0
             throw new IllegalArgumentException
 1309  
                     ("Mapped property names are not allowed: Property '" +
 1310  
                     name + "' on bean class '" + bean.getClass() + "'");
 1311  
         }
 1312  
 
 1313  
         // Handle DynaBean instances specially
 1314  547
         if (bean instanceof DynaBean) {
 1315  107
             DynaProperty descriptor =
 1316  
                     ((DynaBean) bean).getDynaClass().getDynaProperty(name);
 1317  107
             if (descriptor == null) {
 1318  1
                 throw new NoSuchMethodException("Unknown property '" +
 1319  
                         name + "' on dynaclass '" + 
 1320  
                         ((DynaBean) bean).getDynaClass() + "'" );
 1321  
             }
 1322  106
             return (((DynaBean) bean).get(name));
 1323  
         }
 1324  
 
 1325  
         // Retrieve the property getter method for the specified property
 1326  440
         PropertyDescriptor descriptor =
 1327  
                 getPropertyDescriptor(bean, name);
 1328  440
         if (descriptor == null) {
 1329  8
             throw new NoSuchMethodException("Unknown property '" +
 1330  
                     name + "' on class '" + bean.getClass() + "'" );
 1331  
         }
 1332  432
         Method readMethod = getReadMethod(bean.getClass(), descriptor);
 1333  432
         if (readMethod == null) {
 1334  12
             throw new NoSuchMethodException("Property '" + name +
 1335  
                     "' has no getter method in class '" + bean.getClass() + "'");
 1336  
         }
 1337  
 
 1338  
         // Call the property getter and return the value
 1339  420
         Object value = invokeMethod(readMethod, bean, EMPTY_OBJECT_ARRAY);
 1340  420
         return (value);
 1341  
 
 1342  
     }
 1343  
 
 1344  
 
 1345  
     /**
 1346  
      * <p>Return an accessible property setter method for this property,
 1347  
      * if there is one; otherwise return <code>null</code>.</p>
 1348  
      *
 1349  
      * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
 1350  
      *
 1351  
      * @param descriptor Property descriptor to return a setter for
 1352  
      * @return The write method
 1353  
      */
 1354  
     public Method getWriteMethod(PropertyDescriptor descriptor) {
 1355  
 
 1356  39
         return (MethodUtils.getAccessibleMethod(descriptor.getWriteMethod()));
 1357  
 
 1358  
     }
 1359  
 
 1360  
 
 1361  
     /**
 1362  
      * <p>Return an accessible property setter method for this property,
 1363  
      * if there is one; otherwise return <code>null</code>.</p>
 1364  
      *
 1365  
      * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
 1366  
      *
 1367  
      * @param clazz The class of the read method will be invoked on
 1368  
      * @param descriptor Property descriptor to return a setter for
 1369  
      * @return The write method
 1370  
      */
 1371  
     Method getWriteMethod(Class clazz, PropertyDescriptor descriptor) {
 1372  560
         return (MethodUtils.getAccessibleMethod(clazz, descriptor.getWriteMethod()));
 1373  
     }
 1374  
 
 1375  
 
 1376  
     /**
 1377  
      * <p>Return <code>true</code> if the specified property name identifies
 1378  
      * a readable property on the specified bean; otherwise, return
 1379  
      * <code>false</code>.
 1380  
      *
 1381  
      * @param bean Bean to be examined (may be a {@link DynaBean}
 1382  
      * @param name Property name to be evaluated
 1383  
      * @return <code>true</code> if the property is readable,
 1384  
      * otherwise <code>false</code>
 1385  
      *
 1386  
      * @exception IllegalArgumentException if <code>bean</code>
 1387  
      *  or <code>name</code> is <code>null</code>
 1388  
      *
 1389  
      * @since BeanUtils 1.6
 1390  
      */
 1391  
     public boolean isReadable(Object bean, String name) {
 1392  
 
 1393  
         // Validate method parameters
 1394  228
         if (bean == null) {
 1395  0
             throw new IllegalArgumentException("No bean specified");
 1396  
         }
 1397  228
         if (name == null) {
 1398  0
             throw new IllegalArgumentException("No name specified for bean class '" +
 1399  
                     bean.getClass() + "'");
 1400  
         }
 1401  
 
 1402  
         // Resolve nested references
 1403  237
         while (resolver.hasNested(name)) {
 1404  10
             String next = resolver.next(name);
 1405  10
             Object nestedBean = null; 
 1406  
             try {
 1407  10
                 nestedBean = getProperty(bean, next);
 1408  0
             } catch (IllegalAccessException e) {
 1409  0
                 return false;
 1410  0
             } catch (InvocationTargetException e) {
 1411  0
                 return false;
 1412  0
             } catch (NoSuchMethodException e) {
 1413  0
                 return false;
 1414  10
             }
 1415  10
             if (nestedBean == null) {
 1416  1
                 throw new NestedNullException
 1417  
                         ("Null property value for '" + next +
 1418  
                         "' on bean class '" + bean.getClass() + "'");
 1419  
             }
 1420  9
             bean = nestedBean;
 1421  9
             name = resolver.remove(name);
 1422  9
         }
 1423  
 
 1424  
         // Remove any subscript from the final name value
 1425  227
         name = resolver.getProperty(name);
 1426  
 
 1427  
         // Treat WrapDynaBean as special case - may be a write-only property
 1428  
         // (see Jira issue# BEANUTILS-61)
 1429  227
         if (bean instanceof WrapDynaBean) {
 1430  20
             bean = ((WrapDynaBean)bean).getInstance();
 1431  
         }
 1432  
 
 1433  
         // Return the requested result
 1434  227
         if (bean instanceof DynaBean) {
 1435  
             // All DynaBean properties are readable
 1436  84
             return (((DynaBean) bean).getDynaClass().getDynaProperty(name) != null);
 1437  
         } else {
 1438  
             try {
 1439  143
                 PropertyDescriptor desc =
 1440  
                     getPropertyDescriptor(bean, name);
 1441  143
                 if (desc != null) {
 1442  143
                     Method readMethod = getReadMethod(bean.getClass(), desc);
 1443  143
                     if (readMethod == null) {
 1444  36
                         if (desc instanceof IndexedPropertyDescriptor) {
 1445  20
                             readMethod = ((IndexedPropertyDescriptor) desc).getIndexedReadMethod();
 1446  16
                         } else if (desc instanceof MappedPropertyDescriptor) {
 1447  8
                             readMethod = ((MappedPropertyDescriptor) desc).getMappedReadMethod();
 1448  
                         }
 1449  36
                         readMethod = MethodUtils.getAccessibleMethod(bean.getClass(), readMethod);
 1450  
                     }
 1451  143
                     return (readMethod != null);
 1452  
                 } else {
 1453  0
                     return (false);
 1454  
                 }
 1455  0
             } catch (IllegalAccessException e) {
 1456  0
                 return (false);
 1457  0
             } catch (InvocationTargetException e) {
 1458  0
                 return (false);
 1459  0
             } catch (NoSuchMethodException e) {
 1460  0
                 return (false);
 1461  
             }
 1462  
         }
 1463  
 
 1464  
     }
 1465  
 
 1466  
 
 1467  
     /**
 1468  
      * <p>Return <code>true</code> if the specified property name identifies
 1469  
      * a writeable property on the specified bean; otherwise, return
 1470  
      * <code>false</code>.
 1471  
      *
 1472  
      * @param bean Bean to be examined (may be a {@link DynaBean}
 1473  
      * @param name Property name to be evaluated
 1474  
      * @return <code>true</code> if the property is writeable,
 1475  
      * otherwise <code>false</code>
 1476  
      *
 1477  
      * @exception IllegalArgumentException if <code>bean</code>
 1478  
      *  or <code>name</code> is <code>null</code>
 1479  
      *
 1480  
      * @since BeanUtils 1.6
 1481  
      */
 1482  
     public boolean isWriteable(Object bean, String name) {
 1483  
 
 1484  
         // Validate method parameters
 1485  268
         if (bean == null) {
 1486  0
             throw new IllegalArgumentException("No bean specified");
 1487  
         }
 1488  268
         if (name == null) {
 1489  0
             throw new IllegalArgumentException("No name specified for bean class '" +
 1490  
                     bean.getClass() + "'");
 1491  
         }
 1492  
 
 1493  
         // Resolve nested references
 1494  277
         while (resolver.hasNested(name)) {
 1495  10
             String next = resolver.next(name);
 1496  10
             Object nestedBean = null; 
 1497  
             try {
 1498  10
                 nestedBean = getProperty(bean, next);
 1499  0
             } catch (IllegalAccessException e) {
 1500  0
                 return false;
 1501  0
             } catch (InvocationTargetException e) {
 1502  0
                 return false;
 1503  0
             } catch (NoSuchMethodException e) {
 1504  0
                 return false;
 1505  10
             }
 1506  10
             if (nestedBean == null) {
 1507  1
                 throw new NestedNullException
 1508  
                         ("Null property value for '" + next +
 1509  
                         "' on bean class '" + bean.getClass() + "'");
 1510  
             }
 1511  9
             bean = nestedBean;
 1512  9
             name = resolver.remove(name);
 1513  9
         }
 1514  
 
 1515  
         // Remove any subscript from the final name value
 1516  267
         name = resolver.getProperty(name);
 1517  
 
 1518  
         // Treat WrapDynaBean as special case - may be a read-only property
 1519  
         // (see Jira issue# BEANUTILS-61)
 1520  267
         if (bean instanceof WrapDynaBean) {
 1521  8
             bean = ((WrapDynaBean)bean).getInstance();
 1522  
         }
 1523  
 
 1524  
         // Return the requested result
 1525  267
         if (bean instanceof DynaBean) {
 1526  
             // All DynaBean properties are writeable
 1527  90
             return (((DynaBean) bean).getDynaClass().getDynaProperty(name) != null);
 1528  
         } else {
 1529  
             try {
 1530  177
                 PropertyDescriptor desc =
 1531  
                     getPropertyDescriptor(bean, name);
 1532  177
                 if (desc != null) {
 1533  151
                     Method writeMethod = getWriteMethod(bean.getClass(), desc);
 1534  151
                     if (writeMethod == null) {
 1535  53
                         if (desc instanceof IndexedPropertyDescriptor) {
 1536  17
                             writeMethod = ((IndexedPropertyDescriptor) desc).getIndexedWriteMethod();
 1537  36
                         } else if (desc instanceof MappedPropertyDescriptor) {
 1538  10
                             writeMethod = ((MappedPropertyDescriptor) desc).getMappedWriteMethod();
 1539  
                         }
 1540  53
                         writeMethod = MethodUtils.getAccessibleMethod(bean.getClass(), writeMethod);
 1541  
                     }
 1542  151
                     return (writeMethod != null);
 1543  
                 } else {
 1544  26
                     return (false);
 1545  
                 }
 1546  0
             } catch (IllegalAccessException e) {
 1547  0
                 return (false);
 1548  0
             } catch (InvocationTargetException e) {
 1549  0
                 return (false);
 1550  0
             } catch (NoSuchMethodException e) {
 1551  0
                 return (false);
 1552  
             }
 1553  
         }
 1554  
 
 1555  
     }
 1556  
 
 1557  
 
 1558  
     /**
 1559  
      * Set the value of the specified indexed property of the specified
 1560  
      * bean, with no type conversions.  The zero-relative index of the
 1561  
      * required value must be included (in square brackets) as a suffix to
 1562  
      * the property name, or <code>IllegalArgumentException</code> will be
 1563  
      * thrown.  In addition to supporting the JavaBeans specification, this
 1564  
      * method has been extended to support <code>List</code> objects as well.
 1565  
      *
 1566  
      * @param bean Bean whose property is to be modified
 1567  
      * @param name <code>propertyname[index]</code> of the property value
 1568  
      *  to be modified
 1569  
      * @param value Value to which the specified property element
 1570  
      *  should be set
 1571  
      *
 1572  
      * @exception IndexOutOfBoundsException if the specified index
 1573  
      *  is outside the valid range for the underlying property
 1574  
      * @exception IllegalAccessException if the caller does not have
 1575  
      *  access to the property accessor method
 1576  
      * @exception IllegalArgumentException if <code>bean</code> or
 1577  
      *  <code>name</code> is null
 1578  
      * @exception InvocationTargetException if the property accessor method
 1579  
      *  throws an exception
 1580  
      * @exception NoSuchMethodException if an accessor method for this
 1581  
      *  propety cannot be found
 1582  
      */
 1583  
     public void setIndexedProperty(Object bean, String name,
 1584  
                                           Object value)
 1585  
             throws IllegalAccessException, InvocationTargetException,
 1586  
             NoSuchMethodException {
 1587  
 
 1588  57
         if (bean == null) {
 1589  4
             throw new IllegalArgumentException("No bean specified");
 1590  
         }
 1591  53
         if (name == null) {
 1592  0
             throw new IllegalArgumentException("No name specified for bean class '" +
 1593  
                     bean.getClass() + "'");
 1594  
         }
 1595  
 
 1596  
         // Identify the index of the requested individual property
 1597  53
         int index = -1;
 1598  
         try {
 1599  53
             index = resolver.getIndex(name);
 1600  0
         } catch (IllegalArgumentException e) {
 1601  0
             throw new IllegalArgumentException("Invalid indexed property '" +
 1602  
                     name + "' on bean class '" + bean.getClass() + "'");
 1603  53
         }
 1604  53
         if (index < 0) {
 1605  4
             throw new IllegalArgumentException("Invalid indexed property '" +
 1606  
                     name + "' on bean class '" + bean.getClass() + "'");
 1607  
         }
 1608  
 
 1609  
         // Isolate the name
 1610  49
         name = resolver.getProperty(name);
 1611  
 
 1612  
         // Set the specified indexed property value
 1613  49
         setIndexedProperty(bean, name, index, value);
 1614  
 
 1615  43
     }
 1616  
 
 1617  
 
 1618  
     /**
 1619  
      * Set the value of the specified indexed property of the specified
 1620  
      * bean, with no type conversions.  In addition to supporting the JavaBeans
 1621  
      * specification, this method has been extended to support
 1622  
      * <code>List</code> objects as well.
 1623  
      *
 1624  
      * @param bean Bean whose property is to be set
 1625  
      * @param name Simple property name of the property value to be set
 1626  
      * @param index Index of the property value to be set
 1627  
      * @param value Value to which the indexed property element is to be set
 1628  
      *
 1629  
      * @exception IndexOutOfBoundsException if the specified index
 1630  
      *  is outside the valid range for the underlying property
 1631  
      * @exception IllegalAccessException if the caller does not have
 1632  
      *  access to the property accessor method
 1633  
      * @exception IllegalArgumentException if <code>bean</code> or
 1634  
      *  <code>name</code> is null
 1635  
      * @exception InvocationTargetException if the property accessor method
 1636  
      *  throws an exception
 1637  
      * @exception NoSuchMethodException if an accessor method for this
 1638  
      *  propety cannot be found
 1639  
      */
 1640  
     public void setIndexedProperty(Object bean, String name,
 1641  
                                           int index, Object value)
 1642  
             throws IllegalAccessException, InvocationTargetException,
 1643  
             NoSuchMethodException {
 1644  
 
 1645  117
         if (bean == null) {
 1646  4
             throw new IllegalArgumentException("No bean specified");
 1647  
         }
 1648  113
         if (name == null || name.length() == 0) {
 1649  14
             if (bean.getClass().isArray()) {
 1650  4
                 Array.set(bean, index, value);
 1651  4
                 return;
 1652  10
             } else if (bean instanceof List) {
 1653  2
                 ((List)bean).set(index, value);   
 1654  2
                 return;
 1655  
             }
 1656  
         }
 1657  107
         if (name == null) {
 1658  4
             throw new IllegalArgumentException("No name specified for bean class '" +
 1659  
                     bean.getClass() + "'");
 1660  
         }
 1661  
 
 1662  
         // Handle DynaBean instances specially
 1663  103
         if (bean instanceof DynaBean) {
 1664  32
             DynaProperty descriptor =
 1665  
                     ((DynaBean) bean).getDynaClass().getDynaProperty(name);
 1666  32
             if (descriptor == null) {
 1667  2
                 throw new NoSuchMethodException("Unknown property '" +
 1668  
                         name + "' on bean class '" + bean.getClass() + "'");
 1669  
             }
 1670  30
             ((DynaBean) bean).set(name, index, value);
 1671  19
             return;
 1672  
         }
 1673  
 
 1674  
         // Retrieve the property descriptor for the specified property
 1675  71
         PropertyDescriptor descriptor =
 1676  
                 getPropertyDescriptor(bean, name);
 1677  71
         if (descriptor == null) {
 1678  3
             throw new NoSuchMethodException("Unknown property '" +
 1679  
                     name + "' on bean class '" + bean.getClass() + "'");
 1680  
         }
 1681  
 
 1682  
         // Call the indexed setter method if there is one
 1683  68
         if (descriptor instanceof IndexedPropertyDescriptor) {
 1684  32
             Method writeMethod = ((IndexedPropertyDescriptor) descriptor).
 1685  
                     getIndexedWriteMethod();
 1686  32
             writeMethod = MethodUtils.getAccessibleMethod(bean.getClass(), writeMethod);
 1687  32
             if (writeMethod != null) {
 1688  30
                 Object[] subscript = new Object[2];
 1689  30
                 subscript[0] = new Integer(index);
 1690  30
                 subscript[1] = value;
 1691  
                 try {
 1692  30
                     if (log.isTraceEnabled()) {
 1693  0
                         String valueClassName =
 1694  
                             value == null ? "<null>" 
 1695  
                                           : value.getClass().getName();
 1696  0
                         log.trace("setSimpleProperty: Invoking method "
 1697  
                                   + writeMethod +" with index=" + index
 1698  
                                   + ", value=" + value
 1699  
                                   + " (class " + valueClassName+ ")");
 1700  
                     }
 1701  30
                     invokeMethod(writeMethod, bean, subscript);
 1702  6
                 } catch (InvocationTargetException e) {
 1703  6
                     if (e.getTargetException() instanceof
 1704  
                             IndexOutOfBoundsException) {
 1705  6
                         throw (IndexOutOfBoundsException)
 1706  
                                 e.getTargetException();
 1707  
                     } else {
 1708  0
                         throw e;
 1709  
                     }
 1710  24
                 }
 1711  24
                 return;
 1712  
             }
 1713  
         }
 1714  
 
 1715  
         // Otherwise, the underlying property must be an array or a list
 1716  38
         Method readMethod = getReadMethod(bean.getClass(), descriptor);
 1717  38
         if (readMethod == null) {
 1718  2
             throw new NoSuchMethodException("Property '" + name +
 1719  
                     "' has no getter method on bean class '" + bean.getClass() + "'");
 1720  
         }
 1721  
 
 1722  
         // Call the property getter to get the array or list
 1723  36
         Object array = invokeMethod(readMethod, bean, EMPTY_OBJECT_ARRAY);
 1724  36
         if (!array.getClass().isArray()) {
 1725  5
             if (array instanceof List) {
 1726  
                 // Modify the specified value in the List
 1727  5
                 ((List) array).set(index, value);
 1728  
             } else {
 1729  0
                 throw new IllegalArgumentException("Property '" + name +
 1730  
                         "' is not indexed on bean class '" + bean.getClass() + "'");
 1731  
             }
 1732  
         } else {
 1733  
             // Modify the specified value in the array
 1734  31
             Array.set(array, index, value);
 1735  
         }
 1736  
 
 1737  29
     }
 1738  
 
 1739  
 
 1740  
     /**
 1741  
      * Set the value of the specified mapped property of the
 1742  
      * specified bean, with no type conversions.  The key of the
 1743  
      * value to set must be included (in brackets) as a suffix to
 1744  
      * the property name, or <code>IllegalArgumentException</code> will be
 1745  
      * thrown.
 1746  
      *
 1747  
      * @param bean Bean whose property is to be set
 1748  
      * @param name <code>propertyname(key)</code> of the property value
 1749  
      *  to be set
 1750  
      * @param value The property value to be set
 1751  
      *
 1752  
      * @exception IllegalAccessException if the caller does not have
 1753  
      *  access to the property accessor method
 1754  
      * @exception InvocationTargetException if the property accessor method
 1755  
      *  throws an exception
 1756  
      * @exception NoSuchMethodException if an accessor method for this
 1757  
      *  propety cannot be found
 1758  
      */
 1759  
     public void setMappedProperty(Object bean, String name,
 1760  
                                          Object value)
 1761  
             throws IllegalAccessException, InvocationTargetException,
 1762  
             NoSuchMethodException {
 1763  
 
 1764  27
         if (bean == null) {
 1765  2
             throw new IllegalArgumentException("No bean specified");
 1766  
         }
 1767  25
         if (name == null) {
 1768  0
             throw new IllegalArgumentException("No name specified for bean class '" +
 1769  
                     bean.getClass() + "'");
 1770  
         }
 1771  
 
 1772  
         // Identify the key of the requested individual property
 1773  25
         String key  = null;
 1774  
         try {
 1775  25
             key = resolver.getKey(name);
 1776  0
         } catch (IllegalArgumentException e) {
 1777  0
             throw new IllegalArgumentException
 1778  
                     ("Invalid mapped property '" + name + 
 1779  
                     "' on bean class '" + bean.getClass() + "'");
 1780  25
         }
 1781  25
         if (key == null) {
 1782  2
             throw new IllegalArgumentException
 1783  
                     ("Invalid mapped property '" + name + 
 1784  
                     "' on bean class '" + bean.getClass() + "'");
 1785  
         }
 1786  
 
 1787  
         // Isolate the name
 1788  23
         name = resolver.getProperty(name);
 1789  
 
 1790  
         // Request the specified indexed property value
 1791  23
         setMappedProperty(bean, name, key, value);
 1792  
 
 1793  19
     }
 1794  
 
 1795  
 
 1796  
     /**
 1797  
      * Set the value of the specified mapped property of the specified
 1798  
      * bean, with no type conversions.
 1799  
      *
 1800  
      * @param bean Bean whose property is to be set
 1801  
      * @param name Mapped property name of the property value to be set
 1802  
      * @param key Key of the property value to be set
 1803  
      * @param value The property value to be set
 1804  
      *
 1805  
      * @exception IllegalAccessException if the caller does not have
 1806  
      *  access to the property accessor method
 1807  
      * @exception InvocationTargetException if the property accessor method
 1808  
      *  throws an exception
 1809  
      * @exception NoSuchMethodException if an accessor method for this
 1810  
      *  propety cannot be found
 1811  
      */
 1812  
     public void setMappedProperty(Object bean, String name,
 1813  
                                          String key, Object value)
 1814  
             throws IllegalAccessException, InvocationTargetException,
 1815  
             NoSuchMethodException {
 1816  
 
 1817  40
         if (bean == null) {
 1818  2
             throw new IllegalArgumentException("No bean specified");
 1819  
         }
 1820  38
         if (name == null) {
 1821  2
             throw new IllegalArgumentException("No name specified for bean class '" +
 1822  
                     bean.getClass() + "'");
 1823  
         }
 1824  36
         if (key == null) {
 1825  2
             throw new IllegalArgumentException("No key specified for property '" +
 1826  
                     name + "' on bean class '" + bean.getClass() + "'");
 1827  
         }
 1828  
 
 1829  
         // Handle DynaBean instances specially
 1830  34
         if (bean instanceof DynaBean) {
 1831  9
             DynaProperty descriptor =
 1832  
                     ((DynaBean) bean).getDynaClass().getDynaProperty(name);
 1833  9
             if (descriptor == null) {
 1834  1
                 throw new NoSuchMethodException("Unknown property '" +
 1835  
                         name + "' on bean class '" + bean.getClass() + "'");
 1836  
             }
 1837  8
             ((DynaBean) bean).set(name, key, value);
 1838  7
             return;
 1839  
         }
 1840  
 
 1841  
         // Retrieve the property descriptor for the specified property
 1842  25
         PropertyDescriptor descriptor =
 1843  
                 getPropertyDescriptor(bean, name);
 1844  25
         if (descriptor == null) {
 1845  1
             throw new NoSuchMethodException("Unknown property '" +
 1846  
                     name + "' on bean class '" + bean.getClass() + "'");
 1847  
         }
 1848  
 
 1849  24
         if (descriptor instanceof MappedPropertyDescriptor) {
 1850  
             // Call the keyed setter method if there is one
 1851  20
             Method mappedWriteMethod =
 1852  
                     ((MappedPropertyDescriptor) descriptor).
 1853  
                     getMappedWriteMethod();
 1854  20
             mappedWriteMethod = MethodUtils.getAccessibleMethod(bean.getClass(), mappedWriteMethod);
 1855  20
             if (mappedWriteMethod != null) {
 1856  18
                 Object[] params = new Object[2];
 1857  18
                 params[0] = key;
 1858  18
                 params[1] = value;
 1859  18
                 if (log.isTraceEnabled()) {
 1860  0
                     String valueClassName =
 1861  
                         value == null ? "<null>" : value.getClass().getName();
 1862  0
                     log.trace("setSimpleProperty: Invoking method "
 1863  
                               + mappedWriteMethod + " with key=" + key
 1864  
                               + ", value=" + value
 1865  
                               + " (class " + valueClassName +")");
 1866  
                 }
 1867  18
                 invokeMethod(mappedWriteMethod, bean, params);
 1868  18
             } else {
 1869  2
                 throw new NoSuchMethodException
 1870  
                     ("Property '" + name + "' has no mapped setter method" +
 1871  
                      "on bean class '" + bean.getClass() + "'");
 1872  
             }
 1873  18
         } else {
 1874  
           /* means that the result has to be retrieved from a map */
 1875  4
           Method readMethod = getReadMethod(bean.getClass(), descriptor);
 1876  4
           if (readMethod != null) {
 1877  4
             Object invokeResult = invokeMethod(readMethod, bean, EMPTY_OBJECT_ARRAY);
 1878  
             /* test and fetch from the map */
 1879  4
             if (invokeResult instanceof java.util.Map) {
 1880  4
               ((java.util.Map)invokeResult).put(key, value);
 1881  
             }
 1882  4
           } else {
 1883  0
             throw new NoSuchMethodException("Property '" + name +
 1884  
                     "' has no mapped getter method on bean class '" +
 1885  
                     bean.getClass() + "'");
 1886  
           }
 1887  
         }
 1888  
 
 1889  22
     }
 1890  
 
 1891  
 
 1892  
     /**
 1893  
      * Set the value of the (possibly nested) property of the specified
 1894  
      * name, for the specified bean, with no type conversions.
 1895  
      * <p>
 1896  
      * Example values for parameter "name" are:
 1897  
      * <ul>
 1898  
      * <li> "a" -- sets the value of property a of the specified bean </li>
 1899  
      * <li> "a.b" -- gets the value of property a of the specified bean,
 1900  
      * then on that object sets the value of property b.</li>
 1901  
      * <li> "a(key)" -- sets a value of mapped-property a on the specified
 1902  
      * bean. This effectively means bean.setA("key").</li>
 1903  
      * <li> "a[3]" -- sets a value of indexed-property a on the specified
 1904  
      * bean. This effectively means bean.setA(3).</li>
 1905  
      * </ul>
 1906  
      *
 1907  
      * @param bean Bean whose property is to be modified
 1908  
      * @param name Possibly nested name of the property to be modified
 1909  
      * @param value Value to which the property is to be set
 1910  
      *
 1911  
      * @exception IllegalAccessException if the caller does not have
 1912  
      *  access to the property accessor method
 1913  
      * @exception IllegalArgumentException if <code>bean</code> or
 1914  
      *  <code>name</code> is null
 1915  
      * @exception IllegalArgumentException if a nested reference to a
 1916  
      *  property returns null
 1917  
      * @exception InvocationTargetException if the property accessor method
 1918  
      *  throws an exception
 1919  
      * @exception NoSuchMethodException if an accessor method for this
 1920  
      *  propety cannot be found
 1921  
      */
 1922  
     public void setNestedProperty(Object bean,
 1923  
                                          String name, Object value)
 1924  
             throws IllegalAccessException, InvocationTargetException,
 1925  
             NoSuchMethodException {
 1926  
 
 1927  310
         if (bean == null) {
 1928  2
             throw new IllegalArgumentException("No bean specified");
 1929  
         }
 1930  308
         if (name == null) {
 1931  2
             throw new IllegalArgumentException("No name specified for bean class '" +
 1932  
                     bean.getClass() + "'");
 1933  
         }
 1934  
 
 1935  
         // Resolve nested references
 1936  344
         while (resolver.hasNested(name)) {
 1937  40
             String next = resolver.next(name);
 1938  40
             Object nestedBean = null;
 1939  40
             if (bean instanceof Map) {
 1940  1
                 nestedBean = getPropertyOfMapBean((Map)bean, next);
 1941  39
             } else if (resolver.isMapped(next)) {
 1942  4
                 nestedBean = getMappedProperty(bean, next);
 1943  35
             } else if (resolver.isIndexed(next)) {
 1944  7
                 nestedBean = getIndexedProperty(bean, next);
 1945  
             } else {
 1946  28
                 nestedBean = getSimpleProperty(bean, next);
 1947  
             }
 1948  40
             if (nestedBean == null) {
 1949  2
                 throw new NestedNullException
 1950  
                         ("Null property value for '" + name +
 1951  
                          "' on bean class '" + bean.getClass() + "'");
 1952  
             }
 1953  38
             bean = nestedBean;
 1954  38
             name = resolver.remove(name);
 1955  38
         }
 1956  
 
 1957  304
         if (bean instanceof Map) {
 1958  13
             setPropertyOfMapBean((Map) bean, name, value);
 1959  291
         } else if (resolver.isMapped(name)) {
 1960  19
             setMappedProperty(bean, name, value);
 1961  272
         } else if (resolver.isIndexed(name)) {
 1962  34
             setIndexedProperty(bean, name, value);
 1963  
         } else {
 1964  238
             setSimpleProperty(bean, name, value);
 1965  
         }
 1966  
 
 1967  282
     }
 1968  
 
 1969  
     /**
 1970  
      * This method is called by method setNestedProperty when the current bean
 1971  
      * is found to be a Map object, and defines how to deal with setting
 1972  
      * a property on a Map.
 1973  
      * <p>
 1974  
      * The standard implementation here is to:
 1975  
      * <ul>
 1976  
      * <li>call bean.set(propertyName) for all propertyName values.</li>
 1977  
      * <li>throw an IllegalArgumentException if the property specifier
 1978  
      * contains MAPPED_DELIM or INDEXED_DELIM, as Map entries are essentially
 1979  
      * simple properties; mapping and indexing operations do not make sense
 1980  
      * when accessing a map (even thought the returned object may be a Map
 1981  
      * or an Array).</li>
 1982  
      * </ul>
 1983  
      * <p>
 1984  
      * The default behaviour of beanutils 1.7.1 or later is for assigning to
 1985  
      * "a.b" to mean a.put(b, obj) always. However the behaviour of beanutils 
 1986  
      * version 1.6.0, 1.6.1, 1.7.0 was for "a.b" to mean a.setB(obj) if such
 1987  
      * a method existed, and a.put(b, obj) otherwise. In version 1.5 it meant
 1988  
      * a.put(b, obj) always (ie the same as the behaviour in the current version).
 1989  
      * In versions prior to 1.5 it meant a.setB(obj) always. [yes, this is 
 1990  
      * all <i>very</i> unfortunate]
 1991  
      * <p>
 1992  
      * Users who would like to customise the meaning of "a.b" in method 
 1993  
      * setNestedProperty when a is a Map can create a custom subclass of
 1994  
      * this class and override this method to implement the behaviour of 
 1995  
      * their choice, such as restoring the pre-1.4 behaviour of this class
 1996  
      * if they wish. When overriding this method, do not forget to deal 
 1997  
      * with MAPPED_DELIM and INDEXED_DELIM characters in the propertyName.
 1998  
      * <p>
 1999  
      * Note, however, that the recommended solution for objects that
 2000  
      * implement Map but want their simple properties to come first is
 2001  
      * for <i>those</i> objects to override their get/put methods to implement
 2002  
      * that behaviour, and <i>not</i> to solve the problem by modifying the
 2003  
      * default behaviour of the PropertyUtilsBean class by overriding this
 2004  
      * method.
 2005  
      *
 2006  
      * @param bean Map bean
 2007  
      * @param propertyName The property name
 2008  
      * @param value the property value
 2009  
      * 
 2010  
      * @throws IllegalArgumentException when the propertyName is regarded as
 2011  
      * being invalid.
 2012  
      * 
 2013  
      * @throws IllegalAccessException just in case subclasses override this
 2014  
      * method to try to access real setter methods and find permission is denied.
 2015  
      * 
 2016  
      * @throws InvocationTargetException just in case subclasses override this
 2017  
      * method to try to access real setter methods, and find it throws an
 2018  
      * exception when invoked.
 2019  
      * 
 2020  
      * @throws NoSuchMethodException just in case subclasses override this
 2021  
      * method to try to access real setter methods, and want to fail if
 2022  
      * no simple method is available.
 2023  
      * @since 1.8.0
 2024  
      */
 2025  
     protected void setPropertyOfMapBean(Map bean, String propertyName, Object value)
 2026  
         throws IllegalArgumentException, IllegalAccessException, 
 2027  
         InvocationTargetException, NoSuchMethodException {
 2028  
 
 2029  11
         if (resolver.isMapped(propertyName)) {
 2030  6
             String name = resolver.getProperty(propertyName);
 2031  6
             if (name == null || name.length() == 0) {
 2032  5
                 propertyName = resolver.getKey(propertyName);
 2033  
             }
 2034  
         }
 2035  
 
 2036  11
         if (resolver.isIndexed(propertyName) ||
 2037  
             resolver.isMapped(propertyName)) {
 2038  1
             throw new IllegalArgumentException(
 2039  
                     "Indexed or mapped properties are not supported on"
 2040  
                     + " objects of type Map: " + propertyName);
 2041  
         }
 2042  
 
 2043  10
         bean.put(propertyName, value);
 2044  10
     }
 2045  
 
 2046  
 
 2047  
 
 2048  
     /**
 2049  
      * Set the value of the specified property of the specified bean,
 2050  
      * no matter which property reference format is used, with no
 2051  
      * type conversions.
 2052  
      *
 2053  
      * @param bean Bean whose property is to be modified
 2054  
      * @param name Possibly indexed and/or nested name of the property
 2055  
      *  to be modified
 2056  
      * @param value Value to which this property is to be set
 2057  
      *
 2058  
      * @exception IllegalAccessException if the caller does not have
 2059  
      *  access to the property accessor method
 2060  
      * @exception IllegalArgumentException if <code>bean</code> or
 2061  
      *  <code>name</code> is null
 2062  
      * @exception InvocationTargetException if the property accessor method
 2063  
      *  throws an exception
 2064  
      * @exception NoSuchMethodException if an accessor method for this
 2065  
      *  propety cannot be found
 2066  
      */
 2067  
     public void setProperty(Object bean, String name, Object value)
 2068  
             throws IllegalAccessException, InvocationTargetException,
 2069  
             NoSuchMethodException {
 2070  
 
 2071  278
         setNestedProperty(bean, name, value);
 2072  
 
 2073  259
     }
 2074  
 
 2075  
 
 2076  
     /**
 2077  
      * Set the value of the specified simple property of the specified bean,
 2078  
      * with no type conversions.
 2079  
      *
 2080  
      * @param bean Bean whose property is to be modified
 2081  
      * @param name Name of the property to be modified
 2082  
      * @param value Value to which the property should be set
 2083  
      *
 2084  
      * @exception IllegalAccessException if the caller does not have
 2085  
      *  access to the property accessor method
 2086  
      * @exception IllegalArgumentException if <code>bean</code> or
 2087  
      *  <code>name</code> is null
 2088  
      * @exception IllegalArgumentException if the property name is
 2089  
      *  nested or indexed
 2090  
      * @exception InvocationTargetException if the property accessor method
 2091  
      *  throws an exception
 2092  
      * @exception NoSuchMethodException if an accessor method for this
 2093  
      *  propety cannot be found
 2094  
      */
 2095  
     public void setSimpleProperty(Object bean,
 2096  
                                          String name, Object value)
 2097  
             throws IllegalAccessException, InvocationTargetException,
 2098  
             NoSuchMethodException {
 2099  
 
 2100  521
         if (bean == null) {
 2101  2
             throw new IllegalArgumentException("No bean specified");
 2102  
         }
 2103  519
         if (name == null) {
 2104  2
             throw new IllegalArgumentException("No name specified for bean class '" +
 2105  
                     bean.getClass() + "'");
 2106  
         }
 2107  
 
 2108  
         // Validate the syntax of the property name
 2109  517
         if (resolver.hasNested(name)) {
 2110  2
             throw new IllegalArgumentException
 2111  
                     ("Nested property names are not allowed: Property '" +
 2112  
                     name + "' on bean class '" + bean.getClass() + "'");
 2113  515
         } else if (resolver.isIndexed(name)) {
 2114  2
             throw new IllegalArgumentException
 2115  
                     ("Indexed property names are not allowed: Property '" +
 2116  
                     name + "' on bean class '" + bean.getClass() + "'");
 2117  513
         } else if (resolver.isMapped(name)) {
 2118  0
             throw new IllegalArgumentException
 2119  
                     ("Mapped property names are not allowed: Property '" +
 2120  
                     name + "' on bean class '" + bean.getClass() + "'");
 2121  
         }
 2122  
 
 2123  
         // Handle DynaBean instances specially
 2124  513
         if (bean instanceof DynaBean) {
 2125  99
             DynaProperty descriptor =
 2126  
                     ((DynaBean) bean).getDynaClass().getDynaProperty(name);
 2127  99
             if (descriptor == null) {
 2128  1
                 throw new NoSuchMethodException("Unknown property '" +
 2129  
                         name + "' on dynaclass '" + 
 2130  
                         ((DynaBean) bean).getDynaClass() + "'" );
 2131  
             }
 2132  98
             ((DynaBean) bean).set(name, value);
 2133  97
             return;
 2134  
         }
 2135  
 
 2136  
         // Retrieve the property setter method for the specified property
 2137  414
         PropertyDescriptor descriptor =
 2138  
                 getPropertyDescriptor(bean, name);
 2139  414
         if (descriptor == null) {
 2140  5
             throw new NoSuchMethodException("Unknown property '" +
 2141  
                     name + "' on class '" + bean.getClass() + "'" );
 2142  
         }
 2143  409
         Method writeMethod = getWriteMethod(bean.getClass(), descriptor);
 2144  409
         if (writeMethod == null) {
 2145  7
             throw new NoSuchMethodException("Property '" + name +
 2146  
                     "' has no setter method in class '" + bean.getClass() + "'");
 2147  
         }
 2148  
 
 2149  
         // Call the property setter method
 2150  402
         Object[] values = new Object[1];
 2151  402
         values[0] = value;
 2152  402
         if (log.isTraceEnabled()) {
 2153  0
             String valueClassName =
 2154  
                 value == null ? "<null>" : value.getClass().getName();
 2155  0
             log.trace("setSimpleProperty: Invoking method " + writeMethod
 2156  
                       + " with value " + value + " (class " + valueClassName + ")");
 2157  
         }
 2158  402
         invokeMethod(writeMethod, bean, values);
 2159  
 
 2160  391
     }
 2161  
     
 2162  
     /** This just catches and wraps IllegalArgumentException. */
 2163  
     private Object invokeMethod(
 2164  
                         Method method, 
 2165  
                         Object bean, 
 2166  
                         Object[] values) 
 2167  
                             throws
 2168  
                                 IllegalAccessException,
 2169  
                                 InvocationTargetException {
 2170  1138
         if(bean == null) {
 2171  0
             throw new IllegalArgumentException("No bean specified " +
 2172  
                 "- this should have been checked before reaching this method");
 2173  
         }
 2174  
 
 2175  
         try {
 2176  
             
 2177  1138
             return method.invoke(bean, values);
 2178  
         
 2179  0
         } catch (NullPointerException cause) {
 2180  
             // JDK 1.3 and JDK 1.4 throw NullPointerException if an argument is
 2181  
             // null for a primitive value (JDK 1.5+ throw IllegalArgumentException)
 2182  0
             String valueString = "";
 2183  0
             if (values != null) {
 2184  0
                 for (int i = 0; i < values.length; i++) {
 2185  0
                     if (i>0) {
 2186  0
                         valueString += ", " ;
 2187  
                     }
 2188  0
                     if (values[i] == null) {
 2189  0
                         valueString += "<null>";
 2190  
                     } else {
 2191  0
                         valueString += (values[i]).getClass().getName();
 2192  
                     }
 2193  
                 }
 2194  
             }
 2195  0
             String expectedString = "";
 2196  0
             Class[] parTypes = method.getParameterTypes();
 2197  0
             if (parTypes != null) {
 2198  0
                 for (int i = 0; i < parTypes.length; i++) {
 2199  0
                     if (i > 0) {
 2200  0
                         expectedString += ", ";
 2201  
                     }
 2202  0
                     expectedString += parTypes[i].getName();
 2203  
                 }
 2204  
             }
 2205  0
             IllegalArgumentException e = new IllegalArgumentException(
 2206  
                 "Cannot invoke " + method.getDeclaringClass().getName() + "." 
 2207  
                 + method.getName() + " on bean class '" + bean.getClass() +
 2208  
                 "' - " + cause.getMessage()
 2209  
                 // as per https://issues.apache.org/jira/browse/BEANUTILS-224
 2210  
                 + " - had objects of type \"" + valueString
 2211  
                 + "\" but expected signature \""
 2212  
                 +   expectedString + "\""
 2213  
                 );
 2214  0
             if (!BeanUtils.initCause(e, cause)) {
 2215  0
                 log.error("Method invocation failed", cause);
 2216  
             }
 2217  0
             throw e;
 2218  11
         } catch (IllegalArgumentException cause) {
 2219  11
             String valueString = "";
 2220  11
             if (values != null) {
 2221  22
                 for (int i = 0; i < values.length; i++) {
 2222  11
                     if (i>0) {
 2223  0
                         valueString += ", " ;
 2224  
                     }
 2225  11
                     if (values[i] == null) {
 2226  2
                         valueString += "<null>";
 2227  
                     } else {
 2228  9
                         valueString += (values[i]).getClass().getName();
 2229  
                     }
 2230  
                 }
 2231  
             }
 2232  11
             String expectedString = "";
 2233  11
             Class[] parTypes = method.getParameterTypes();
 2234  11
             if (parTypes != null) {
 2235  22
                 for (int i = 0; i < parTypes.length; i++) {
 2236  11
                     if (i > 0) {
 2237  0
                         expectedString += ", ";
 2238  
                     }
 2239  11
                     expectedString += parTypes[i].getName();
 2240  
                 }
 2241  
             }
 2242  11
             IllegalArgumentException e = new IllegalArgumentException(
 2243  
                 "Cannot invoke " + method.getDeclaringClass().getName() + "." 
 2244  
                 + method.getName() + " on bean class '" + bean.getClass() +
 2245  
                 "' - " + cause.getMessage()
 2246  
                 // as per https://issues.apache.org/jira/browse/BEANUTILS-224
 2247  
                 + " - had objects of type \"" + valueString
 2248  
                 + "\" but expected signature \""
 2249  
                 +   expectedString + "\""
 2250  
                 );
 2251  11
             if (!BeanUtils.initCause(e, cause)) {
 2252  0
                 log.error("Method invocation failed", cause);
 2253  
             }
 2254  11
             throw e;
 2255  
             
 2256  
         }
 2257  
     }
 2258  
 }