| /* |
| * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//beanutils/src/java/org/apache/commons/beanutils/PropertyUtils.java,v 1.15 2001/12/15 19:19:24 craigmcc Exp $ |
| * $Revision: 1.15 $ |
| * $Date: 2001/12/15 19:19:24 $ |
| * |
| * ==================================================================== |
| * |
| * The Apache Software License, Version 1.1 |
| * |
| * Copyright (c) 1999-2001 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, if |
| * any, must include the following acknowlegement: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowlegement may appear in the software itself, |
| * if and wherever such third-party acknowlegements normally appear. |
| * |
| * 4. The names "The Jakarta Project", "Commons", and "Apache Software |
| * Foundation" must not be used to endorse or promote products derived |
| * from this software without prior written permission. For written |
| * permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache" |
| * nor may "Apache" appear in their names without prior written |
| * permission of the Apache Group. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| * |
| */ |
| |
| |
| package org.apache.commons.beanutils; |
| |
| |
| import java.beans.BeanInfo; |
| import java.beans.IndexedPropertyDescriptor; |
| import java.beans.IntrospectionException; |
| import java.beans.Introspector; |
| import java.beans.PropertyDescriptor; |
| import java.lang.reflect.Array; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Map; |
| import org.apache.commons.collections.FastHashMap; |
| |
| |
| /** |
| * Utility methods for using Java Reflection APIs to facilitate generic |
| * property getter and setter operations on Java objects. Much of this |
| * code was originally included in <code>BeanUtils</code>, but has been |
| * separated because of the volume of code involved. |
| * <p> |
| * In general, the objects that are examined and modified using these |
| * methods are expected to conform to the property getter and setter method |
| * naming conventions described in the JavaBeans Specification (Version 1.0.1). |
| * No data type conversions are performed, and there are no usage of any |
| * <code>PropertyEditor</code> classes that have been registered, although |
| * a convenient way to access the registered classes themselves is included. |
| * <p> |
| * For the purposes of this class, five formats for referencing a particular |
| * property value of a bean are defined, with the layout of an identifying |
| * String in parentheses: |
| * <ul> |
| * <li><strong>Simple (<code>name</code>)</strong> - The specified |
| * <code>name</code> identifies an individual property of a particular |
| * JavaBean. The name of the actual getter or setter method to be used |
| * is determined using standard JavaBeans instrospection, so that (unless |
| * overridden by a <code>BeanInfo</code> class, a property named "xyz" |
| * will have a getter method named <code>getXyz()</code> or (for boolean |
| * properties only) <code>isXyz()</code>, and a setter method named |
| * <code>setXyz()</code>.</li> |
| * <li><strong>Nested (<code>name1.name2.name3</code>)</strong> The first |
| * name element is used to select a property getter, as for simple |
| * references above. The object returned for this property is then |
| * consulted, using the same approach, for a property getter for a |
| * property named <code>name2</code>, and so on. The property value that |
| * is ultimately retrieved or modified is the one identified by the |
| * last name element.</li> |
| * <li><strong>Indexed (<code>name[index]</code>)</strong> - The underlying |
| * property value is assumed to be an array, or this JavaBean is assumed |
| * to have indexed property getter and setter methods. The appropriate |
| * (zero-relative) entry in the array is selected.</li> |
| * <li><strong>Mapped (<code>name(key)</code>)</strong> - The JavaBean |
| * is assumed to have an property getter and setter methods with an |
| * additional attribute of type <code>java.lang.String</code>.</li> |
| * <li><strong>Combined (<code>name1.name2[index].name3(key)</code>)</strong> - |
| * Combining mapped, nested, and indexed references is also |
| * supported.</li> |
| * </ul> |
| * |
| * @author Craig R. McClanahan |
| * @author Ralph Schaer |
| * @author Chris Audley |
| * @author Rey François |
| * @author Gregor Raýman |
| * @author Jan Sorensen |
| * @version $Revision: 1.15 $ $Date: 2001/12/15 19:19:24 $ |
| */ |
| |
| public class PropertyUtils { |
| |
| |
| // ----------------------------------------------------- Manifest Constants |
| |
| |
| /** |
| * The delimiter that preceeds the zero-relative subscript for an |
| * indexed reference. |
| */ |
| public static final char INDEXED_DELIM = '['; |
| |
| |
| /** |
| * The delimiter that follows the zero-relative subscript for an |
| * indexed reference. |
| */ |
| public static final char INDEXED_DELIM2 = ']'; |
| |
| |
| /** |
| * The delimiter that preceeds the key of a mapped property. |
| */ |
| public static final char MAPPED_DELIM = '('; |
| |
| |
| /** |
| * The delimiter that follows the key of a mapped property. |
| */ |
| public static final char MAPPED_DELIM2 = ')'; |
| |
| |
| /** |
| * The delimiter that separates the components of a nested reference. |
| */ |
| public static final char NESTED_DELIM = '.'; |
| |
| |
| // ------------------------------------------------------- Static Variables |
| |
| |
| /** |
| * The debugging detail level for this component. |
| */ |
| private static int debug = 0; |
| |
| public static int getDebug() { |
| return (debug); |
| } |
| |
| public static void setDebug(int newDebug) { |
| debug = newDebug; |
| } |
| |
| |
| /** |
| * The cache of PropertyDescriptor arrays for beans we have already |
| * introspected, keyed by the java.lang.Class of this object. |
| */ |
| private static FastHashMap descriptorsCache = null; |
| private static FastHashMap mappedDescriptorsCache = null; |
| static { |
| descriptorsCache = new FastHashMap(); |
| descriptorsCache.setFast(true); |
| mappedDescriptorsCache = new FastHashMap(); |
| mappedDescriptorsCache.setFast(true); |
| } |
| |
| |
| // --------------------------------------------------------- Public Methods |
| |
| |
| /** |
| * Clear any cached property descriptors information for all classes |
| * loaded by any class loaders. This is useful in cases where class |
| * loaders are thrown away to implement class reloading. |
| */ |
| public static void clearDescriptors() { |
| |
| descriptorsCache.clear(); |
| mappedDescriptorsCache.clear(); |
| Introspector.flushCaches(); |
| |
| } |
| |
| |
| /** |
| * Copy property values from the "origin" bean to the "destination" bean |
| * for all cases where the property names are the same (even though the |
| * actual getter and setter methods might have been customized via |
| * <code>BeanInfo</code> classes). No conversions are performed on the |
| * actual property values -- it is assumed that the values retrieved from |
| * the origin bean are assignment-compatible with the types expected by |
| * the destination bean. |
| * |
| * @param dest Destination bean whose properties are modified |
| * @param orig Origin bean whose properties are retrieved |
| * |
| * @exception IllegalAccessException if the caller does not have |
| * access to the property accessor method |
| * @exception IllegalArgumentException if the <code>dest</code> or |
| * <code>orig</code> argument is null |
| * @exception InvocationTargetException if the property accessor method |
| * throws an exception |
| * @exception NoSuchMethodException if an accessor method for this |
| * propety cannot be found |
| */ |
| public static void copyProperties(Object dest, Object orig) |
| throws IllegalAccessException, InvocationTargetException, |
| NoSuchMethodException { |
| |
| if (dest == null) |
| throw new IllegalArgumentException |
| ("No destination bean specified"); |
| if (orig == null) |
| throw new IllegalArgumentException("No origin bean specified"); |
| |
| PropertyDescriptor origDescriptors[] = getPropertyDescriptors(orig); |
| for (int i = 0; i < origDescriptors.length; i++) { |
| String name = origDescriptors[i].getName(); |
| if (getPropertyDescriptor(dest, name) != null) { |
| Object value = getSimpleProperty(orig, name); |
| try { |
| setSimpleProperty(dest, name, value); |
| } catch (NoSuchMethodException e) { |
| ; // Skip non-matching property |
| } |
| } |
| } |
| |
| } |
| |
| |
| /** |
| * Return the entire set of properties for which the specified bean |
| * provides a read method. This map contains the unconverted property |
| * values for all properties for which a read method is provided |
| * (i.e. where the <code>getReadMethod()</code> returns non-null). |
| * |
| * @param bean Bean whose properties are to be extracted |
| * |
| * @exception IllegalAccessException if the caller does not have |
| * access to the property accessor method |
| * @exception IllegalArgumentException if <code>bean</code> is null |
| * @exception InvocationTargetException if the property accessor method |
| * throws an exception |
| * @exception NoSuchMethodException if an accessor method for this |
| * propety cannot be found |
| */ |
| // FIXME - does not account for mapped properties |
| public static Map describe(Object bean) |
| throws IllegalAccessException, InvocationTargetException, |
| NoSuchMethodException { |
| |
| if (bean == null) |
| throw new IllegalArgumentException("No bean specified"); |
| PropertyDescriptor descriptors[] = |
| PropertyUtils.getPropertyDescriptors(bean); |
| Map description = new HashMap(descriptors.length); |
| for (int i = 0; i < descriptors.length; i++) { |
| String name = descriptors[i].getName(); |
| if (descriptors[i].getReadMethod() != null) |
| description.put(name, getProperty(bean, name)); |
| } |
| return (description); |
| |
| } |
| |
| |
| /** |
| * Return the value of the specified indexed property of the specified |
| * bean, with no type conversions. The zero-relative index of the |
| * required value must be included (in square brackets) as a suffix to |
| * the property name, or <code>IllegalArgumentException</code> will be |
| * thrown. |
| * |
| * @param bean Bean whose property is to be extracted |
| * @param name <code>propertyname[index]</code> of the property value |
| * to be extracted |
| * |
| * @exception ArrayIndexOutOfBoundsException if the specified index |
| * is outside the valid range for the underlying array |
| * @exception IllegalAccessException if the caller does not have |
| * access to the property accessor method |
| * @exception IllegalArgumentException if <code>bean</code> or |
| * <code>name</code> is null |
| * @exception InvocationTargetException if the property accessor method |
| * throws an exception |
| * @exception NoSuchMethodException if an accessor method for this |
| * propety cannot be found |
| */ |
| public static Object getIndexedProperty(Object bean, String name) |
| throws IllegalAccessException, InvocationTargetException, |
| NoSuchMethodException { |
| |
| if (bean == null) |
| throw new IllegalArgumentException("No bean specified"); |
| if (name == null) |
| throw new IllegalArgumentException("No name specified"); |
| |
| // Identify the index of the requested individual property |
| int delim = name.indexOf(INDEXED_DELIM); |
| int delim2 = name.indexOf(INDEXED_DELIM2); |
| if ((delim < 0) || (delim2 <= delim)) |
| throw new IllegalArgumentException("Invalid indexed property '" + |
| name + "'"); |
| int index = -1; |
| try { |
| String subscript = name.substring(delim + 1, delim2); |
| index = Integer.parseInt(subscript); |
| } catch (NumberFormatException e) { |
| throw new IllegalArgumentException("Invalid indexed property '" + |
| name + "'"); |
| } |
| name = name.substring(0, delim); |
| |
| // Request the specified indexed property value |
| return (getIndexedProperty(bean, name, index)); |
| |
| } |
| |
| |
| /** |
| * Return the value of the specified indexed property of the specified |
| * bean, with no type conversions. |
| * |
| * @param bean Bean whose property is to be extracted |
| * @param name Simple property name of the property value to be extracted |
| * @param index Index of the property value to be extracted |
| * |
| * @exception ArrayIndexOutOfBoundsException if the specified index |
| * is outside the valid range for the underlying array |
| * @exception IllegalAccessException if the caller does not have |
| * access to the property accessor method |
| * @exception IllegalArgumentException if <code>bean</code> or |
| * <code>name</code> is null |
| * @exception InvocationTargetException if the property accessor method |
| * throws an exception |
| * @exception NoSuchMethodException if an accessor method for this |
| * propety cannot be found |
| */ |
| public static Object getIndexedProperty(Object bean, |
| String name, int index) |
| throws IllegalAccessException, InvocationTargetException, |
| NoSuchMethodException { |
| |
| if (bean == null) |
| throw new IllegalArgumentException("No bean specified"); |
| if (name == null) |
| throw new IllegalArgumentException("No name specified"); |
| |
| // Retrieve the property descriptor for the specified property |
| PropertyDescriptor descriptor = |
| getPropertyDescriptor(bean, name); |
| if (descriptor == null) |
| throw new NoSuchMethodException("Unknown property '" + |
| name + "'"); |
| |
| // Call the indexed getter method if there is one |
| if (descriptor instanceof IndexedPropertyDescriptor) { |
| Method readMethod = ((IndexedPropertyDescriptor) descriptor). |
| getIndexedReadMethod(); |
| if (readMethod != null) { |
| Object subscript[] = new Object[1]; |
| subscript[0] = new Integer(index); |
| try { |
| return (readMethod.invoke(bean, subscript)); |
| } catch (InvocationTargetException e) { |
| if (e.getTargetException() instanceof |
| ArrayIndexOutOfBoundsException) |
| throw (ArrayIndexOutOfBoundsException) |
| e.getTargetException(); |
| else |
| throw e; |
| } |
| } |
| } |
| |
| // Otherwise, the underlying property must be an array |
| Method readMethod = getReadMethod(descriptor); |
| if (readMethod == null) |
| throw new NoSuchMethodException("Property '" + name + |
| "' has no getter method"); |
| |
| // Call the property getter and return the value |
| Object value = readMethod.invoke(bean, new Object[0]); |
| if (!value.getClass().isArray()) |
| throw new IllegalArgumentException("Property '" + name + |
| "' is not indexed"); |
| return (Array.get(value, index)); |
| |
| } |
| |
| |
| /** |
| * Return the value of the specified mapped property of the |
| * specified bean, with no type conversions. The key of the |
| * required value must be included (in brackets) as a suffix to |
| * the property name, or <code>IllegalArgumentException</code> will be |
| * thrown. |
| * |
| * @param bean Bean whose property is to be extracted |
| * @param name <code>propertyname(key)</code> of the property value |
| * to be extracted |
| * |
| * @exception IllegalAccessException if the caller does not have |
| * access to the property accessor method |
| * @exception InvocationTargetException if the property accessor method |
| * throws an exception |
| * @exception NoSuchMethodException if an accessor method for this |
| * propety cannot be found |
| */ |
| public static Object getMappedProperty(Object bean, String name) |
| throws IllegalAccessException, InvocationTargetException, |
| NoSuchMethodException { |
| |
| if (bean == null) |
| throw new IllegalArgumentException("No bean specified"); |
| if (name == null) |
| throw new IllegalArgumentException("No name specified"); |
| |
| // Identify the index of the requested individual property |
| int delim = name.indexOf(MAPPED_DELIM); |
| int delim2 = name.indexOf(MAPPED_DELIM2); |
| if ((delim < 0) || (delim2 <= delim)) |
| throw new IllegalArgumentException |
| ("Invalid mapped property '" + name + "'"); |
| // Isolate the name and the key |
| String key = name.substring(delim + 1, delim2); |
| name = name.substring(0, delim); |
| |
| // Request the specified indexed property value |
| return (getMappedProperty(bean, name, key)); |
| |
| } |
| |
| |
| /** |
| * Return the value of the specified mapped property of the specified |
| * bean, with no type conversions. |
| * |
| * @param bean Bean whose property is to be extracted |
| * @param name Mapped property name of the property value to be extracted |
| * @param key Key of the property value to be extracted |
| * |
| * @exception IllegalAccessException if the caller does not have |
| * access to the property accessor method |
| * @exception InvocationTargetException if the property accessor method |
| * throws an exception |
| * @exception NoSuchMethodException if an accessor method for this |
| * propety cannot be found |
| */ |
| public static Object getMappedProperty(Object bean, |
| String name, String key) |
| throws IllegalAccessException, InvocationTargetException, |
| NoSuchMethodException { |
| |
| if (bean == null) |
| throw new IllegalArgumentException("No bean specified"); |
| if (name == null) |
| throw new IllegalArgumentException("No name specified"); |
| if (key == null) |
| throw new IllegalArgumentException("No key specified"); |
| |
| Object result = null; |
| |
| // Retrieve the property descriptor for the specified property |
| PropertyDescriptor descriptor = getPropertyDescriptor(bean, name); |
| if (descriptor == null) |
| throw new NoSuchMethodException("Unknown property '" + |
| name + "'"); |
| |
| if (descriptor instanceof MappedPropertyDescriptor) { |
| // Call the keyed getter method if there is one |
| Method readMethod = ((MappedPropertyDescriptor)descriptor). |
| getMappedReadMethod(); |
| if (readMethod != null) { |
| Object keyArray[] = new Object[1]; |
| keyArray[0] = key; |
| result = readMethod.invoke(bean, keyArray); |
| } else { |
| throw new NoSuchMethodException("Property '" + name + |
| "' has no mapped getter method"); |
| } |
| } |
| return result; |
| |
| } |
| |
| |
| /** |
| * Return the mapped property descriptors for this bean. |
| * |
| * @param bean Bean to be introspected |
| */ |
| public static FastHashMap getMappedPropertyDescriptors(Object bean) { |
| |
| if (bean == null) |
| return null; |
| |
| // Look up any cached descriptors for this bean class |
| Class beanClass = bean.getClass(); |
| return (FastHashMap) mappedDescriptorsCache.get(beanClass); |
| |
| } |
| |
| |
| /** |
| * Return the value of the (possibly nested) property of the specified |
| * name, for the specified bean, with no type conversions. |
| * |
| * @param bean Bean whose property is to be extracted |
| * @param name Possibly nested name of the property to be extracted |
| * |
| * @exception IllegalAccessException if the caller does not have |
| * access to the property accessor method |
| * @exception IllegalArgumentException if <code>bean</code> or |
| * <code>name</code> is null |
| * @exception IllegalArgumentException if a nested reference to a |
| * property returns null |
| * @exception InvocationTargetException if the property accessor method |
| * throws an exception |
| * @exception NoSuchMethodException if an accessor method for this |
| * propety cannot be found |
| */ |
| public static Object getNestedProperty(Object bean, String name) |
| throws IllegalAccessException, InvocationTargetException, |
| NoSuchMethodException { |
| |
| if (bean == null) |
| throw new IllegalArgumentException("No bean specified"); |
| if (name == null) |
| throw new IllegalArgumentException("No name specified"); |
| |
| int indexOfINDEXED_DELIM = -1; |
| int indexOfMAPPED_DELIM = -1; |
| while (true) { |
| /* |
| int delim = name.indexOf(NESTED_DELIM); |
| if (delim < 0) |
| break; |
| String next = name.substring(0, delim); |
| if (next.indexOf(INDEXED_DELIM) >= 0) |
| bean = getIndexedProperty(bean, next); |
| else |
| bean = getSimpleProperty(bean, next); |
| if (bean == null) |
| throw new IllegalArgumentException |
| ("Null property value for '" + |
| name.substring(0, delim) + "'"); |
| name = name.substring(delim + 1); |
| */ |
| int period = name.indexOf(NESTED_DELIM); |
| if (period < 0) |
| break; |
| String next = name.substring(0, period); |
| indexOfINDEXED_DELIM = next.indexOf(INDEXED_DELIM); |
| indexOfMAPPED_DELIM = next.indexOf(MAPPED_DELIM); |
| if (indexOfMAPPED_DELIM >= 0 && |
| (indexOfINDEXED_DELIM <0 || |
| indexOfMAPPED_DELIM < indexOfINDEXED_DELIM)) |
| bean = getMappedProperty(bean, next); |
| else { |
| if (indexOfINDEXED_DELIM >= 0) |
| bean = getIndexedProperty(bean, next); |
| else |
| bean = getSimpleProperty(bean, next); |
| } |
| if (bean == null) |
| throw new IllegalArgumentException |
| ("Null property value for '" + |
| name.substring(0, period) + "'"); |
| name = name.substring(period + 1); |
| } |
| |
| /* |
| if (name.indexOf(INDEXED_DELIM) >= 0) |
| return (getIndexedProperty(bean, name)); |
| else |
| return (getSimpleProperty(bean, name)); |
| */ |
| indexOfINDEXED_DELIM = name.indexOf(INDEXED_DELIM); |
| indexOfMAPPED_DELIM = name.indexOf(MAPPED_DELIM); |
| |
| if (indexOfMAPPED_DELIM >= 0 && (indexOfINDEXED_DELIM <0 || indexOfMAPPED_DELIM < indexOfINDEXED_DELIM)) |
| bean = getMappedProperty(bean, name); |
| else { |
| if (indexOfINDEXED_DELIM >= 0) |
| bean = getIndexedProperty(bean, name); |
| else |
| bean = getSimpleProperty(bean, name); |
| } |
| return bean; |
| |
| } |
| |
| |
| /** |
| * Return the value of the specified property of the specified bean, |
| * no matter which property reference format is used, with no |
| * type conversions. |
| * |
| * @param bean Bean whose property is to be extracted |
| * @param name Possibly indexed and/or nested name of the property |
| * to be extracted |
| * |
| * @exception IllegalAccessException if the caller does not have |
| * access to the property accessor method |
| * @exception IllegalArgumentException if <code>bean</code> or |
| * <code>name</code> is null |
| * @exception InvocationTargetException if the property accessor method |
| * throws an exception |
| * @exception NoSuchMethodException if an accessor method for this |
| * propety cannot be found |
| */ |
| public static Object getProperty(Object bean, String name) |
| throws IllegalAccessException, InvocationTargetException, |
| NoSuchMethodException { |
| |
| return (getNestedProperty(bean, name)); |
| |
| } |
| |
| |
| /** |
| * Retrieve the property descriptor for the specified property of the |
| * specified bean, or return <code>null</code> if there is no such |
| * descriptor. This method resolves indexed and nested property |
| * references in the same manner as other methods in this class, except |
| * that if the last (or only) name element is indexed, the descriptor |
| * for the last resolved property itself is returned. |
| * |
| * @param bean Bean for which a property descriptor is requested |
| * @param name Possibly indexed and/or nested name of the property for |
| * which a property descriptor is requested |
| * |
| * @exception IllegalAccessException if the caller does not have |
| * access to the property accessor method |
| * @exception IllegalArgumentException if <code>bean</code> or |
| * <code>name</code> is null |
| * @exception IllegalArgumentException if a nested reference to a |
| * property returns null |
| * @exception InvocationTargetException if the property accessor method |
| * throws an exception |
| * @exception NoSuchMethodException if an accessor method for this |
| * propety cannot be found |
| */ |
| public static PropertyDescriptor getPropertyDescriptor(Object bean, |
| String name) |
| throws IllegalAccessException, InvocationTargetException, |
| NoSuchMethodException { |
| |
| if (bean == null) |
| throw new IllegalArgumentException("No bean specified"); |
| if (name == null) |
| throw new IllegalArgumentException("No name specified"); |
| |
| // Resolve nested references |
| while (true) { |
| int period = name.indexOf(NESTED_DELIM); |
| if (period < 0) |
| break; |
| String next = name.substring(0, period); |
| /* |
| if (next.indexOf(INDEXED_DELIM) >= 0) |
| bean = getIndexedProperty(bean, next); |
| else |
| bean = getSimpleProperty(bean, next); |
| if (bean == null) |
| throw new IllegalArgumentException |
| ("Null property value for '" + |
| name.substring(0, period) + "'"); |
| name = name.substring(period + 1); |
| */ |
| int indexOfINDEXED_DELIM = next.indexOf(INDEXED_DELIM); |
| int indexOfMAPPED_DELIM = next.indexOf(MAPPED_DELIM); |
| if (indexOfMAPPED_DELIM >= 0 && (indexOfINDEXED_DELIM <0 || indexOfMAPPED_DELIM < indexOfINDEXED_DELIM)) |
| bean = getMappedProperty(bean, next); |
| else { |
| if (indexOfINDEXED_DELIM >= 0) |
| bean = getIndexedProperty(bean, next); |
| else |
| bean = getSimpleProperty(bean, next); |
| } |
| if (bean == null) |
| throw new IllegalArgumentException |
| ("Null property value for '" + |
| name.substring(0, period) + "'"); |
| name = name.substring(period + 1); |
| } |
| |
| // Remove any subscript from the final name value |
| int left = name.indexOf(INDEXED_DELIM); |
| if (left >= 0) |
| name = name.substring(0, left); |
| left = name.indexOf(MAPPED_DELIM); |
| if (left >= 0) |
| name = name.substring(0, left); |
| |
| // Look up and return this property from our cache |
| // creating and adding it to the cache if not found. |
| if ((bean == null) || (name == null)) |
| return (null); |
| |
| PropertyDescriptor descriptors[] = getPropertyDescriptors(bean); |
| if (descriptors != null) { |
| for (int i = 0; i < descriptors.length; i++) { |
| if (name.equals(descriptors[i].getName())) |
| return (descriptors[i]); |
| } |
| } |
| |
| PropertyDescriptor result = null; |
| FastHashMap mappedDescriptors = |
| getMappedPropertyDescriptors(bean); |
| if (mappedDescriptors != null) { |
| result = (PropertyDescriptor) mappedDescriptors.get(name); |
| } |
| if (result==null) { |
| // not found, try to create it |
| try { |
| result = |
| new MappedPropertyDescriptor(name, bean.getClass()); |
| } catch (IntrospectionException ie) { |
| } |
| } |
| if (result!=null) { |
| if (mappedDescriptors==null) { |
| mappedDescriptors = new FastHashMap(); |
| mappedDescriptors.setFast(true); |
| mappedDescriptorsCache.put |
| (bean.getClass(), mappedDescriptors); |
| } |
| mappedDescriptors.put(name, result); |
| } |
| return result; |
| |
| } |
| |
| |
| /** |
| * Retrieve the property descriptors for the specified bean, introspecting |
| * and caching them the first time a particular bean class is encountered. |
| * |
| * @param bean Bean for which property descriptors are requested |
| * |
| * @exception IllegalArgumentException if <code>bean</code> is null |
| */ |
| public static PropertyDescriptor[] getPropertyDescriptors(Object bean) { |
| |
| if (bean == null) |
| throw new IllegalArgumentException("No bean specified"); |
| |
| // Look up any cached descriptors for this bean class |
| Class beanClass = bean.getClass(); |
| PropertyDescriptor descriptors[] = null; |
| descriptors = |
| (PropertyDescriptor[]) descriptorsCache.get(beanClass); |
| if (descriptors != null) |
| return (descriptors); |
| |
| // Introspect the bean and cache the generated descriptors |
| BeanInfo beanInfo = null; |
| try { |
| beanInfo = Introspector.getBeanInfo(bean.getClass()); |
| } catch (IntrospectionException e) { |
| return (new PropertyDescriptor[0]); |
| } |
| descriptors = beanInfo.getPropertyDescriptors(); |
| if (descriptors == null) |
| descriptors = new PropertyDescriptor[0]; |
| descriptorsCache.put(beanClass, descriptors); |
| return (descriptors); |
| |
| } |
| |
| |
| /** |
| * Return the Java Class repesenting the property editor class that has |
| * been registered for this property (if any). This method follows the |
| * same name resolution rules used by <code>getPropertyDescriptor()</code>, |
| * so if the last element of a name reference is indexed, the property |
| * editor for the underlying property's class is returned. |
| * <p> |
| * Note that <code>null</code> will be returned if there is no property, |
| * or if there is no registered property editor class. Because this |
| * return value is ambiguous, you should determine the existence of the |
| * property itself by other means. |
| * |
| * @param bean Bean for which a property descriptor is requested |
| * @param name Possibly indexed and/or nested name of the property for |
| * which a property descriptor is requested |
| * |
| * @exception IllegalAccessException if the caller does not have |
| * access to the property accessor method |
| * @exception IllegalArgumentException if <code>bean</code> or |
| * <code>name</code> is null |
| * @exception IllegalArgumentException if a nested reference to a |
| * property returns null |
| * @exception InvocationTargetException if the property accessor method |
| * throws an exception |
| * @exception NoSuchMethodException if an accessor method for this |
| * propety cannot be found |
| */ |
| public static Class getPropertyEditorClass(Object bean, String name) |
| throws IllegalAccessException, InvocationTargetException, |
| NoSuchMethodException { |
| |
| if (bean == null) |
| throw new IllegalArgumentException("No bean specified"); |
| if (name == null) |
| throw new IllegalArgumentException("No name specified"); |
| |
| PropertyDescriptor descriptor = |
| getPropertyDescriptor(bean, name); |
| if (descriptor != null) |
| return (descriptor.getPropertyEditorClass()); |
| else |
| return (null); |
| |
| } |
| |
| |
| /** |
| * Return the Java Class representing the property type of the specified |
| * property, or <code>null</code> if there is no such property for the |
| * specified bean. This method follows the same name resolution rules |
| * used by <code>getPropertyDescriptor()</code>, so if the last element |
| * of a name reference is indexed, the type of the property itself will |
| * be returned. If the last (or only) element has no property with the |
| * specified name, <code>null</code> is returned. |
| * |
| * @param bean Bean for which a property descriptor is requested |
| * @param name Possibly indexed and/or nested name of the property for |
| * which a property descriptor is requested |
| * |
| * @exception IllegalAccessException if the caller does not have |
| * access to the property accessor method |
| * @exception IllegalArgumentException if <code>bean</code> or |
| * <code>name</code> is null |
| * @exception IllegalArgumentException if a nested reference to a |
| * property returns null |
| * @exception InvocationTargetException if the property accessor method |
| * throws an exception |
| * @exception NoSuchMethodException if an accessor method for this |
| * propety cannot be found |
| */ |
| public static Class getPropertyType(Object bean, String name) |
| throws IllegalAccessException, InvocationTargetException, |
| NoSuchMethodException { |
| |
| if (bean == null) |
| throw new IllegalArgumentException("No bean specified"); |
| if (name == null) |
| throw new IllegalArgumentException("No name specified"); |
| |
| PropertyDescriptor descriptor = |
| getPropertyDescriptor(bean, name); |
| if (descriptor == null) |
| return (null); |
| else if (descriptor instanceof IndexedPropertyDescriptor) |
| return (((IndexedPropertyDescriptor) descriptor). |
| getIndexedPropertyType()); |
| else |
| return (descriptor.getPropertyType()); |
| |
| } |
| |
| |
| /** |
| * Return an accessible property getter method for this property, |
| * if there is one; otherwise return <code>null</code>. |
| * |
| * @param descriptor Property descriptor to return a getter for |
| */ |
| public static Method getReadMethod(PropertyDescriptor descriptor) { |
| |
| return (MethodUtils.getAccessibleMethod(descriptor.getReadMethod())); |
| |
| } |
| |
| |
| /** |
| * Return the value of the specified simple property of the specified |
| * bean, with no type conversions. |
| * |
| * @param bean Bean whose property is to be extracted |
| * @param name Name of the property to be extracted |
| * |
| * @exception IllegalAccessException if the caller does not have |
| * access to the property accessor method |
| * @exception IllegalArgumentException if <code>bean</code> or |
| * <code>name</code> is null |
| * @exception IllegalArgumentException if the property name |
| * is nested or indexed |
| * @exception InvocationTargetException if the property accessor method |
| * throws an exception |
| * @exception NoSuchMethodException if an accessor method for this |
| * propety cannot be found |
| */ |
| public static Object getSimpleProperty(Object bean, String name) |
| throws IllegalAccessException, InvocationTargetException, |
| NoSuchMethodException { |
| |
| if (bean == null) |
| throw new IllegalArgumentException("No bean specified"); |
| if (name == null) |
| throw new IllegalArgumentException("No name specified"); |
| |
| // Validate the syntax of the property name |
| if (name.indexOf(NESTED_DELIM) >= 0) |
| throw new IllegalArgumentException |
| ("Nested property names are not allowed"); |
| else if (name.indexOf(INDEXED_DELIM) >= 0) |
| throw new IllegalArgumentException |
| ("Indexed property names are not allowed"); |
| else if (name.indexOf(MAPPED_DELIM) >= 0) |
| throw new IllegalArgumentException |
| ("Mapped property names are not allowed"); |
| |
| // Retrieve the property getter method for the specified property |
| PropertyDescriptor descriptor = |
| getPropertyDescriptor(bean, name); |
| if (descriptor == null) |
| throw new NoSuchMethodException("Unknown property '" + |
| name + "'"); |
| Method readMethod = getReadMethod(descriptor); |
| if (readMethod == null) |
| throw new NoSuchMethodException("Property '" + name + |
| "' has no getter method"); |
| |
| // Call the property getter and return the value |
| Object value = readMethod.invoke(bean, new Object[0]); |
| return (value); |
| |
| } |
| |
| |
| /** |
| * Return an accessible property setter method for this property, |
| * if there is one; otherwise return <code>null</code>. |
| * |
| * @param descriptor Property descriptor to return a setter for |
| */ |
| public static Method getWriteMethod(PropertyDescriptor descriptor) { |
| |
| return (MethodUtils.getAccessibleMethod(descriptor.getWriteMethod())); |
| |
| } |
| |
| |
| /** |
| * Set the value of the specified indexed property of the specified |
| * bean, with no type conversions. The zero-relative index of the |
| * required value must be included (in square brackets) as a suffix to |
| * the property name, or <code>IllegalArgumentException</code> will be |
| * thrown. |
| * |
| * @param bean Bean whose property is to be modified |
| * @param name <code>propertyname[index]</code> of the property value |
| * to be modified |
| * @param value Value to which the specified property element |
| * should be set |
| * |
| * @exception ArrayIndexOutOfBoundsException if the specified index |
| * is outside the valid range for the underlying array |
| * @exception IllegalAccessException if the caller does not have |
| * access to the property accessor method |
| * @exception IllegalArgumentException if <code>bean</code> or |
| * <code>name</code> is null |
| * @exception InvocationTargetException if the property accessor method |
| * throws an exception |
| * @exception NoSuchMethodException if an accessor method for this |
| * propety cannot be found |
| */ |
| public static void setIndexedProperty(Object bean, String name, |
| Object value) |
| throws IllegalAccessException, InvocationTargetException, |
| NoSuchMethodException { |
| |
| if (bean == null) |
| throw new IllegalArgumentException("No bean specified"); |
| if (name == null) |
| throw new IllegalArgumentException("No name specified"); |
| |
| // Identify the index of the requested individual property |
| int delim = name.indexOf(INDEXED_DELIM); |
| int delim2 = name.indexOf(INDEXED_DELIM2); |
| if ((delim < 0) || (delim2 <= delim)) |
| throw new IllegalArgumentException("Invalid indexed property '" + |
| name + "'"); |
| int index = -1; |
| try { |
| String subscript = name.substring(delim + 1, delim2); |
| index = Integer.parseInt(subscript); |
| } catch (NumberFormatException e) { |
| throw new IllegalArgumentException("Invalid indexed property '" + |
| name + "'"); |
| } |
| name = name.substring(0, delim); |
| |
| // Set the specified indexed property value |
| setIndexedProperty(bean, name, index, value); |
| |
| } |
| |
| |
| /** |
| * Set the value of the specified indexed property of the specified |
| * bean, with no type conversions. |
| * |
| * @param bean Bean whose property is to be set |
| * @param name Simple property name of the property value to be set |
| * @param index Index of the property value to be set |
| * @param value Value to which the indexed property element is to be set |
| * |
| * @exception ArrayIndexOutOfBoundsException if the specified index |
| * is outside the valid range for the underlying array |
| * @exception IllegalAccessException if the caller does not have |
| * access to the property accessor method |
| * @exception IllegalArgumentException if <code>bean</code> or |
| * <code>name</code> is null |
| * @exception InvocationTargetException if the property accessor method |
| * throws an exception |
| * @exception NoSuchMethodException if an accessor method for this |
| * propety cannot be found |
| */ |
| public static void setIndexedProperty(Object bean, String name, |
| int index, Object value) |
| throws IllegalAccessException, InvocationTargetException, |
| NoSuchMethodException { |
| |
| if (bean == null) |
| throw new IllegalArgumentException("No bean specified"); |
| if (name == null) |
| throw new IllegalArgumentException("No name specified"); |
| |
| // Retrieve the property descriptor for the specified property |
| PropertyDescriptor descriptor = |
| getPropertyDescriptor(bean, name); |
| if (descriptor == null) |
| throw new NoSuchMethodException("Unknown property '" + |
| name + "'"); |
| |
| // Call the indexed setter method if there is one |
| if (descriptor instanceof IndexedPropertyDescriptor) { |
| Method writeMethod = ((IndexedPropertyDescriptor) descriptor). |
| getIndexedWriteMethod(); |
| if (writeMethod != null) { |
| Object subscript[] = new Object[2]; |
| subscript[0] = new Integer(index); |
| subscript[1] = value; |
| try { |
| writeMethod.invoke(bean, subscript); |
| } catch (InvocationTargetException e) { |
| if (e.getTargetException() instanceof |
| ArrayIndexOutOfBoundsException) |
| throw (ArrayIndexOutOfBoundsException) |
| e.getTargetException(); |
| else |
| throw e; |
| } |
| return; |
| } |
| } |
| |
| // Otherwise, the underlying property must be an array |
| Method readMethod = descriptor.getReadMethod(); |
| if (readMethod == null) |
| throw new NoSuchMethodException("Property '" + name + |
| "' has no getter method"); |
| |
| // Call the property getter to get the array |
| Object array = readMethod.invoke(bean, new Object[0]); |
| if (!array.getClass().isArray()) |
| throw new IllegalArgumentException("Property '" + name + |
| "' is not indexed"); |
| |
| // Modify the specified value |
| Array.set(array, index, value); |
| |
| } |
| |
| |
| /** |
| * Set the value of the specified mapped property of the |
| * specified bean, with no type conversions. The key of the |
| * value to set must be included (in brackets) as a suffix to |
| * the property name, or <code>IllegalArgumentException</code> will be |
| * thrown. |
| * |
| * @param bean Bean whose property is to be set |
| * @param name <code>propertyname(key)</code> of the property value |
| * to be set |
| * @param value The property value to be set |
| * |
| * @exception IllegalAccessException if the caller does not have |
| * access to the property accessor method |
| * @exception InvocationTargetException if the property accessor method |
| * throws an exception |
| * @exception NoSuchMethodException if an accessor method for this |
| * propety cannot be found |
| */ |
| public static void setMappedProperty(Object bean, String name, |
| Object value) |
| throws IllegalAccessException, InvocationTargetException, |
| NoSuchMethodException { |
| |
| if (bean == null) |
| throw new IllegalArgumentException("No bean specified"); |
| if (name == null) |
| throw new IllegalArgumentException("No name specified"); |
| |
| // Identify the index of the requested individual property |
| int delim = name.indexOf(MAPPED_DELIM); |
| int delim2 = name.indexOf(MAPPED_DELIM2); |
| if ((delim < 0) || (delim2 <= delim)) |
| throw new IllegalArgumentException |
| ("Invalid mapped property '" + name + "'"); |
| // Isolate the name and the key |
| String key = name.substring(delim + 1, delim2); |
| name = name.substring(0, delim); |
| |
| // Request the specified indexed property value |
| setMappedProperty(bean, name, key, value); |
| |
| } |
| |
| |
| /** |
| * Set the value of the specified mapped property of the specified |
| * bean, with no type conversions. |
| * |
| * @param bean Bean whose property is to be set |
| * @param name Mapped property name of the property value to be set |
| * @param key Key of the property value to be set |
| * @param value The property value to be set |
| * |
| * @exception IllegalAccessException if the caller does not have |
| * access to the property accessor method |
| * @exception InvocationTargetException if the property accessor method |
| * throws an exception |
| * @exception NoSuchMethodException if an accessor method for this |
| * propety cannot be found |
| */ |
| public static void setMappedProperty(Object bean, String name, |
| String key, Object value) |
| throws IllegalAccessException, InvocationTargetException, |
| NoSuchMethodException { |
| |
| if (bean == null) |
| throw new IllegalArgumentException("No bean specified"); |
| if (name == null) |
| throw new IllegalArgumentException("No name specified"); |
| if (key == null) |
| throw new IllegalArgumentException("No key specified"); |
| |
| // Retrieve the property descriptor for the specified property |
| PropertyDescriptor descriptor = |
| getPropertyDescriptor(bean, name); |
| if (descriptor == null) |
| throw new NoSuchMethodException("Unknown property '" + |
| name + "'"); |
| |
| if (descriptor instanceof MappedPropertyDescriptor) { |
| // Call the keyed setter method if there is one |
| Method mappedWriteMethod = |
| ((MappedPropertyDescriptor)descriptor). |
| getMappedWriteMethod(); |
| if (mappedWriteMethod != null) { |
| Object params[] = new Object[2]; |
| params[0] = key; |
| params[1] = value; |
| mappedWriteMethod.invoke(bean, params); |
| } else { |
| throw new NoSuchMethodException |
| ("Property '" + name + |
| "' has no mapped setter method"); |
| } |
| } |
| |
| } |
| |
| |
| /** |
| * Set the value of the (possibly nested) property of the specified |
| * name, for the specified bean, with no type conversions. |
| * |
| * @param bean Bean whose property is to be modified |
| * @param name Possibly nested name of the property to be modified |
| * @param value Value to which the property is to be set |
| * |
| * @exception IllegalAccessException if the caller does not have |
| * access to the property accessor method |
| * @exception IllegalArgumentException if <code>bean</code> or |
| * <code>name</code> is null |
| * @exception IllegalArgumentException if a nested reference to a |
| * property returns null |
| * @exception InvocationTargetException if the property accessor method |
| * throws an exception |
| * @exception NoSuchMethodException if an accessor method for this |
| * propety cannot be found |
| */ |
| public static void setNestedProperty(Object bean, |
| String name, Object value) |
| throws IllegalAccessException, InvocationTargetException, |
| NoSuchMethodException { |
| |
| if (bean == null) |
| throw new IllegalArgumentException("No bean specified"); |
| if (name == null) |
| throw new IllegalArgumentException("No name specified"); |
| |
| int indexOfINDEXED_DELIM = -1; |
| int indexOfMAPPED_DELIM = -1; |
| while (true) { |
| int delim = name.indexOf(NESTED_DELIM); |
| if (delim < 0) |
| break; |
| String next = name.substring(0, delim); |
| /* |
| if (next.indexOf(INDEXED_DELIM) >= 0) |
| bean = getIndexedProperty(bean, next); |
| else |
| bean = getSimpleProperty(bean, next); |
| if (bean == null) |
| throw new IllegalArgumentException |
| ("Null property value for '" + |
| name.substring(0, delim) + "'"); |
| name = name.substring(delim + 1); |
| */ |
| indexOfINDEXED_DELIM = next.indexOf(INDEXED_DELIM); |
| indexOfMAPPED_DELIM = next.indexOf(MAPPED_DELIM); |
| if (indexOfMAPPED_DELIM >= 0 && (indexOfINDEXED_DELIM <0 || indexOfMAPPED_DELIM < indexOfINDEXED_DELIM)) |
| bean = getMappedProperty(bean, next); |
| else { |
| if (indexOfINDEXED_DELIM >= 0) |
| bean = getIndexedProperty(bean, next); |
| else |
| bean = getSimpleProperty(bean, next); |
| } |
| if (bean == null) |
| throw new IllegalArgumentException |
| ("Null property value for '" + |
| name.substring(0, delim) + "'"); |
| name = name.substring(delim + 1); |
| } |
| |
| /* |
| if (name.indexOf(INDEXED_DELIM) >= 0) |
| setIndexedProperty(bean, name, value); |
| else |
| setSimpleProperty(bean, name, value); |
| */ |
| indexOfINDEXED_DELIM = name.indexOf(INDEXED_DELIM); |
| indexOfMAPPED_DELIM = name.indexOf(MAPPED_DELIM); |
| |
| if (indexOfMAPPED_DELIM >= 0 && |
| (indexOfINDEXED_DELIM <0 || |
| indexOfMAPPED_DELIM < indexOfINDEXED_DELIM)) |
| setMappedProperty(bean, name, value); |
| else { |
| if (indexOfINDEXED_DELIM >= 0) |
| setIndexedProperty(bean, name, value); |
| else |
| setSimpleProperty(bean, name, value); |
| } |
| |
| } |
| |
| |
| /** |
| * Set the value of the specified property of the specified bean, |
| * no matter which property reference format is used, with no |
| * type conversions. |
| * |
| * @param bean Bean whose property is to be modified |
| * @param name Possibly indexed and/or nested name of the property |
| * to be modified |
| * @param value Value to which this property is to be set |
| * |
| * @exception IllegalAccessException if the caller does not have |
| * access to the property accessor method |
| * @exception IllegalArgumentException if <code>bean</code> or |
| * <code>name</code> is null |
| * @exception InvocationTargetException if the property accessor method |
| * throws an exception |
| * @exception NoSuchMethodException if an accessor method for this |
| * propety cannot be found |
| */ |
| public static void setProperty(Object bean, String name, Object value) |
| throws IllegalAccessException, InvocationTargetException, |
| NoSuchMethodException { |
| |
| setNestedProperty(bean, name, value); |
| |
| } |
| |
| |
| /** |
| * Set the value of the specified simple property of the specified bean, |
| * with no type conversions. |
| * |
| * @param bean Bean whose property is to be modified |
| * @param name Name of the property to be modified |
| * @param value Value to which the property should be set |
| * |
| * @exception IllegalAccessException if the caller does not have |
| * access to the property accessor method |
| * @exception IllegalArgumentException if <code>bean</code> or |
| * <code>name</code> is null |
| * @exception IllegalArgumentException if the property name is |
| * nested or indexed |
| * @exception InvocationTargetException if the property accessor method |
| * throws an exception |
| * @exception NoSuchMethodException if an accessor method for this |
| * propety cannot be found |
| */ |
| public static void setSimpleProperty(Object bean, |
| String name, Object value) |
| throws IllegalAccessException, InvocationTargetException, |
| NoSuchMethodException { |
| |
| if (bean == null) |
| throw new IllegalArgumentException("No bean specified"); |
| if (name == null) |
| throw new IllegalArgumentException("No name specified"); |
| |
| // Validate the syntax of the property name |
| if (name.indexOf(NESTED_DELIM) >= 0) |
| throw new IllegalArgumentException |
| ("Nested property names are not allowed"); |
| else if (name.indexOf(INDEXED_DELIM) >= 0) |
| throw new IllegalArgumentException |
| ("Indexed property names are not allowed"); |
| else if (name.indexOf(MAPPED_DELIM) >= 0) |
| throw new IllegalArgumentException |
| ("Mapped property names are not allowed"); |
| |
| // Retrieve the property setter method for the specified property |
| PropertyDescriptor descriptor = |
| getPropertyDescriptor(bean, name); |
| if (descriptor == null) |
| throw new NoSuchMethodException("Unknown property '" + |
| name + "'"); |
| Method writeMethod = getWriteMethod(descriptor); |
| if (writeMethod == null) |
| throw new NoSuchMethodException("Property '" + name + |
| "' has no setter method"); |
| |
| // Call the property setter method |
| Object values[] = new Object[1]; |
| values[0] = value; |
| writeMethod.invoke(bean, values); |
| |
| } |
| |
| |
| } |