Source Code Cross Referenced for PropertyUtilsBean.java in  » Library » Apache-commons-beanutils-1.8.0-BETA-src » org » apache » commons » beanutils » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Library » Apache commons beanutils 1.8.0 BETA src » org.apache.commons.beanutils 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Licensed to the Apache Software Foundation (ASF) under one or more
0003:         * contributor license agreements.  See the NOTICE file distributed with
0004:         * this work for additional information regarding copyright ownership.
0005:         * The ASF licenses this file to You under the Apache License, Version 2.0
0006:         * (the "License"); you may not use this file except in compliance with
0007:         * the License.  You may obtain a copy of the License at
0008:         *
0009:         *      http://www.apache.org/licenses/LICENSE-2.0
0010:         *
0011:         * Unless required by applicable law or agreed to in writing, software
0012:         * distributed under the License is distributed on an "AS IS" BASIS,
0013:         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014:         * See the License for the specific language governing permissions and
0015:         * limitations under the License.
0016:         */
0017:
0018:        package org.apache.commons.beanutils;
0019:
0020:        import java.beans.BeanInfo;
0021:        import java.beans.IndexedPropertyDescriptor;
0022:        import java.beans.IntrospectionException;
0023:        import java.beans.Introspector;
0024:        import java.beans.PropertyDescriptor;
0025:        import java.lang.reflect.Array;
0026:        import java.lang.reflect.InvocationTargetException;
0027:        import java.lang.reflect.Method;
0028:        import java.util.HashMap;
0029:        import java.util.Iterator;
0030:        import java.util.List;
0031:        import java.util.Map;
0032:
0033:        import org.apache.commons.beanutils.expression.DefaultResolver;
0034:        import org.apache.commons.beanutils.expression.Resolver;
0035:        import org.apache.commons.collections.FastHashMap;
0036:        import org.apache.commons.logging.Log;
0037:        import org.apache.commons.logging.LogFactory;
0038:
0039:        /**
0040:         * Utility methods for using Java Reflection APIs to facilitate generic
0041:         * property getter and setter operations on Java objects.  Much of this
0042:         * code was originally included in <code>BeanUtils</code>, but has been
0043:         * separated because of the volume of code involved.
0044:         * <p>
0045:         * In general, the objects that are examined and modified using these
0046:         * methods are expected to conform to the property getter and setter method
0047:         * naming conventions described in the JavaBeans Specification (Version 1.0.1).
0048:         * No data type conversions are performed, and there are no usage of any
0049:         * <code>PropertyEditor</code> classes that have been registered, although
0050:         * a convenient way to access the registered classes themselves is included.
0051:         * <p>
0052:         * For the purposes of this class, five formats for referencing a particular
0053:         * property value of a bean are defined, with the <i>default</i> layout of an
0054:         * identifying String in parentheses. However the notation for these formats
0055:         * and how they are resolved is now (since BeanUtils 1.8.0) controlled by
0056:         * the configured {@link Resolver} implementation:
0057:         * <ul>
0058:         * <li><strong>Simple (<code>name</code>)</strong> - The specified
0059:         *     <code>name</code> identifies an individual property of a particular
0060:         *     JavaBean.  The name of the actual getter or setter method to be used
0061:         *     is determined using standard JavaBeans instrospection, so that (unless
0062:         *     overridden by a <code>BeanInfo</code> class, a property named "xyz"
0063:         *     will have a getter method named <code>getXyz()</code> or (for boolean
0064:         *     properties only) <code>isXyz()</code>, and a setter method named
0065:         *     <code>setXyz()</code>.</li>
0066:         * <li><strong>Nested (<code>name1.name2.name3</code>)</strong> The first
0067:         *     name element is used to select a property getter, as for simple
0068:         *     references above.  The object returned for this property is then
0069:         *     consulted, using the same approach, for a property getter for a
0070:         *     property named <code>name2</code>, and so on.  The property value that
0071:         *     is ultimately retrieved or modified is the one identified by the
0072:         *     last name element.</li>
0073:         * <li><strong>Indexed (<code>name[index]</code>)</strong> - The underlying
0074:         *     property value is assumed to be an array, or this JavaBean is assumed
0075:         *     to have indexed property getter and setter methods.  The appropriate
0076:         *     (zero-relative) entry in the array is selected.  <code>List</code>
0077:         *     objects are now also supported for read/write.  You simply need to define
0078:         *     a getter that returns the <code>List</code></li>
0079:         * <li><strong>Mapped (<code>name(key)</code>)</strong> - The JavaBean
0080:         *     is assumed to have an property getter and setter methods with an
0081:         *     additional attribute of type <code>java.lang.String</code>.</li>
0082:         * <li><strong>Combined (<code>name1.name2[index].name3(key)</code>)</strong> -
0083:         *     Combining mapped, nested, and indexed references is also
0084:         *     supported.</li>
0085:         * </ul>
0086:         *
0087:         * @author Craig R. McClanahan
0088:         * @author Ralph Schaer
0089:         * @author Chris Audley
0090:         * @author Rey Francois
0091:         * @author Gregor Rayman
0092:         * @author Jan Sorensen
0093:         * @author Scott Sanders
0094:         * @author Erik Meade
0095:         * @version $Revision: 557008 $ $Date: 2007-07-17 19:27:26 +0100 (Tue, 17 Jul 2007) $
0096:         * @see Resolver
0097:         * @see PropertyUtils
0098:         * @since 1.7
0099:         */
0100:
0101:        public class PropertyUtilsBean {
0102:
0103:            private Resolver resolver = new DefaultResolver();
0104:
0105:            // --------------------------------------------------------- Class Methods
0106:
0107:            /**
0108:             * Return the PropertyUtils bean instance.
0109:             * @return The PropertyUtils bean instance
0110:             */
0111:            protected static PropertyUtilsBean getInstance() {
0112:                return BeanUtilsBean.getInstance().getPropertyUtils();
0113:            }
0114:
0115:            // --------------------------------------------------------- Variables
0116:
0117:            /**
0118:             * The cache of PropertyDescriptor arrays for beans we have already
0119:             * introspected, keyed by the java.lang.Class of this object.
0120:             */
0121:            private FastHashMap descriptorsCache = null;
0122:            private FastHashMap mappedDescriptorsCache = null;
0123:            private static final Class[] EMPTY_CLASS_PARAMETERS = new Class[0];
0124:            private static final Class[] LIST_CLASS_PARAMETER = new Class[] { java.util.List.class };
0125:
0126:            /** Log instance */
0127:            private Log log = LogFactory.getLog(PropertyUtils.class);
0128:
0129:            // ---------------------------------------------------------- Constructors
0130:
0131:            /** Base constructor */
0132:            public PropertyUtilsBean() {
0133:                descriptorsCache = new FastHashMap();
0134:                descriptorsCache.setFast(true);
0135:                mappedDescriptorsCache = new FastHashMap();
0136:                mappedDescriptorsCache.setFast(true);
0137:            }
0138:
0139:            // --------------------------------------------------------- Public Methods
0140:
0141:            /**
0142:             * Return the configured {@link Resolver} implementation used by BeanUtils.
0143:             * <p>
0144:             * The {@link Resolver} handles the <i>property name</i>
0145:             * expressions and the implementation in use effectively
0146:             * controls the dialect of the <i>expression language</i>
0147:             * that BeanUtils recongnises.
0148:             * <p>
0149:             * {@link DefaultResolver} is the default implementation used.
0150:             *
0151:             * @return resolver The property expression resolver.
0152:             */
0153:            public Resolver getResolver() {
0154:                return resolver;
0155:            }
0156:
0157:            /**
0158:             * Configure the {@link Resolver} implementation used by BeanUtils.
0159:             * <p>
0160:             * The {@link Resolver} handles the <i>property name</i>
0161:             * expressions and the implementation in use effectively
0162:             * controls the dialect of the <i>expression language</i>
0163:             * that BeanUtils recongnises.
0164:             * <p>
0165:             * {@link DefaultResolver} is the default implementation used.
0166:             *
0167:             * @param resolver The property expression resolver.
0168:             */
0169:            public void setResolver(Resolver resolver) {
0170:                if (resolver == null) {
0171:                    this .resolver = new DefaultResolver();
0172:                } else {
0173:                    this .resolver = resolver;
0174:                }
0175:            }
0176:
0177:            /**
0178:             * Clear any cached property descriptors information for all classes
0179:             * loaded by any class loaders.  This is useful in cases where class
0180:             * loaders are thrown away to implement class reloading.
0181:             */
0182:            public void clearDescriptors() {
0183:
0184:                descriptorsCache.clear();
0185:                mappedDescriptorsCache.clear();
0186:                Introspector.flushCaches();
0187:
0188:            }
0189:
0190:            /**
0191:             * <p>Copy property values from the "origin" bean to the "destination" bean
0192:             * for all cases where the property names are the same (even though the
0193:             * actual getter and setter methods might have been customized via
0194:             * <code>BeanInfo</code> classes).  No conversions are performed on the
0195:             * actual property values -- it is assumed that the values retrieved from
0196:             * the origin bean are assignment-compatible with the types expected by
0197:             * the destination bean.</p>
0198:             *
0199:             * <p>If the origin "bean" is actually a <code>Map</code>, it is assumed
0200:             * to contain String-valued <strong>simple</strong> property names as the keys, pointing
0201:             * at the corresponding property values that will be set in the destination
0202:             * bean.<strong>Note</strong> that this method is intended to perform 
0203:             * a "shallow copy" of the properties and so complex properties 
0204:             * (for example, nested ones) will not be copied.</p>
0205:             * 
0206:             * <p>Note, that this method will not copy a List to a List, or an Object[] 
0207:             * to an Object[]. It's specifically for copying JavaBean properties. </p>
0208:             *
0209:             * @param dest Destination bean whose properties are modified
0210:             * @param orig Origin bean whose properties are retrieved
0211:             *
0212:             * @exception IllegalAccessException if the caller does not have
0213:             *  access to the property accessor method
0214:             * @exception IllegalArgumentException if the <code>dest</code> or
0215:             *  <code>orig</code> argument is null
0216:             * @exception InvocationTargetException if the property accessor method
0217:             *  throws an exception
0218:             * @exception NoSuchMethodException if an accessor method for this
0219:             *  propety cannot be found
0220:             */
0221:            public void copyProperties(Object dest, Object orig)
0222:                    throws IllegalAccessException, InvocationTargetException,
0223:                    NoSuchMethodException {
0224:
0225:                if (dest == null) {
0226:                    throw new IllegalArgumentException(
0227:                            "No destination bean specified");
0228:                }
0229:                if (orig == null) {
0230:                    throw new IllegalArgumentException(
0231:                            "No origin bean specified");
0232:                }
0233:
0234:                if (orig instanceof  DynaBean) {
0235:                    DynaProperty[] origDescriptors = ((DynaBean) orig)
0236:                            .getDynaClass().getDynaProperties();
0237:                    for (int i = 0; i < origDescriptors.length; i++) {
0238:                        String name = origDescriptors[i].getName();
0239:                        if (isReadable(orig, name) && isWriteable(dest, name)) {
0240:                            try {
0241:                                Object value = ((DynaBean) orig).get(name);
0242:                                if (dest instanceof  DynaBean) {
0243:                                    ((DynaBean) dest).set(name, value);
0244:                                } else {
0245:                                    setSimpleProperty(dest, name, value);
0246:                                }
0247:                            } catch (NoSuchMethodException e) {
0248:                                if (log.isDebugEnabled()) {
0249:                                    log.debug("Error writing to '" + name
0250:                                            + "' on class '" + dest.getClass()
0251:                                            + "'", e);
0252:                                }
0253:                            }
0254:                        }
0255:                    }
0256:                } else if (orig instanceof  Map) {
0257:                    Iterator names = ((Map) orig).keySet().iterator();
0258:                    while (names.hasNext()) {
0259:                        String name = (String) names.next();
0260:                        if (isWriteable(dest, name)) {
0261:                            try {
0262:                                Object value = ((Map) orig).get(name);
0263:                                if (dest instanceof  DynaBean) {
0264:                                    ((DynaBean) dest).set(name, value);
0265:                                } else {
0266:                                    setSimpleProperty(dest, name, value);
0267:                                }
0268:                            } catch (NoSuchMethodException e) {
0269:                                if (log.isDebugEnabled()) {
0270:                                    log.debug("Error writing to '" + name
0271:                                            + "' on class '" + dest.getClass()
0272:                                            + "'", e);
0273:                                }
0274:                            }
0275:                        }
0276:                    }
0277:                } else /* if (orig is a standard JavaBean) */{
0278:                    PropertyDescriptor[] origDescriptors = getPropertyDescriptors(orig);
0279:                    for (int i = 0; i < origDescriptors.length; i++) {
0280:                        String name = origDescriptors[i].getName();
0281:                        if (isReadable(orig, name) && isWriteable(dest, name)) {
0282:                            try {
0283:                                Object value = getSimpleProperty(orig, name);
0284:                                if (dest instanceof  DynaBean) {
0285:                                    ((DynaBean) dest).set(name, value);
0286:                                } else {
0287:                                    setSimpleProperty(dest, name, value);
0288:                                }
0289:                            } catch (NoSuchMethodException e) {
0290:                                if (log.isDebugEnabled()) {
0291:                                    log.debug("Error writing to '" + name
0292:                                            + "' on class '" + dest.getClass()
0293:                                            + "'", e);
0294:                                }
0295:                            }
0296:                        }
0297:                    }
0298:                }
0299:
0300:            }
0301:
0302:            /**
0303:             * <p>Return the entire set of properties for which the specified bean
0304:             * provides a read method.  This map contains the unconverted property
0305:             * values for all properties for which a read method is provided
0306:             * (i.e. where the <code>getReadMethod()</code> returns non-null).</p>
0307:             *
0308:             * <p><strong>FIXME</strong> - Does not account for mapped properties.</p>
0309:             *
0310:             * @param bean Bean whose properties are to be extracted
0311:             * @return The set of properties for the bean
0312:             *
0313:             * @exception IllegalAccessException if the caller does not have
0314:             *  access to the property accessor method
0315:             * @exception IllegalArgumentException if <code>bean</code> is null
0316:             * @exception InvocationTargetException if the property accessor method
0317:             *  throws an exception
0318:             * @exception NoSuchMethodException if an accessor method for this
0319:             *  propety cannot be found
0320:             */
0321:            public Map describe(Object bean) throws IllegalAccessException,
0322:                    InvocationTargetException, NoSuchMethodException {
0323:
0324:                if (bean == null) {
0325:                    throw new IllegalArgumentException("No bean specified");
0326:                }
0327:                Map description = new HashMap();
0328:                if (bean instanceof  DynaBean) {
0329:                    DynaProperty[] descriptors = ((DynaBean) bean)
0330:                            .getDynaClass().getDynaProperties();
0331:                    for (int i = 0; i < descriptors.length; i++) {
0332:                        String name = descriptors[i].getName();
0333:                        description.put(name, getProperty(bean, name));
0334:                    }
0335:                } else {
0336:                    PropertyDescriptor[] descriptors = getPropertyDescriptors(bean);
0337:                    for (int i = 0; i < descriptors.length; i++) {
0338:                        String name = descriptors[i].getName();
0339:                        if (descriptors[i].getReadMethod() != null) {
0340:                            description.put(name, getProperty(bean, name));
0341:                        }
0342:                    }
0343:                }
0344:                return (description);
0345:
0346:            }
0347:
0348:            /**
0349:             * Return the value of the specified indexed property of the specified
0350:             * bean, with no type conversions.  The zero-relative index of the
0351:             * required value must be included (in square brackets) as a suffix to
0352:             * the property name, or <code>IllegalArgumentException</code> will be
0353:             * thrown.  In addition to supporting the JavaBeans specification, this
0354:             * method has been extended to support <code>List</code> objects as well.
0355:             *
0356:             * @param bean Bean whose property is to be extracted
0357:             * @param name <code>propertyname[index]</code> of the property value
0358:             *  to be extracted
0359:             * @return the indexed property value
0360:             *
0361:             * @exception IndexOutOfBoundsException if the specified index
0362:             *  is outside the valid range for the underlying array or List
0363:             * @exception IllegalAccessException if the caller does not have
0364:             *  access to the property accessor method
0365:             * @exception IllegalArgumentException if <code>bean</code> or
0366:             *  <code>name</code> is null
0367:             * @exception InvocationTargetException if the property accessor method
0368:             *  throws an exception
0369:             * @exception NoSuchMethodException if an accessor method for this
0370:             *  propety cannot be found
0371:             */
0372:            public Object getIndexedProperty(Object bean, String name)
0373:                    throws IllegalAccessException, InvocationTargetException,
0374:                    NoSuchMethodException {
0375:
0376:                if (bean == null) {
0377:                    throw new IllegalArgumentException("No bean specified");
0378:                }
0379:                if (name == null) {
0380:                    throw new IllegalArgumentException(
0381:                            "No name specified for bean class '"
0382:                                    + bean.getClass() + "'");
0383:                }
0384:
0385:                // Identify the index of the requested individual property
0386:                int index = -1;
0387:                try {
0388:                    index = resolver.getIndex(name);
0389:                } catch (IllegalArgumentException e) {
0390:                    throw new IllegalArgumentException(
0391:                            "Invalid indexed property '" + name
0392:                                    + "' on bean class '" + bean.getClass()
0393:                                    + "' " + e.getMessage());
0394:                }
0395:                if (index < 0) {
0396:                    throw new IllegalArgumentException(
0397:                            "Invalid indexed property '" + name
0398:                                    + "' on bean class '" + bean.getClass()
0399:                                    + "'");
0400:                }
0401:
0402:                // Isolate the name
0403:                name = resolver.getProperty(name);
0404:
0405:                // Request the specified indexed property value
0406:                return (getIndexedProperty(bean, name, index));
0407:
0408:            }
0409:
0410:            /**
0411:             * Return the value of the specified indexed property of the specified
0412:             * bean, with no type conversions.  In addition to supporting the JavaBeans
0413:             * specification, this method has been extended to support
0414:             * <code>List</code> objects as well.
0415:             *
0416:             * @param bean Bean whose property is to be extracted
0417:             * @param name Simple property name of the property value to be extracted
0418:             * @param index Index of the property value to be extracted
0419:             * @return the indexed property value
0420:             *
0421:             * @exception IndexOutOfBoundsException if the specified index
0422:             *  is outside the valid range for the underlying property
0423:             * @exception IllegalAccessException if the caller does not have
0424:             *  access to the property accessor method
0425:             * @exception IllegalArgumentException if <code>bean</code> or
0426:             *  <code>name</code> is null
0427:             * @exception InvocationTargetException if the property accessor method
0428:             *  throws an exception
0429:             * @exception NoSuchMethodException if an accessor method for this
0430:             *  propety cannot be found
0431:             */
0432:            public Object getIndexedProperty(Object bean, String name, int index)
0433:                    throws IllegalAccessException, InvocationTargetException,
0434:                    NoSuchMethodException {
0435:
0436:                if (bean == null) {
0437:                    throw new IllegalArgumentException("No bean specified");
0438:                }
0439:                if (name == null || name.length() == 0) {
0440:                    if (bean.getClass().isArray()) {
0441:                        return Array.get(bean, index);
0442:                    } else if (bean instanceof  List) {
0443:                        return ((List) bean).get(index);
0444:                    }
0445:                }
0446:                if (name == null) {
0447:                    throw new IllegalArgumentException(
0448:                            "No name specified for bean class '"
0449:                                    + bean.getClass() + "'");
0450:                }
0451:
0452:                // Handle DynaBean instances specially
0453:                if (bean instanceof  DynaBean) {
0454:                    DynaProperty descriptor = ((DynaBean) bean).getDynaClass()
0455:                            .getDynaProperty(name);
0456:                    if (descriptor == null) {
0457:                        throw new NoSuchMethodException("Unknown property '"
0458:                                + name + "' on bean class '" + bean.getClass()
0459:                                + "'");
0460:                    }
0461:                    return (((DynaBean) bean).get(name, index));
0462:                }
0463:
0464:                // Retrieve the property descriptor for the specified property
0465:                PropertyDescriptor descriptor = getPropertyDescriptor(bean,
0466:                        name);
0467:                if (descriptor == null) {
0468:                    throw new NoSuchMethodException("Unknown property '" + name
0469:                            + "' on bean class '" + bean.getClass() + "'");
0470:                }
0471:
0472:                // Call the indexed getter method if there is one
0473:                if (descriptor instanceof  IndexedPropertyDescriptor) {
0474:                    Method readMethod = ((IndexedPropertyDescriptor) descriptor)
0475:                            .getIndexedReadMethod();
0476:                    readMethod = MethodUtils.getAccessibleMethod(readMethod);
0477:                    if (readMethod != null) {
0478:                        Object[] subscript = new Object[1];
0479:                        subscript[0] = new Integer(index);
0480:                        try {
0481:                            return (invokeMethod(readMethod, bean, subscript));
0482:                        } catch (InvocationTargetException e) {
0483:                            if (e.getTargetException() instanceof  IndexOutOfBoundsException) {
0484:                                throw (IndexOutOfBoundsException) e
0485:                                        .getTargetException();
0486:                            } else {
0487:                                throw e;
0488:                            }
0489:                        }
0490:                    }
0491:                }
0492:
0493:                // Otherwise, the underlying property must be an array
0494:                Method readMethod = getReadMethod(descriptor);
0495:                if (readMethod == null) {
0496:                    throw new NoSuchMethodException("Property '" + name
0497:                            + "' has no " + "getter method on bean class '"
0498:                            + bean.getClass() + "'");
0499:                }
0500:
0501:                // Call the property getter and return the value
0502:                Object value = invokeMethod(readMethod, bean, new Object[0]);
0503:                if (!value.getClass().isArray()) {
0504:                    if (!(value instanceof  java.util.List)) {
0505:                        throw new IllegalArgumentException("Property '" + name
0506:                                + "' is not indexed on bean class '"
0507:                                + bean.getClass() + "'");
0508:                    } else {
0509:                        //get the List's value
0510:                        return ((java.util.List) value).get(index);
0511:                    }
0512:                } else {
0513:                    //get the array's value
0514:                    return (Array.get(value, index));
0515:                }
0516:
0517:            }
0518:
0519:            /**
0520:             * Return the value of the specified mapped property of the
0521:             * specified bean, with no type conversions.  The key of the
0522:             * required value must be included (in brackets) as a suffix to
0523:             * the property name, or <code>IllegalArgumentException</code> will be
0524:             * thrown.
0525:             *
0526:             * @param bean Bean whose property is to be extracted
0527:             * @param name <code>propertyname(key)</code> of the property value
0528:             *  to be extracted
0529:             * @return the mapped property value
0530:             *
0531:             * @exception IllegalAccessException if the caller does not have
0532:             *  access to the property accessor method
0533:             * @exception InvocationTargetException if the property accessor method
0534:             *  throws an exception
0535:             * @exception NoSuchMethodException if an accessor method for this
0536:             *  propety cannot be found
0537:             */
0538:            public Object getMappedProperty(Object bean, String name)
0539:                    throws IllegalAccessException, InvocationTargetException,
0540:                    NoSuchMethodException {
0541:
0542:                if (bean == null) {
0543:                    throw new IllegalArgumentException("No bean specified");
0544:                }
0545:                if (name == null) {
0546:                    throw new IllegalArgumentException(
0547:                            "No name specified for bean class '"
0548:                                    + bean.getClass() + "'");
0549:                }
0550:
0551:                // Identify the key of the requested individual property
0552:                String key = null;
0553:                try {
0554:                    key = resolver.getKey(name);
0555:                } catch (IllegalArgumentException e) {
0556:                    throw new IllegalArgumentException(
0557:                            "Invalid mapped property '" + name
0558:                                    + "' on bean class '" + bean.getClass()
0559:                                    + "' " + e.getMessage());
0560:                }
0561:                if (key == null) {
0562:                    throw new IllegalArgumentException(
0563:                            "Invalid mapped property '" + name
0564:                                    + "' on bean class '" + bean.getClass()
0565:                                    + "'");
0566:                }
0567:
0568:                // Isolate the name
0569:                name = resolver.getProperty(name);
0570:
0571:                // Request the specified indexed property value
0572:                return (getMappedProperty(bean, name, key));
0573:
0574:            }
0575:
0576:            /**
0577:             * Return the value of the specified mapped property of the specified
0578:             * bean, with no type conversions.
0579:             *
0580:             * @param bean Bean whose property is to be extracted
0581:             * @param name Mapped property name of the property value to be extracted
0582:             * @param key Key of the property value to be extracted
0583:             * @return the mapped property value
0584:             *
0585:             * @exception IllegalAccessException if the caller does not have
0586:             *  access to the property accessor method
0587:             * @exception InvocationTargetException if the property accessor method
0588:             *  throws an exception
0589:             * @exception NoSuchMethodException if an accessor method for this
0590:             *  propety cannot be found
0591:             */
0592:            public Object getMappedProperty(Object bean, String name, String key)
0593:                    throws IllegalAccessException, InvocationTargetException,
0594:                    NoSuchMethodException {
0595:
0596:                if (bean == null) {
0597:                    throw new IllegalArgumentException("No bean specified");
0598:                }
0599:                if (name == null) {
0600:                    throw new IllegalArgumentException(
0601:                            "No name specified for bean class '"
0602:                                    + bean.getClass() + "'");
0603:                }
0604:                if (key == null) {
0605:                    throw new IllegalArgumentException(
0606:                            "No key specified for property '" + name
0607:                                    + "' on bean class " + bean.getClass()
0608:                                    + "'");
0609:                }
0610:
0611:                // Handle DynaBean instances specially
0612:                if (bean instanceof  DynaBean) {
0613:                    DynaProperty descriptor = ((DynaBean) bean).getDynaClass()
0614:                            .getDynaProperty(name);
0615:                    if (descriptor == null) {
0616:                        throw new NoSuchMethodException("Unknown property '"
0617:                                + name + "'+ on bean class '" + bean.getClass()
0618:                                + "'");
0619:                    }
0620:                    return (((DynaBean) bean).get(name, key));
0621:                }
0622:
0623:                Object result = null;
0624:
0625:                // Retrieve the property descriptor for the specified property
0626:                PropertyDescriptor descriptor = getPropertyDescriptor(bean,
0627:                        name);
0628:                if (descriptor == null) {
0629:                    throw new NoSuchMethodException("Unknown property '" + name
0630:                            + "'+ on bean class '" + bean.getClass() + "'");
0631:                }
0632:
0633:                if (descriptor instanceof  MappedPropertyDescriptor) {
0634:                    // Call the keyed getter method if there is one
0635:                    Method readMethod = ((MappedPropertyDescriptor) descriptor)
0636:                            .getMappedReadMethod();
0637:                    readMethod = MethodUtils.getAccessibleMethod(readMethod);
0638:                    if (readMethod != null) {
0639:                        Object[] keyArray = new Object[1];
0640:                        keyArray[0] = key;
0641:                        result = invokeMethod(readMethod, bean, keyArray);
0642:                    } else {
0643:                        throw new NoSuchMethodException(
0644:                                "Property '"
0645:                                        + name
0646:                                        + "' has no mapped getter method on bean class '"
0647:                                        + bean.getClass() + "'");
0648:                    }
0649:                } else {
0650:                    /* means that the result has to be retrieved from a map */
0651:                    Method readMethod = getReadMethod(descriptor);
0652:                    if (readMethod != null) {
0653:                        Object invokeResult = invokeMethod(readMethod, bean,
0654:                                new Object[0]);
0655:                        /* test and fetch from the map */
0656:                        if (invokeResult instanceof  java.util.Map) {
0657:                            result = ((java.util.Map) invokeResult).get(key);
0658:                        }
0659:                    } else {
0660:                        throw new NoSuchMethodException(
0661:                                "Property '"
0662:                                        + name
0663:                                        + "' has no mapped getter method on bean class '"
0664:                                        + bean.getClass() + "'");
0665:                    }
0666:                }
0667:                return result;
0668:
0669:            }
0670:
0671:            /**
0672:             * <p>Return the mapped property descriptors for this bean class.</p>
0673:             *
0674:             * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
0675:             *
0676:             * @param beanClass Bean class to be introspected
0677:             * @return the mapped property descriptors
0678:             * @deprecated This method should not be exposed
0679:             */
0680:            public FastHashMap getMappedPropertyDescriptors(Class beanClass) {
0681:
0682:                if (beanClass == null) {
0683:                    return null;
0684:                }
0685:
0686:                // Look up any cached descriptors for this bean class
0687:                return (FastHashMap) mappedDescriptorsCache.get(beanClass);
0688:
0689:            }
0690:
0691:            /**
0692:             * <p>Return the mapped property descriptors for this bean.</p>
0693:             *
0694:             * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
0695:             *
0696:             * @param bean Bean to be introspected
0697:             * @return the mapped property descriptors
0698:             * @deprecated This method should not be exposed
0699:             */
0700:            public FastHashMap getMappedPropertyDescriptors(Object bean) {
0701:
0702:                if (bean == null) {
0703:                    return null;
0704:                }
0705:                return (getMappedPropertyDescriptors(bean.getClass()));
0706:
0707:            }
0708:
0709:            /**
0710:             * Return the value of the (possibly nested) property of the specified
0711:             * name, for the specified bean, with no type conversions.
0712:             *
0713:             * @param bean Bean whose property is to be extracted
0714:             * @param name Possibly nested name of the property to be extracted
0715:             * @return the nested property value
0716:             *
0717:             * @exception IllegalAccessException if the caller does not have
0718:             *  access to the property accessor method
0719:             * @exception IllegalArgumentException if <code>bean</code> or
0720:             *  <code>name</code> is null
0721:             * @exception NestedNullException if a nested reference to a
0722:             *  property returns null
0723:             * @exception InvocationTargetException 
0724:             * if the property accessor method throws an exception
0725:             * @exception NoSuchMethodException if an accessor method for this
0726:             *  propety cannot be found
0727:             */
0728:            public Object getNestedProperty(Object bean, String name)
0729:                    throws IllegalAccessException, InvocationTargetException,
0730:                    NoSuchMethodException {
0731:
0732:                if (bean == null) {
0733:                    throw new IllegalArgumentException("No bean specified");
0734:                }
0735:                if (name == null) {
0736:                    throw new IllegalArgumentException(
0737:                            "No name specified for bean class '"
0738:                                    + bean.getClass() + "'");
0739:                }
0740:
0741:                // Resolve nested references
0742:                while (resolver.hasNested(name)) {
0743:                    String next = resolver.next(name);
0744:                    Object nestedBean = null;
0745:                    if (bean instanceof  Map) {
0746:                        nestedBean = getPropertyOfMapBean((Map) bean, next);
0747:                    } else if (resolver.isMapped(next)) {
0748:                        nestedBean = getMappedProperty(bean, next);
0749:                    } else if (resolver.isIndexed(next)) {
0750:                        nestedBean = getIndexedProperty(bean, next);
0751:                    } else {
0752:                        nestedBean = getSimpleProperty(bean, next);
0753:                    }
0754:                    if (nestedBean == null) {
0755:                        throw new NestedNullException(
0756:                                "Null property value for '" + name
0757:                                        + "' on bean class '" + bean.getClass()
0758:                                        + "'");
0759:                    }
0760:                    bean = nestedBean;
0761:                    name = resolver.remove(name);
0762:                }
0763:
0764:                if (bean instanceof  Map) {
0765:                    bean = getPropertyOfMapBean((Map) bean, name);
0766:                } else if (resolver.isMapped(name)) {
0767:                    bean = getMappedProperty(bean, name);
0768:                } else if (resolver.isIndexed(name)) {
0769:                    bean = getIndexedProperty(bean, name);
0770:                } else {
0771:                    bean = getSimpleProperty(bean, name);
0772:                }
0773:                return bean;
0774:
0775:            }
0776:
0777:            /**
0778:             * This method is called by getNestedProperty and setNestedProperty to
0779:             * define what it means to get a property from an object which implements
0780:             * Map. See setPropertyOfMapBean for more information.
0781:             *
0782:             * @param bean Map bean
0783:             * @param propertyName The property name
0784:             * @return the property value
0785:             * 
0786:             * @throws IllegalArgumentException when the propertyName is regarded as
0787:             * being invalid.
0788:             * 
0789:             * @throws IllegalAccessException just in case subclasses override this
0790:             * method to try to access real getter methods and find permission is denied.
0791:             * 
0792:             * @throws InvocationTargetException just in case subclasses override this
0793:             * method to try to access real getter methods, and find it throws an
0794:             * exception when invoked.
0795:             * 
0796:             * @throws NoSuchMethodException just in case subclasses override this
0797:             * method to try to access real getter methods, and want to fail if
0798:             * no simple method is available.
0799:             */
0800:            protected Object getPropertyOfMapBean(Map bean, String propertyName)
0801:                    throws IllegalArgumentException, IllegalAccessException,
0802:                    InvocationTargetException, NoSuchMethodException {
0803:
0804:                if (resolver.isMapped(propertyName)) {
0805:                    String name = resolver.getProperty(propertyName);
0806:                    if (name == null || name.length() == 0) {
0807:                        propertyName = resolver.getKey(propertyName);
0808:                    }
0809:                }
0810:
0811:                if (resolver.isIndexed(propertyName)
0812:                        || resolver.isMapped(propertyName)) {
0813:                    throw new IllegalArgumentException(
0814:                            "Indexed or mapped properties are not supported on"
0815:                                    + " objects of type Map: " + propertyName);
0816:                }
0817:
0818:                return bean.get(propertyName);
0819:            }
0820:
0821:            /**
0822:             * Return the value of the specified property of the specified bean,
0823:             * no matter which property reference format is used, with no
0824:             * type conversions.
0825:             *
0826:             * @param bean Bean whose property is to be extracted
0827:             * @param name Possibly indexed and/or nested name of the property
0828:             *  to be extracted
0829:             * @return the property value
0830:             *
0831:             * @exception IllegalAccessException if the caller does not have
0832:             *  access to the property accessor method
0833:             * @exception IllegalArgumentException if <code>bean</code> or
0834:             *  <code>name</code> is null
0835:             * @exception InvocationTargetException if the property accessor method
0836:             *  throws an exception
0837:             * @exception NoSuchMethodException if an accessor method for this
0838:             *  propety cannot be found
0839:             */
0840:            public Object getProperty(Object bean, String name)
0841:                    throws IllegalAccessException, InvocationTargetException,
0842:                    NoSuchMethodException {
0843:
0844:                return (getNestedProperty(bean, name));
0845:
0846:            }
0847:
0848:            /**
0849:             * <p>Retrieve the property descriptor for the specified property of the
0850:             * specified bean, or return <code>null</code> if there is no such
0851:             * descriptor.  This method resolves indexed and nested property
0852:             * references in the same manner as other methods in this class, except
0853:             * that if the last (or only) name element is indexed, the descriptor
0854:             * for the last resolved property itself is returned.</p>
0855:             *
0856:             * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
0857:             *
0858:             * @param bean Bean for which a property descriptor is requested
0859:             * @param name Possibly indexed and/or nested name of the property for
0860:             *  which a property descriptor is requested
0861:             * @return the property descriptor
0862:             *
0863:             * @exception IllegalAccessException if the caller does not have
0864:             *  access to the property accessor method
0865:             * @exception IllegalArgumentException if <code>bean</code> or
0866:             *  <code>name</code> is null
0867:             * @exception IllegalArgumentException if a nested reference to a
0868:             *  property returns null
0869:             * @exception InvocationTargetException if the property accessor method
0870:             *  throws an exception
0871:             * @exception NoSuchMethodException if an accessor method for this
0872:             *  propety cannot be found
0873:             */
0874:            public PropertyDescriptor getPropertyDescriptor(Object bean,
0875:                    String name) throws IllegalAccessException,
0876:                    InvocationTargetException, NoSuchMethodException {
0877:
0878:                if (bean == null) {
0879:                    throw new IllegalArgumentException("No bean specified");
0880:                }
0881:                if (name == null) {
0882:                    throw new IllegalArgumentException(
0883:                            "No name specified for bean class '"
0884:                                    + bean.getClass() + "'");
0885:                }
0886:
0887:                // Resolve nested references
0888:                while (resolver.hasNested(name)) {
0889:                    String next = resolver.next(name);
0890:                    Object nestedBean = null;
0891:                    if (bean instanceof  Map) {
0892:                        nestedBean = getPropertyOfMapBean((Map) bean, next);
0893:                    } else if (resolver.isMapped(next)) {
0894:                        nestedBean = getMappedProperty(bean, next);
0895:                    } else if (resolver.isIndexed(next)) {
0896:                        nestedBean = getIndexedProperty(bean, next);
0897:                    } else {
0898:                        nestedBean = getSimpleProperty(bean, next);
0899:                    }
0900:                    if (nestedBean == null) {
0901:                        throw new NestedNullException(
0902:                                "Null property value for '" + name
0903:                                        + "' on bean class '" + bean.getClass()
0904:                                        + "'");
0905:                    }
0906:                    bean = nestedBean;
0907:                    name = resolver.remove(name);
0908:                }
0909:
0910:                // Remove any subscript from the final name value
0911:                name = resolver.getProperty(name);
0912:
0913:                // Look up and return this property from our cache
0914:                // creating and adding it to the cache if not found.
0915:                if ((bean == null) || (name == null)) {
0916:                    return (null);
0917:                }
0918:
0919:                PropertyDescriptor[] descriptors = getPropertyDescriptors(bean);
0920:                if (descriptors != null) {
0921:
0922:                    for (int i = 0; i < descriptors.length; i++) {
0923:                        if (name.equals(descriptors[i].getName())) {
0924:                            return (descriptors[i]);
0925:                        }
0926:                    }
0927:                }
0928:
0929:                PropertyDescriptor result = null;
0930:                FastHashMap mappedDescriptors = getMappedPropertyDescriptors(bean);
0931:                if (mappedDescriptors == null) {
0932:                    mappedDescriptors = new FastHashMap();
0933:                    mappedDescriptors.setFast(true);
0934:                    mappedDescriptorsCache.put(bean.getClass(),
0935:                            mappedDescriptors);
0936:                }
0937:                result = (PropertyDescriptor) mappedDescriptors.get(name);
0938:                if (result == null) {
0939:                    // not found, try to create it
0940:                    try {
0941:                        result = new MappedPropertyDescriptor(name, bean
0942:                                .getClass());
0943:                    } catch (IntrospectionException ie) {
0944:                        /* Swallow IntrospectionException
0945:                         * TODO: Why?
0946:                         */
0947:                    }
0948:                    if (result != null) {
0949:                        mappedDescriptors.put(name, result);
0950:                    }
0951:                }
0952:
0953:                return result;
0954:
0955:            }
0956:
0957:            /**
0958:             * <p>Retrieve the property descriptors for the specified class,
0959:             * introspecting and caching them the first time a particular bean class
0960:             * is encountered.</p>
0961:             *
0962:             * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
0963:             *
0964:             * @param beanClass Bean class for which property descriptors are requested
0965:             * @return the property descriptors
0966:             *
0967:             * @exception IllegalArgumentException if <code>beanClass</code> is null
0968:             */
0969:            public PropertyDescriptor[] getPropertyDescriptors(Class beanClass) {
0970:
0971:                if (beanClass == null) {
0972:                    throw new IllegalArgumentException(
0973:                            "No bean class specified");
0974:                }
0975:
0976:                // Look up any cached descriptors for this bean class
0977:                PropertyDescriptor[] descriptors = null;
0978:                descriptors = (PropertyDescriptor[]) descriptorsCache
0979:                        .get(beanClass);
0980:                if (descriptors != null) {
0981:                    return (descriptors);
0982:                }
0983:
0984:                // Introspect the bean and cache the generated descriptors
0985:                BeanInfo beanInfo = null;
0986:                try {
0987:                    beanInfo = Introspector.getBeanInfo(beanClass);
0988:                } catch (IntrospectionException e) {
0989:                    return (new PropertyDescriptor[0]);
0990:                }
0991:                descriptors = beanInfo.getPropertyDescriptors();
0992:                if (descriptors == null) {
0993:                    descriptors = new PropertyDescriptor[0];
0994:                }
0995:
0996:                // ----------------- Workaround for Bug 28358 --------- START ------------------
0997:                //
0998:                // The following code fixes an issue where IndexedPropertyDescriptor behaves
0999:                // Differently in different versions of the JDK for 'indexed' properties which
1000:                // use java.util.List (rather than an array).
1001:                //
1002:                // If you have a Bean with the following getters/setters for an indexed property:
1003:                //
1004:                //     public List getFoo()
1005:                //     public Object getFoo(int index)
1006:                //     public void setFoo(List foo)
1007:                //     public void setFoo(int index, Object foo)
1008:                //
1009:                // then the IndexedPropertyDescriptor's getReadMethod() and getWriteMethod()
1010:                // behave as follows:
1011:                //
1012:                //     JDK 1.3.1_04: returns valid Method objects from these methods.
1013:                //     JDK 1.4.2_05: returns null from these methods.
1014:                //
1015:                for (int i = 0; i < descriptors.length; i++) {
1016:                    if (descriptors[i] instanceof  IndexedPropertyDescriptor) {
1017:                        IndexedPropertyDescriptor descriptor = (IndexedPropertyDescriptor) descriptors[i];
1018:                        String propName = descriptor.getName().substring(0, 1)
1019:                                .toUpperCase()
1020:                                + descriptor.getName().substring(1);
1021:
1022:                        if (descriptor.getReadMethod() == null) {
1023:                            String methodName = descriptor
1024:                                    .getIndexedReadMethod() != null ? descriptor
1025:                                    .getIndexedReadMethod().getName()
1026:                                    : "get" + propName;
1027:                            Method readMethod = MethodUtils
1028:                                    .getMatchingAccessibleMethod(beanClass,
1029:                                            methodName, EMPTY_CLASS_PARAMETERS);
1030:                            if (readMethod != null) {
1031:                                try {
1032:                                    descriptor.setReadMethod(readMethod);
1033:                                } catch (Exception e) {
1034:                                    log
1035:                                            .error(
1036:                                                    "Error setting indexed property read method",
1037:                                                    e);
1038:                                }
1039:                            }
1040:                        }
1041:                        if (descriptor.getWriteMethod() == null) {
1042:                            String methodName = descriptor
1043:                                    .getIndexedWriteMethod() != null ? descriptor
1044:                                    .getIndexedWriteMethod().getName()
1045:                                    : "set" + propName;
1046:                            Method writeMethod = MethodUtils
1047:                                    .getMatchingAccessibleMethod(beanClass,
1048:                                            methodName, LIST_CLASS_PARAMETER);
1049:                            if (writeMethod == null) {
1050:                                Method[] methods = beanClass.getMethods();
1051:                                for (int j = 0; j < methods.length; j++) {
1052:                                    if (methods[j].getName().equals(methodName)) {
1053:                                        Class[] parameterTypes = methods[j]
1054:                                                .getParameterTypes();
1055:                                        if (parameterTypes.length == 1
1056:                                                && List.class
1057:                                                        .isAssignableFrom(parameterTypes[0])) {
1058:                                            writeMethod = methods[j];
1059:                                            break;
1060:                                        }
1061:                                    }
1062:                                }
1063:                            }
1064:                            if (writeMethod != null) {
1065:                                try {
1066:                                    descriptor.setWriteMethod(writeMethod);
1067:                                } catch (Exception e) {
1068:                                    log
1069:                                            .error(
1070:                                                    "Error setting indexed property write method",
1071:                                                    e);
1072:                                }
1073:                            }
1074:                        }
1075:                    }
1076:                }
1077:                // ----------------- Workaround for Bug 28358 ---------- END -------------------
1078:
1079:                descriptorsCache.put(beanClass, descriptors);
1080:                return (descriptors);
1081:
1082:            }
1083:
1084:            /**
1085:             * <p>Retrieve the property descriptors for the specified bean,
1086:             * introspecting and caching them the first time a particular bean class
1087:             * is encountered.</p>
1088:             *
1089:             * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
1090:             *
1091:             * @param bean Bean for which property descriptors are requested
1092:             * @return the property descriptors
1093:             *
1094:             * @exception IllegalArgumentException if <code>bean</code> is null
1095:             */
1096:            public PropertyDescriptor[] getPropertyDescriptors(Object bean) {
1097:
1098:                if (bean == null) {
1099:                    throw new IllegalArgumentException("No bean specified");
1100:                }
1101:                return (getPropertyDescriptors(bean.getClass()));
1102:
1103:            }
1104:
1105:            /**
1106:             * <p>Return the Java Class repesenting the property editor class that has
1107:             * been registered for this property (if any).  This method follows the
1108:             * same name resolution rules used by <code>getPropertyDescriptor()</code>,
1109:             * so if the last element of a name reference is indexed, the property
1110:             * editor for the underlying property's class is returned.</p>
1111:             *
1112:             * <p>Note that <code>null</code> will be returned if there is no property,
1113:             * or if there is no registered property editor class.  Because this
1114:             * return value is ambiguous, you should determine the existence of the
1115:             * property itself by other means.</p>
1116:             *
1117:             * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
1118:             *
1119:             * @param bean Bean for which a property descriptor is requested
1120:             * @param name Possibly indexed and/or nested name of the property for
1121:             *  which a property descriptor is requested
1122:             * @return the property editor class
1123:             *
1124:             * @exception IllegalAccessException if the caller does not have
1125:             *  access to the property accessor method
1126:             * @exception IllegalArgumentException if <code>bean</code> or
1127:             *  <code>name</code> is null
1128:             * @exception IllegalArgumentException if a nested reference to a
1129:             *  property returns null
1130:             * @exception InvocationTargetException if the property accessor method
1131:             *  throws an exception
1132:             * @exception NoSuchMethodException if an accessor method for this
1133:             *  propety cannot be found
1134:             */
1135:            public Class getPropertyEditorClass(Object bean, String name)
1136:                    throws IllegalAccessException, InvocationTargetException,
1137:                    NoSuchMethodException {
1138:
1139:                if (bean == null) {
1140:                    throw new IllegalArgumentException("No bean specified");
1141:                }
1142:                if (name == null) {
1143:                    throw new IllegalArgumentException(
1144:                            "No name specified for bean class '"
1145:                                    + bean.getClass() + "'");
1146:                }
1147:
1148:                PropertyDescriptor descriptor = getPropertyDescriptor(bean,
1149:                        name);
1150:                if (descriptor != null) {
1151:                    return (descriptor.getPropertyEditorClass());
1152:                } else {
1153:                    return (null);
1154:                }
1155:
1156:            }
1157:
1158:            /**
1159:             * Return the Java Class representing the property type of the specified
1160:             * property, or <code>null</code> if there is no such property for the
1161:             * specified bean.  This method follows the same name resolution rules
1162:             * used by <code>getPropertyDescriptor()</code>, so if the last element
1163:             * of a name reference is indexed, the type of the property itself will
1164:             * be returned.  If the last (or only) element has no property with the
1165:             * specified name, <code>null</code> is returned.
1166:             *
1167:             * @param bean Bean for which a property descriptor is requested
1168:             * @param name Possibly indexed and/or nested name of the property for
1169:             *  which a property descriptor is requested
1170:             * @return The property type
1171:             *
1172:             * @exception IllegalAccessException if the caller does not have
1173:             *  access to the property accessor method
1174:             * @exception IllegalArgumentException if <code>bean</code> or
1175:             *  <code>name</code> is null
1176:             * @exception IllegalArgumentException if a nested reference to a
1177:             *  property returns null
1178:             * @exception InvocationTargetException if the property accessor method
1179:             *  throws an exception
1180:             * @exception NoSuchMethodException if an accessor method for this
1181:             *  propety cannot be found
1182:             */
1183:            public Class getPropertyType(Object bean, String name)
1184:                    throws IllegalAccessException, InvocationTargetException,
1185:                    NoSuchMethodException {
1186:
1187:                if (bean == null) {
1188:                    throw new IllegalArgumentException("No bean specified");
1189:                }
1190:                if (name == null) {
1191:                    throw new IllegalArgumentException(
1192:                            "No name specified for bean class '"
1193:                                    + bean.getClass() + "'");
1194:                }
1195:
1196:                // Special handling for DynaBeans
1197:                if (bean instanceof  DynaBean) {
1198:                    DynaProperty descriptor = ((DynaBean) bean).getDynaClass()
1199:                            .getDynaProperty(name);
1200:                    if (descriptor == null) {
1201:                        return (null);
1202:                    }
1203:                    Class type = descriptor.getType();
1204:                    if (type == null) {
1205:                        return (null);
1206:                    } else if (type.isArray()) {
1207:                        return (type.getComponentType());
1208:                    } else {
1209:                        return (type);
1210:                    }
1211:                }
1212:
1213:                PropertyDescriptor descriptor = getPropertyDescriptor(bean,
1214:                        name);
1215:                if (descriptor == null) {
1216:                    return (null);
1217:                } else if (descriptor instanceof  IndexedPropertyDescriptor) {
1218:                    return (((IndexedPropertyDescriptor) descriptor)
1219:                            .getIndexedPropertyType());
1220:                } else if (descriptor instanceof  MappedPropertyDescriptor) {
1221:                    return (((MappedPropertyDescriptor) descriptor)
1222:                            .getMappedPropertyType());
1223:                } else {
1224:                    return (descriptor.getPropertyType());
1225:                }
1226:
1227:            }
1228:
1229:            /**
1230:             * <p>Return an accessible property getter method for this property,
1231:             * if there is one; otherwise return <code>null</code>.</p>
1232:             *
1233:             * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
1234:             *
1235:             * @param descriptor Property descriptor to return a getter for
1236:             * @return The read method
1237:             */
1238:            public Method getReadMethod(PropertyDescriptor descriptor) {
1239:
1240:                return (MethodUtils.getAccessibleMethod(descriptor
1241:                        .getReadMethod()));
1242:
1243:            }
1244:
1245:            /**
1246:             * Return the value of the specified simple property of the specified
1247:             * bean, with no type conversions.
1248:             *
1249:             * @param bean Bean whose property is to be extracted
1250:             * @param name Name of the property to be extracted
1251:             * @return The property value
1252:             *
1253:             * @exception IllegalAccessException if the caller does not have
1254:             *  access to the property accessor method
1255:             * @exception IllegalArgumentException if <code>bean</code> or
1256:             *  <code>name</code> is null
1257:             * @exception IllegalArgumentException if the property name
1258:             *  is nested or indexed
1259:             * @exception InvocationTargetException if the property accessor method
1260:             *  throws an exception
1261:             * @exception NoSuchMethodException if an accessor method for this
1262:             *  propety cannot be found
1263:             */
1264:            public Object getSimpleProperty(Object bean, String name)
1265:                    throws IllegalAccessException, InvocationTargetException,
1266:                    NoSuchMethodException {
1267:
1268:                if (bean == null) {
1269:                    throw new IllegalArgumentException("No bean specified");
1270:                }
1271:                if (name == null) {
1272:                    throw new IllegalArgumentException(
1273:                            "No name specified for bean class '"
1274:                                    + bean.getClass() + "'");
1275:                }
1276:
1277:                // Validate the syntax of the property name
1278:                if (resolver.hasNested(name)) {
1279:                    throw new IllegalArgumentException(
1280:                            "Nested property names are not allowed: Property '"
1281:                                    + name + "' on bean class '"
1282:                                    + bean.getClass() + "'");
1283:                } else if (resolver.isIndexed(name)) {
1284:                    throw new IllegalArgumentException(
1285:                            "Indexed property names are not allowed: Property '"
1286:                                    + name + "' on bean class '"
1287:                                    + bean.getClass() + "'");
1288:                } else if (resolver.isMapped(name)) {
1289:                    throw new IllegalArgumentException(
1290:                            "Mapped property names are not allowed: Property '"
1291:                                    + name + "' on bean class '"
1292:                                    + bean.getClass() + "'");
1293:                }
1294:
1295:                // Handle DynaBean instances specially
1296:                if (bean instanceof  DynaBean) {
1297:                    DynaProperty descriptor = ((DynaBean) bean).getDynaClass()
1298:                            .getDynaProperty(name);
1299:                    if (descriptor == null) {
1300:                        throw new NoSuchMethodException("Unknown property '"
1301:                                + name + "' on dynaclass '"
1302:                                + ((DynaBean) bean).getDynaClass() + "'");
1303:                    }
1304:                    return (((DynaBean) bean).get(name));
1305:                }
1306:
1307:                // Retrieve the property getter method for the specified property
1308:                PropertyDescriptor descriptor = getPropertyDescriptor(bean,
1309:                        name);
1310:                if (descriptor == null) {
1311:                    throw new NoSuchMethodException("Unknown property '" + name
1312:                            + "' on class '" + bean.getClass() + "'");
1313:                }
1314:                Method readMethod = getReadMethod(descriptor);
1315:                if (readMethod == null) {
1316:                    throw new NoSuchMethodException("Property '" + name
1317:                            + "' has no getter method in class '"
1318:                            + bean.getClass() + "'");
1319:                }
1320:
1321:                // Call the property getter and return the value
1322:                Object value = invokeMethod(readMethod, bean, new Object[0]);
1323:                return (value);
1324:
1325:            }
1326:
1327:            /**
1328:             * <p>Return an accessible property setter method for this property,
1329:             * if there is one; otherwise return <code>null</code>.</p>
1330:             *
1331:             * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
1332:             *
1333:             * @param descriptor Property descriptor to return a setter for
1334:             * @return The write method
1335:             */
1336:            public Method getWriteMethod(PropertyDescriptor descriptor) {
1337:
1338:                return (MethodUtils.getAccessibleMethod(descriptor
1339:                        .getWriteMethod()));
1340:
1341:            }
1342:
1343:            /**
1344:             * <p>Return <code>true</code> if the specified property name identifies
1345:             * a readable property on the specified bean; otherwise, return
1346:             * <code>false</code>.
1347:             *
1348:             * @param bean Bean to be examined (may be a {@link DynaBean}
1349:             * @param name Property name to be evaluated
1350:             * @return <code>true</code> if the property is readable,
1351:             * otherwise <code>false</code>
1352:             *
1353:             * @exception IllegalArgumentException if <code>bean</code>
1354:             *  or <code>name</code> is <code>null</code>
1355:             *
1356:             * @since BeanUtils 1.6
1357:             */
1358:            public boolean isReadable(Object bean, String name) {
1359:
1360:                // Validate method parameters
1361:                if (bean == null) {
1362:                    throw new IllegalArgumentException("No bean specified");
1363:                }
1364:                if (name == null) {
1365:                    throw new IllegalArgumentException(
1366:                            "No name specified for bean class '"
1367:                                    + bean.getClass() + "'");
1368:                }
1369:
1370:                // Treat WrapDynaBean as special case - may be a write-only property
1371:                // (see Jira issue# BEANUTILS-61)
1372:                if (bean instanceof  WrapDynaBean) {
1373:                    bean = ((WrapDynaBean) bean).getInstance();
1374:                }
1375:
1376:                // Return the requested result
1377:                if (bean instanceof  DynaBean) {
1378:                    // All DynaBean properties are readable
1379:                    return (((DynaBean) bean).getDynaClass().getDynaProperty(
1380:                            name) != null);
1381:                } else {
1382:                    try {
1383:                        PropertyDescriptor desc = getPropertyDescriptor(bean,
1384:                                name);
1385:                        if (desc != null) {
1386:                            Method readMethod = getReadMethod(desc);
1387:                            if (readMethod == null) {
1388:                                if (desc instanceof  IndexedPropertyDescriptor) {
1389:                                    readMethod = ((IndexedPropertyDescriptor) desc)
1390:                                            .getIndexedReadMethod();
1391:                                } else if (desc instanceof  MappedPropertyDescriptor) {
1392:                                    readMethod = ((MappedPropertyDescriptor) desc)
1393:                                            .getMappedReadMethod();
1394:                                }
1395:                                readMethod = MethodUtils
1396:                                        .getAccessibleMethod(readMethod);
1397:                            }
1398:                            return (readMethod != null);
1399:                        } else {
1400:                            return (false);
1401:                        }
1402:                    } catch (IllegalAccessException e) {
1403:                        return (false);
1404:                    } catch (InvocationTargetException e) {
1405:                        return (false);
1406:                    } catch (NoSuchMethodException e) {
1407:                        return (false);
1408:                    }
1409:                }
1410:
1411:            }
1412:
1413:            /**
1414:             * <p>Return <code>true</code> if the specified property name identifies
1415:             * a writeable property on the specified bean; otherwise, return
1416:             * <code>false</code>.
1417:             *
1418:             * @param bean Bean to be examined (may be a {@link DynaBean}
1419:             * @param name Property name to be evaluated
1420:             * @return <code>true</code> if the property is writeable,
1421:             * otherwise <code>false</code>
1422:             *
1423:             * @exception IllegalArgumentException if <code>bean</code>
1424:             *  or <code>name</code> is <code>null</code>
1425:             *
1426:             * @since BeanUtils 1.6
1427:             */
1428:            public boolean isWriteable(Object bean, String name) {
1429:
1430:                // Validate method parameters
1431:                if (bean == null) {
1432:                    throw new IllegalArgumentException("No bean specified");
1433:                }
1434:                if (name == null) {
1435:                    throw new IllegalArgumentException(
1436:                            "No name specified for bean class '"
1437:                                    + bean.getClass() + "'");
1438:                }
1439:
1440:                // Treat WrapDynaBean as special case - may be a read-only property
1441:                // (see Jira issue# BEANUTILS-61)
1442:                if (bean instanceof  WrapDynaBean) {
1443:                    bean = ((WrapDynaBean) bean).getInstance();
1444:                }
1445:
1446:                // Return the requested result
1447:                if (bean instanceof  DynaBean) {
1448:                    // All DynaBean properties are writeable
1449:                    return (((DynaBean) bean).getDynaClass().getDynaProperty(
1450:                            name) != null);
1451:                } else {
1452:                    try {
1453:                        PropertyDescriptor desc = getPropertyDescriptor(bean,
1454:                                name);
1455:                        if (desc != null) {
1456:                            Method writeMethod = getWriteMethod(desc);
1457:                            if (writeMethod == null) {
1458:                                if (desc instanceof  IndexedPropertyDescriptor) {
1459:                                    writeMethod = ((IndexedPropertyDescriptor) desc)
1460:                                            .getIndexedWriteMethod();
1461:                                } else if (desc instanceof  MappedPropertyDescriptor) {
1462:                                    writeMethod = ((MappedPropertyDescriptor) desc)
1463:                                            .getMappedWriteMethod();
1464:                                }
1465:                                writeMethod = MethodUtils
1466:                                        .getAccessibleMethod(writeMethod);
1467:                            }
1468:                            return (writeMethod != null);
1469:                        } else {
1470:                            return (false);
1471:                        }
1472:                    } catch (IllegalAccessException e) {
1473:                        return (false);
1474:                    } catch (InvocationTargetException e) {
1475:                        return (false);
1476:                    } catch (NoSuchMethodException e) {
1477:                        return (false);
1478:                    }
1479:                }
1480:
1481:            }
1482:
1483:            /**
1484:             * Set the value of the specified indexed property of the specified
1485:             * bean, with no type conversions.  The zero-relative index of the
1486:             * required value must be included (in square brackets) as a suffix to
1487:             * the property name, or <code>IllegalArgumentException</code> will be
1488:             * thrown.  In addition to supporting the JavaBeans specification, this
1489:             * method has been extended to support <code>List</code> objects as well.
1490:             *
1491:             * @param bean Bean whose property is to be modified
1492:             * @param name <code>propertyname[index]</code> of the property value
1493:             *  to be modified
1494:             * @param value Value to which the specified property element
1495:             *  should be set
1496:             *
1497:             * @exception IndexOutOfBoundsException if the specified index
1498:             *  is outside the valid range for the underlying property
1499:             * @exception IllegalAccessException if the caller does not have
1500:             *  access to the property accessor method
1501:             * @exception IllegalArgumentException if <code>bean</code> or
1502:             *  <code>name</code> is null
1503:             * @exception InvocationTargetException if the property accessor method
1504:             *  throws an exception
1505:             * @exception NoSuchMethodException if an accessor method for this
1506:             *  propety cannot be found
1507:             */
1508:            public void setIndexedProperty(Object bean, String name,
1509:                    Object value) throws IllegalAccessException,
1510:                    InvocationTargetException, NoSuchMethodException {
1511:
1512:                if (bean == null) {
1513:                    throw new IllegalArgumentException("No bean specified");
1514:                }
1515:                if (name == null) {
1516:                    throw new IllegalArgumentException(
1517:                            "No name specified for bean class '"
1518:                                    + bean.getClass() + "'");
1519:                }
1520:
1521:                // Identify the index of the requested individual property
1522:                int index = -1;
1523:                try {
1524:                    index = resolver.getIndex(name);
1525:                } catch (IllegalArgumentException e) {
1526:                    throw new IllegalArgumentException(
1527:                            "Invalid indexed property '" + name
1528:                                    + "' on bean class '" + bean.getClass()
1529:                                    + "'");
1530:                }
1531:                if (index < 0) {
1532:                    throw new IllegalArgumentException(
1533:                            "Invalid indexed property '" + name
1534:                                    + "' on bean class '" + bean.getClass()
1535:                                    + "'");
1536:                }
1537:
1538:                // Isolate the name
1539:                name = resolver.getProperty(name);
1540:
1541:                // Set the specified indexed property value
1542:                setIndexedProperty(bean, name, index, value);
1543:
1544:            }
1545:
1546:            /**
1547:             * Set the value of the specified indexed property of the specified
1548:             * bean, with no type conversions.  In addition to supporting the JavaBeans
1549:             * specification, this method has been extended to support
1550:             * <code>List</code> objects as well.
1551:             *
1552:             * @param bean Bean whose property is to be set
1553:             * @param name Simple property name of the property value to be set
1554:             * @param index Index of the property value to be set
1555:             * @param value Value to which the indexed property element is to be set
1556:             *
1557:             * @exception IndexOutOfBoundsException if the specified index
1558:             *  is outside the valid range for the underlying property
1559:             * @exception IllegalAccessException if the caller does not have
1560:             *  access to the property accessor method
1561:             * @exception IllegalArgumentException if <code>bean</code> or
1562:             *  <code>name</code> is null
1563:             * @exception InvocationTargetException if the property accessor method
1564:             *  throws an exception
1565:             * @exception NoSuchMethodException if an accessor method for this
1566:             *  propety cannot be found
1567:             */
1568:            public void setIndexedProperty(Object bean, String name, int index,
1569:                    Object value) throws IllegalAccessException,
1570:                    InvocationTargetException, NoSuchMethodException {
1571:
1572:                if (bean == null) {
1573:                    throw new IllegalArgumentException("No bean specified");
1574:                }
1575:                if (name == null || name.length() == 0) {
1576:                    if (bean.getClass().isArray()) {
1577:                        Array.set(bean, index, value);
1578:                        return;
1579:                    } else if (bean instanceof  List) {
1580:                        ((List) bean).set(index, value);
1581:                        return;
1582:                    }
1583:                }
1584:                if (name == null) {
1585:                    throw new IllegalArgumentException(
1586:                            "No name specified for bean class '"
1587:                                    + bean.getClass() + "'");
1588:                }
1589:
1590:                // Handle DynaBean instances specially
1591:                if (bean instanceof  DynaBean) {
1592:                    DynaProperty descriptor = ((DynaBean) bean).getDynaClass()
1593:                            .getDynaProperty(name);
1594:                    if (descriptor == null) {
1595:                        throw new NoSuchMethodException("Unknown property '"
1596:                                + name + "' on bean class '" + bean.getClass()
1597:                                + "'");
1598:                    }
1599:                    ((DynaBean) bean).set(name, index, value);
1600:                    return;
1601:                }
1602:
1603:                // Retrieve the property descriptor for the specified property
1604:                PropertyDescriptor descriptor = getPropertyDescriptor(bean,
1605:                        name);
1606:                if (descriptor == null) {
1607:                    throw new NoSuchMethodException("Unknown property '" + name
1608:                            + "' on bean class '" + bean.getClass() + "'");
1609:                }
1610:
1611:                // Call the indexed setter method if there is one
1612:                if (descriptor instanceof  IndexedPropertyDescriptor) {
1613:                    Method writeMethod = ((IndexedPropertyDescriptor) descriptor)
1614:                            .getIndexedWriteMethod();
1615:                    writeMethod = MethodUtils.getAccessibleMethod(writeMethod);
1616:                    if (writeMethod != null) {
1617:                        Object[] subscript = new Object[2];
1618:                        subscript[0] = new Integer(index);
1619:                        subscript[1] = value;
1620:                        try {
1621:                            if (log.isTraceEnabled()) {
1622:                                String valueClassName = value == null ? "<null>"
1623:                                        : value.getClass().getName();
1624:                                log.trace("setSimpleProperty: Invoking method "
1625:                                        + writeMethod + " with index=" + index
1626:                                        + ", value=" + value + " (class "
1627:                                        + valueClassName + ")");
1628:                            }
1629:                            invokeMethod(writeMethod, bean, subscript);
1630:                        } catch (InvocationTargetException e) {
1631:                            if (e.getTargetException() instanceof  IndexOutOfBoundsException) {
1632:                                throw (IndexOutOfBoundsException) e
1633:                                        .getTargetException();
1634:                            } else {
1635:                                throw e;
1636:                            }
1637:                        }
1638:                        return;
1639:                    }
1640:                }
1641:
1642:                // Otherwise, the underlying property must be an array or a list
1643:                Method readMethod = getReadMethod(descriptor);
1644:                if (readMethod == null) {
1645:                    throw new NoSuchMethodException("Property '" + name
1646:                            + "' has no getter method on bean class '"
1647:                            + bean.getClass() + "'");
1648:                }
1649:
1650:                // Call the property getter to get the array or list
1651:                Object array = invokeMethod(readMethod, bean, new Object[0]);
1652:                if (!array.getClass().isArray()) {
1653:                    if (array instanceof  List) {
1654:                        // Modify the specified value in the List
1655:                        ((List) array).set(index, value);
1656:                    } else {
1657:                        throw new IllegalArgumentException("Property '" + name
1658:                                + "' is not indexed on bean class '"
1659:                                + bean.getClass() + "'");
1660:                    }
1661:                } else {
1662:                    // Modify the specified value in the array
1663:                    Array.set(array, index, value);
1664:                }
1665:
1666:            }
1667:
1668:            /**
1669:             * Set the value of the specified mapped property of the
1670:             * specified bean, with no type conversions.  The key of the
1671:             * value to set must be included (in brackets) as a suffix to
1672:             * the property name, or <code>IllegalArgumentException</code> will be
1673:             * thrown.
1674:             *
1675:             * @param bean Bean whose property is to be set
1676:             * @param name <code>propertyname(key)</code> of the property value
1677:             *  to be set
1678:             * @param value The property value to be set
1679:             *
1680:             * @exception IllegalAccessException if the caller does not have
1681:             *  access to the property accessor method
1682:             * @exception InvocationTargetException if the property accessor method
1683:             *  throws an exception
1684:             * @exception NoSuchMethodException if an accessor method for this
1685:             *  propety cannot be found
1686:             */
1687:            public void setMappedProperty(Object bean, String name, Object value)
1688:                    throws IllegalAccessException, InvocationTargetException,
1689:                    NoSuchMethodException {
1690:
1691:                if (bean == null) {
1692:                    throw new IllegalArgumentException("No bean specified");
1693:                }
1694:                if (name == null) {
1695:                    throw new IllegalArgumentException(
1696:                            "No name specified for bean class '"
1697:                                    + bean.getClass() + "'");
1698:                }
1699:
1700:                // Identify the key of the requested individual property
1701:                String key = null;
1702:                try {
1703:                    key = resolver.getKey(name);
1704:                } catch (IllegalArgumentException e) {
1705:                    throw new IllegalArgumentException(
1706:                            "Invalid mapped property '" + name
1707:                                    + "' on bean class '" + bean.getClass()
1708:                                    + "'");
1709:                }
1710:                if (key == null) {
1711:                    throw new IllegalArgumentException(
1712:                            "Invalid mapped property '" + name
1713:                                    + "' on bean class '" + bean.getClass()
1714:                                    + "'");
1715:                }
1716:
1717:                // Isolate the name
1718:                name = resolver.getProperty(name);
1719:
1720:                // Request the specified indexed property value
1721:                setMappedProperty(bean, name, key, value);
1722:
1723:            }
1724:
1725:            /**
1726:             * Set the value of the specified mapped property of the specified
1727:             * bean, with no type conversions.
1728:             *
1729:             * @param bean Bean whose property is to be set
1730:             * @param name Mapped property name of the property value to be set
1731:             * @param key Key of the property value to be set
1732:             * @param value The property value to be set
1733:             *
1734:             * @exception IllegalAccessException if the caller does not have
1735:             *  access to the property accessor method
1736:             * @exception InvocationTargetException if the property accessor method
1737:             *  throws an exception
1738:             * @exception NoSuchMethodException if an accessor method for this
1739:             *  propety cannot be found
1740:             */
1741:            public void setMappedProperty(Object bean, String name, String key,
1742:                    Object value) throws IllegalAccessException,
1743:                    InvocationTargetException, NoSuchMethodException {
1744:
1745:                if (bean == null) {
1746:                    throw new IllegalArgumentException("No bean specified");
1747:                }
1748:                if (name == null) {
1749:                    throw new IllegalArgumentException(
1750:                            "No name specified for bean class '"
1751:                                    + bean.getClass() + "'");
1752:                }
1753:                if (key == null) {
1754:                    throw new IllegalArgumentException(
1755:                            "No key specified for property '" + name
1756:                                    + "' on bean class '" + bean.getClass()
1757:                                    + "'");
1758:                }
1759:
1760:                // Handle DynaBean instances specially
1761:                if (bean instanceof  DynaBean) {
1762:                    DynaProperty descriptor = ((DynaBean) bean).getDynaClass()
1763:                            .getDynaProperty(name);
1764:                    if (descriptor == null) {
1765:                        throw new NoSuchMethodException("Unknown property '"
1766:                                + name + "' on bean class '" + bean.getClass()
1767:                                + "'");
1768:                    }
1769:                    ((DynaBean) bean).set(name, key, value);
1770:                    return;
1771:                }
1772:
1773:                // Retrieve the property descriptor for the specified property
1774:                PropertyDescriptor descriptor = getPropertyDescriptor(bean,
1775:                        name);
1776:                if (descriptor == null) {
1777:                    throw new NoSuchMethodException("Unknown property '" + name
1778:                            + "' on bean class '" + bean.getClass() + "'");
1779:                }
1780:
1781:                if (descriptor instanceof  MappedPropertyDescriptor) {
1782:                    // Call the keyed setter method if there is one
1783:                    Method mappedWriteMethod = ((MappedPropertyDescriptor) descriptor)
1784:                            .getMappedWriteMethod();
1785:                    mappedWriteMethod = MethodUtils
1786:                            .getAccessibleMethod(mappedWriteMethod);
1787:                    if (mappedWriteMethod != null) {
1788:                        Object[] params = new Object[2];
1789:                        params[0] = key;
1790:                        params[1] = value;
1791:                        if (log.isTraceEnabled()) {
1792:                            String valueClassName = value == null ? "<null>"
1793:                                    : value.getClass().getName();
1794:                            log.trace("setSimpleProperty: Invoking method "
1795:                                    + mappedWriteMethod + " with key=" + key
1796:                                    + ", value=" + value + " (class "
1797:                                    + valueClassName + ")");
1798:                        }
1799:                        invokeMethod(mappedWriteMethod, bean, params);
1800:                    } else {
1801:                        throw new NoSuchMethodException("Property '" + name
1802:                                + "' has no mapped setter method"
1803:                                + "on bean class '" + bean.getClass() + "'");
1804:                    }
1805:                } else {
1806:                    /* means that the result has to be retrieved from a map */
1807:                    Method readMethod = getReadMethod(descriptor);
1808:                    if (readMethod != null) {
1809:                        Object invokeResult = invokeMethod(readMethod, bean,
1810:                                new Object[0]);
1811:                        /* test and fetch from the map */
1812:                        if (invokeResult instanceof  java.util.Map) {
1813:                            ((java.util.Map) invokeResult).put(key, value);
1814:                        }
1815:                    } else {
1816:                        throw new NoSuchMethodException(
1817:                                "Property '"
1818:                                        + name
1819:                                        + "' has no mapped getter method on bean class '"
1820:                                        + bean.getClass() + "'");
1821:                    }
1822:                }
1823:
1824:            }
1825:
1826:            /**
1827:             * Set the value of the (possibly nested) property of the specified
1828:             * name, for the specified bean, with no type conversions.
1829:             * <p>
1830:             * Example values for parameter "name" are:
1831:             * <ul>
1832:             * <li> "a" -- sets the value of property a of the specified bean </li>
1833:             * <li> "a.b" -- gets the value of property a of the specified bean,
1834:             * then on that object sets the value of property b.</li>
1835:             * <li> "a(key)" -- sets a value of mapped-property a on the specified
1836:             * bean. This effectively means bean.setA("key").</li>
1837:             * <li> "a[3]" -- sets a value of indexed-property a on the specified
1838:             * bean. This effectively means bean.setA(3).</li>
1839:             * </ul>
1840:             *
1841:             * @param bean Bean whose property is to be modified
1842:             * @param name Possibly nested name of the property to be modified
1843:             * @param value Value to which the property is to be set
1844:             *
1845:             * @exception IllegalAccessException if the caller does not have
1846:             *  access to the property accessor method
1847:             * @exception IllegalArgumentException if <code>bean</code> or
1848:             *  <code>name</code> is null
1849:             * @exception IllegalArgumentException if a nested reference to a
1850:             *  property returns null
1851:             * @exception InvocationTargetException if the property accessor method
1852:             *  throws an exception
1853:             * @exception NoSuchMethodException if an accessor method for this
1854:             *  propety cannot be found
1855:             */
1856:            public void setNestedProperty(Object bean, String name, Object value)
1857:                    throws IllegalAccessException, InvocationTargetException,
1858:                    NoSuchMethodException {
1859:
1860:                if (bean == null) {
1861:                    throw new IllegalArgumentException("No bean specified");
1862:                }
1863:                if (name == null) {
1864:                    throw new IllegalArgumentException(
1865:                            "No name specified for bean class '"
1866:                                    + bean.getClass() + "'");
1867:                }
1868:
1869:                // Resolve nested references
1870:                while (resolver.hasNested(name)) {
1871:                    String next = resolver.next(name);
1872:                    Object nestedBean = null;
1873:                    if (bean instanceof  Map) {
1874:                        nestedBean = getPropertyOfMapBean((Map) bean, next);
1875:                    } else if (resolver.isMapped(next)) {
1876:                        nestedBean = getMappedProperty(bean, next);
1877:                    } else if (resolver.isIndexed(next)) {
1878:                        nestedBean = getIndexedProperty(bean, next);
1879:                    } else {
1880:                        nestedBean = getSimpleProperty(bean, next);
1881:                    }
1882:                    if (nestedBean == null) {
1883:                        throw new NestedNullException(
1884:                                "Null property value for '" + name
1885:                                        + "' on bean class '" + bean.getClass()
1886:                                        + "'");
1887:                    }
1888:                    bean = nestedBean;
1889:                    name = resolver.remove(name);
1890:                }
1891:
1892:                if (bean instanceof  Map) {
1893:                    setPropertyOfMapBean((Map) bean, name, value);
1894:                } else if (resolver.isMapped(name)) {
1895:                    setMappedProperty(bean, name, value);
1896:                } else if (resolver.isIndexed(name)) {
1897:                    setIndexedProperty(bean, name, value);
1898:                } else {
1899:                    setSimpleProperty(bean, name, value);
1900:                }
1901:
1902:            }
1903:
1904:            /**
1905:             * This method is called by method setNestedProperty when the current bean
1906:             * is found to be a Map object, and defines how to deal with setting
1907:             * a property on a Map.
1908:             * <p>
1909:             * The standard implementation here is to:
1910:             * <ul>
1911:             * <li>call bean.set(propertyName) for all propertyName values.</li>
1912:             * <li>throw an IllegalArgumentException if the property specifier
1913:             * contains MAPPED_DELIM or INDEXED_DELIM, as Map entries are essentially
1914:             * simple properties; mapping and indexing operations do not make sense
1915:             * when accessing a map (even thought the returned object may be a Map
1916:             * or an Array).</li>
1917:             * </ul>
1918:             * <p>
1919:             * The default behaviour of beanutils 1.7.1 or later is for assigning to
1920:             * "a.b" to mean a.put(b, obj) always. However the behaviour of beanutils 
1921:             * version 1.6.0, 1.6.1, 1.7.0 was for "a.b" to mean a.setB(obj) if such
1922:             * a method existed, and a.put(b, obj) otherwise. In version 1.5 it meant
1923:             * a.put(b, obj) always (ie the same as the behaviour in the current version).
1924:             * In versions prior to 1.5 it meant a.setB(obj) always. [yes, this is 
1925:             * all <i>very</i> unfortunate]
1926:             * <p>
1927:             * Users who would like to customise the meaning of "a.b" in method 
1928:             * setNestedProperty when a is a Map can create a custom subclass of
1929:             * this class and override this method to implement the behaviour of 
1930:             * their choice, such as restoring the pre-1.4 behaviour of this class
1931:             * if they wish. When overriding this method, do not forget to deal 
1932:             * with MAPPED_DELIM and INDEXED_DELIM characters in the propertyName.
1933:             * <p>
1934:             * Note, however, that the recommended solution for objects that
1935:             * implement Map but want their simple properties to come first is
1936:             * for <i>those</i> objects to override their get/put methods to implement
1937:             * that behaviour, and <i>not</i> to solve the problem by modifying the
1938:             * default behaviour of the PropertyUtilsBean class by overriding this
1939:             * method.
1940:             *
1941:             * @param bean Map bean
1942:             * @param propertyName The property name
1943:             * @param value the property value
1944:             * 
1945:             * @throws IllegalArgumentException when the propertyName is regarded as
1946:             * being invalid.
1947:             * 
1948:             * @throws IllegalAccessException just in case subclasses override this
1949:             * method to try to access real setter methods and find permission is denied.
1950:             * 
1951:             * @throws InvocationTargetException just in case subclasses override this
1952:             * method to try to access real setter methods, and find it throws an
1953:             * exception when invoked.
1954:             * 
1955:             * @throws NoSuchMethodException just in case subclasses override this
1956:             * method to try to access real setter methods, and want to fail if
1957:             * no simple method is available.
1958:             */
1959:            protected void setPropertyOfMapBean(Map bean, String propertyName,
1960:                    Object value) throws IllegalArgumentException,
1961:                    IllegalAccessException, InvocationTargetException,
1962:                    NoSuchMethodException {
1963:
1964:                if (resolver.isMapped(propertyName)) {
1965:                    String name = resolver.getProperty(propertyName);
1966:                    if (name == null || name.length() == 0) {
1967:                        propertyName = resolver.getKey(propertyName);
1968:                    }
1969:                }
1970:
1971:                if (resolver.isIndexed(propertyName)
1972:                        || resolver.isMapped(propertyName)) {
1973:                    throw new IllegalArgumentException(
1974:                            "Indexed or mapped properties are not supported on"
1975:                                    + " objects of type Map: " + propertyName);
1976:                }
1977:
1978:                bean.put(propertyName, value);
1979:            }
1980:
1981:            /**
1982:             * Set the value of the specified property of the specified bean,
1983:             * no matter which property reference format is used, with no
1984:             * type conversions.
1985:             *
1986:             * @param bean Bean whose property is to be modified
1987:             * @param name Possibly indexed and/or nested name of the property
1988:             *  to be modified
1989:             * @param value Value to which this property is to be set
1990:             *
1991:             * @exception IllegalAccessException if the caller does not have
1992:             *  access to the property accessor method
1993:             * @exception IllegalArgumentException if <code>bean</code> or
1994:             *  <code>name</code> is null
1995:             * @exception InvocationTargetException if the property accessor method
1996:             *  throws an exception
1997:             * @exception NoSuchMethodException if an accessor method for this
1998:             *  propety cannot be found
1999:             */
2000:            public void setProperty(Object bean, String name, Object value)
2001:                    throws IllegalAccessException, InvocationTargetException,
2002:                    NoSuchMethodException {
2003:
2004:                setNestedProperty(bean, name, value);
2005:
2006:            }
2007:
2008:            /**
2009:             * Set the value of the specified simple property of the specified bean,
2010:             * with no type conversions.
2011:             *
2012:             * @param bean Bean whose property is to be modified
2013:             * @param name Name of the property to be modified
2014:             * @param value Value to which the property should be set
2015:             *
2016:             * @exception IllegalAccessException if the caller does not have
2017:             *  access to the property accessor method
2018:             * @exception IllegalArgumentException if <code>bean</code> or
2019:             *  <code>name</code> is null
2020:             * @exception IllegalArgumentException if the property name is
2021:             *  nested or indexed
2022:             * @exception InvocationTargetException if the property accessor method
2023:             *  throws an exception
2024:             * @exception NoSuchMethodException if an accessor method for this
2025:             *  propety cannot be found
2026:             */
2027:            public void setSimpleProperty(Object bean, String name, Object value)
2028:                    throws IllegalAccessException, InvocationTargetException,
2029:                    NoSuchMethodException {
2030:
2031:                if (bean == null) {
2032:                    throw new IllegalArgumentException("No bean specified");
2033:                }
2034:                if (name == null) {
2035:                    throw new IllegalArgumentException(
2036:                            "No name specified for bean class '"
2037:                                    + bean.getClass() + "'");
2038:                }
2039:
2040:                // Validate the syntax of the property name
2041:                if (resolver.hasNested(name)) {
2042:                    throw new IllegalArgumentException(
2043:                            "Nested property names are not allowed: Property '"
2044:                                    + name + "' on bean class '"
2045:                                    + bean.getClass() + "'");
2046:                } else if (resolver.isIndexed(name)) {
2047:                    throw new IllegalArgumentException(
2048:                            "Indexed property names are not allowed: Property '"
2049:                                    + name + "' on bean class '"
2050:                                    + bean.getClass() + "'");
2051:                } else if (resolver.isMapped(name)) {
2052:                    throw new IllegalArgumentException(
2053:                            "Mapped property names are not allowed: Property '"
2054:                                    + name + "' on bean class '"
2055:                                    + bean.getClass() + "'");
2056:                }
2057:
2058:                // Handle DynaBean instances specially
2059:                if (bean instanceof  DynaBean) {
2060:                    DynaProperty descriptor = ((DynaBean) bean).getDynaClass()
2061:                            .getDynaProperty(name);
2062:                    if (descriptor == null) {
2063:                        throw new NoSuchMethodException("Unknown property '"
2064:                                + name + "' on dynaclass '"
2065:                                + ((DynaBean) bean).getDynaClass() + "'");
2066:                    }
2067:                    ((DynaBean) bean).set(name, value);
2068:                    return;
2069:                }
2070:
2071:                // Retrieve the property setter method for the specified property
2072:                PropertyDescriptor descriptor = getPropertyDescriptor(bean,
2073:                        name);
2074:                if (descriptor == null) {
2075:                    throw new NoSuchMethodException("Unknown property '" + name
2076:                            + "' on class '" + bean.getClass() + "'");
2077:                }
2078:                Method writeMethod = getWriteMethod(descriptor);
2079:                if (writeMethod == null) {
2080:                    throw new NoSuchMethodException("Property '" + name
2081:                            + "' has no setter method in class '"
2082:                            + bean.getClass() + "'");
2083:                }
2084:
2085:                // Call the property setter method
2086:                Object[] values = new Object[1];
2087:                values[0] = value;
2088:                if (log.isTraceEnabled()) {
2089:                    String valueClassName = value == null ? "<null>" : value
2090:                            .getClass().getName();
2091:                    log.trace("setSimpleProperty: Invoking method "
2092:                            + writeMethod + " with value " + value + " (class "
2093:                            + valueClassName + ")");
2094:                }
2095:                invokeMethod(writeMethod, bean, values);
2096:
2097:            }
2098:
2099:            /** This just catches and wraps IllegalArgumentException. */
2100:            private Object invokeMethod(Method method, Object bean,
2101:                    Object[] values) throws IllegalAccessException,
2102:                    InvocationTargetException {
2103:                try {
2104:
2105:                    return method.invoke(bean, values);
2106:
2107:                } catch (IllegalArgumentException cause) {
2108:                    if (bean == null) {
2109:                        throw new IllegalArgumentException(
2110:                                "No bean specified "
2111:                                        + "- this should have been checked before reaching this method");
2112:                    }
2113:                    String valueString = "";
2114:                    if (values != null) {
2115:                        for (int i = 0; i < values.length; i++) {
2116:                            if (i > 0) {
2117:                                valueString += ", ";
2118:                            }
2119:                            valueString += (values[i]).getClass().getName();
2120:                        }
2121:                    }
2122:                    String expectedString = "";
2123:                    Class[] parTypes = method.getParameterTypes();
2124:                    if (parTypes != null) {
2125:                        for (int i = 0; i < parTypes.length; i++) {
2126:                            if (i > 0) {
2127:                                expectedString += ", ";
2128:                            }
2129:                            expectedString += parTypes[i].getName();
2130:                        }
2131:                    }
2132:                    IllegalArgumentException e = new IllegalArgumentException(
2133:                            "Cannot invoke "
2134:                                    + method.getDeclaringClass().getName()
2135:                                    + "." + method.getName()
2136:                                    + " on bean class '" + bean.getClass()
2137:                                    + "' - "
2138:                                    + cause.getMessage()
2139:                                    // as per https://issues.apache.org/jira/browse/BEANUTILS-224
2140:                                    + " - had objects of type \"" + valueString
2141:                                    + "\" but expected signature \""
2142:                                    + expectedString + "\"");
2143:                    if (!BeanUtils.initCause(e, cause)) {
2144:                        log.error("Method invocation failed", cause);
2145:                    }
2146:                    throw e;
2147:
2148:                }
2149:            }
2150:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.