You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2022/08/30 15:06:40 UTC

[commons-beanutils] branch master updated: Use standard Javadoc @since tag format

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-beanutils.git


The following commit(s) were added to refs/heads/master by this push:
     new 5b7aa112 Use standard Javadoc @since tag format
5b7aa112 is described below

commit 5b7aa112049d16b369f88d536a82b3bc959929a9
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Tue Aug 30 11:06:35 2022 -0400

    Use standard Javadoc @since tag format
---
 .../beanutils2/BaseDynaBeanMapDecorator.java       |    2 +-
 .../beanutils2/DynaBeanPropertyMapDecorator.java   |  180 +-
 .../apache/commons/beanutils2/PropertyUtils.java   |    4 +-
 .../commons/beanutils2/PropertyUtilsBean.java      | 4194 ++++++++++----------
 4 files changed, 2190 insertions(+), 2190 deletions(-)

diff --git a/src/main/java/org/apache/commons/beanutils2/BaseDynaBeanMapDecorator.java b/src/main/java/org/apache/commons/beanutils2/BaseDynaBeanMapDecorator.java
index 99926c8f..44a82af4 100644
--- a/src/main/java/org/apache/commons/beanutils2/BaseDynaBeanMapDecorator.java
+++ b/src/main/java/org/apache/commons/beanutils2/BaseDynaBeanMapDecorator.java
@@ -46,7 +46,7 @@ import java.util.Set;
  *    parameters.</p>
  *
  * @param <K> the type of the keys in the decorated map
- * @since BeanUtils 1.9.0
+ * @since 1.9.0
  */
 public abstract class BaseDynaBeanMapDecorator<K> implements Map<K, Object> {
 
diff --git a/src/main/java/org/apache/commons/beanutils2/DynaBeanPropertyMapDecorator.java b/src/main/java/org/apache/commons/beanutils2/DynaBeanPropertyMapDecorator.java
index ff87dfa9..9b8030e3 100644
--- a/src/main/java/org/apache/commons/beanutils2/DynaBeanPropertyMapDecorator.java
+++ b/src/main/java/org/apache/commons/beanutils2/DynaBeanPropertyMapDecorator.java
@@ -1,90 +1,90 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.beanutils2;
-
-/**
- * <p>Decorates a {@link DynaBean} to provide {@code Map} behavior.</p>
- *
- * <p>The motivation for this implementation is to provide access to {@link DynaBean}
- *    properties in technologies that are unaware of BeanUtils and {@link DynaBean}s -
- *    such as the expression languages of JSTL and JSF.</p>
- *
- * <p>This can be achieved either by wrapping the {@link DynaBean} prior to
- *    providing it to the technology to process or by providing a {@code Map}
- *    accessor method on the DynaBean implementation:</p>
- *    <pre><code>
- *         public Map&lt;String, Object&gt; getMap() {
- *             return new DynaBeanPropertyMapDecorator(this);
- *         }</code></pre>
- *
- * <p>This, for example, could be used in JSTL in the following way to access
- *    a DynaBean's {@code fooProperty}:</p>
- *    <ul><li>{@code ${myDynaBean.<b>map</b>.fooProperty}}</li></ul>
- *
- * <h2>Usage</h2>
- *
- * <p>To decorate a {@link DynaBean} simply instantiate this class with the
- *    target {@link DynaBean}:</p>
- *
- * <ul><li>{@code Map&lt;String, Object&gt; fooMap = new DynaBeanPropertyMapDecorator(fooDynaBean);}</li></ul>
- *
- * <p>The above example creates a <b><i>read only</i></b> {@code Map}.
- *    To create  a {@code Map} which can be modified, construct a
- *    {@code DynaBeanPropertyMapDecorator} with the <b><i>read only</i></b>
- *    attribute set to {@code false}:</p>
- *
- * <ul><li><code>Map&lt;String, Object&gt; fooMap =
- *   new DynaBeanPropertyMapDecorator(fooDynaBean, false);</code></li></ul>
- *
- * <h2>Limitations</h2>
- * <p>In this implementation the {@code entrySet()</code>, <code>keySet()}
- *    and {@code values()} methods create an <b><i>unmodifiable</i></b>
- *    {@code Set</code> and it does not support the Map's <code>clear()}
- *    and {@code remove()} operations.</p>
- *
- * @since BeanUtils 1.9.0
- */
-public class DynaBeanPropertyMapDecorator extends BaseDynaBeanMapDecorator<String> {
-
-    /**
-     * Constructs a Map for the specified {@link DynaBean}.
-     *
-     * @param dynaBean The dyna bean being decorated
-     * @param readOnly {@code true} if the Map is read only
-     * otherwise {@code false}
-     * @throws IllegalArgumentException if the {@link DynaBean} is null.
-     */
-    public DynaBeanPropertyMapDecorator(final DynaBean dynaBean, final boolean readOnly) {
-        super(dynaBean, readOnly);
-    }
-
-    /**
-     * Constructs a read only Map for the specified
-     * {@link DynaBean}.
-     *
-     * @param dynaBean The dyna bean being decorated
-     * @throws IllegalArgumentException if the {@link DynaBean} is null.
-     */
-    public DynaBeanPropertyMapDecorator(final DynaBean dynaBean) {
-        super(dynaBean);
-    }
-
-    @Override
-    protected String convertKey(final String propertyName) {
-        return propertyName;
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.beanutils2;
+
+/**
+ * <p>Decorates a {@link DynaBean} to provide {@code Map} behavior.</p>
+ *
+ * <p>The motivation for this implementation is to provide access to {@link DynaBean}
+ *    properties in technologies that are unaware of BeanUtils and {@link DynaBean}s -
+ *    such as the expression languages of JSTL and JSF.</p>
+ *
+ * <p>This can be achieved either by wrapping the {@link DynaBean} prior to
+ *    providing it to the technology to process or by providing a {@code Map}
+ *    accessor method on the DynaBean implementation:</p>
+ *    <pre><code>
+ *         public Map&lt;String, Object&gt; getMap() {
+ *             return new DynaBeanPropertyMapDecorator(this);
+ *         }</code></pre>
+ *
+ * <p>This, for example, could be used in JSTL in the following way to access
+ *    a DynaBean's {@code fooProperty}:</p>
+ *    <ul><li>{@code ${myDynaBean.<b>map</b>.fooProperty}}</li></ul>
+ *
+ * <h2>Usage</h2>
+ *
+ * <p>To decorate a {@link DynaBean} simply instantiate this class with the
+ *    target {@link DynaBean}:</p>
+ *
+ * <ul><li>{@code Map&lt;String, Object&gt; fooMap = new DynaBeanPropertyMapDecorator(fooDynaBean);}</li></ul>
+ *
+ * <p>The above example creates a <b><i>read only</i></b> {@code Map}.
+ *    To create  a {@code Map} which can be modified, construct a
+ *    {@code DynaBeanPropertyMapDecorator} with the <b><i>read only</i></b>
+ *    attribute set to {@code false}:</p>
+ *
+ * <ul><li><code>Map&lt;String, Object&gt; fooMap =
+ *   new DynaBeanPropertyMapDecorator(fooDynaBean, false);</code></li></ul>
+ *
+ * <h2>Limitations</h2>
+ * <p>In this implementation the {@code entrySet()</code>, <code>keySet()}
+ *    and {@code values()} methods create an <b><i>unmodifiable</i></b>
+ *    {@code Set</code> and it does not support the Map's <code>clear()}
+ *    and {@code remove()} operations.</p>
+ *
+ * @since 1.9.0
+ */
+public class DynaBeanPropertyMapDecorator extends BaseDynaBeanMapDecorator<String> {
+
+    /**
+     * Constructs a Map for the specified {@link DynaBean}.
+     *
+     * @param dynaBean The dyna bean being decorated
+     * @param readOnly {@code true} if the Map is read only
+     * otherwise {@code false}
+     * @throws IllegalArgumentException if the {@link DynaBean} is null.
+     */
+    public DynaBeanPropertyMapDecorator(final DynaBean dynaBean, final boolean readOnly) {
+        super(dynaBean, readOnly);
+    }
+
+    /**
+     * Constructs a read only Map for the specified
+     * {@link DynaBean}.
+     *
+     * @param dynaBean The dyna bean being decorated
+     * @throws IllegalArgumentException if the {@link DynaBean} is null.
+     */
+    public DynaBeanPropertyMapDecorator(final DynaBean dynaBean) {
+        super(dynaBean);
+    }
+
+    @Override
+    protected String convertKey(final String propertyName) {
+        return propertyName;
+    }
+}
diff --git a/src/main/java/org/apache/commons/beanutils2/PropertyUtils.java b/src/main/java/org/apache/commons/beanutils2/PropertyUtils.java
index 2ecf9ced..081c532f 100644
--- a/src/main/java/org/apache/commons/beanutils2/PropertyUtils.java
+++ b/src/main/java/org/apache/commons/beanutils2/PropertyUtils.java
@@ -508,7 +508,7 @@ public class PropertyUtils {
      * @throws IllegalArgumentException if {@code bean}
      *  or {@code name</code> is <code>null}
      * @see PropertyUtilsBean#isReadable
-     * @since BeanUtils 1.6
+     * @since 1.6
      */
     public static boolean isReadable(final Object bean, final String name) {
         return PropertyUtilsBean.getInstance().isReadable(bean, name);
@@ -529,7 +529,7 @@ public class PropertyUtils {
      * @throws IllegalArgumentException if {@code bean}
      *  or {@code name</code> is <code>null}
      * @see PropertyUtilsBean#isWriteable
-     * @since BeanUtils 1.6
+     * @since 1.6
      */
     public static boolean isWriteable(final Object bean, final String name) {
         return PropertyUtilsBean.getInstance().isWriteable(bean, name);
diff --git a/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java b/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java
index 9912d7b4..4fa5d32a 100644
--- a/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java
+++ b/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java
@@ -1,2097 +1,2097 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.commons.beanutils2;
-
-import java.beans.IndexedPropertyDescriptor;
-import java.beans.IntrospectionException;
-import java.beans.Introspector;
-import java.beans.PropertyDescriptor;
-import java.lang.reflect.Array;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-import org.apache.commons.beanutils2.expression.DefaultResolver;
-import org.apache.commons.beanutils2.expression.Resolver;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * Utility methods for using Java Reflection APIs to facilitate generic
- * property getter and setter operations on Java objects.  Much of this
- * code was originally included in {@code BeanUtils}, but has been
- * separated because of the volume of code involved.
- * <p>
- * In general, the objects that are examined and modified using these
- * methods are expected to conform to the property getter and setter method
- * naming conventions described in the JavaBeans Specification (Version 1.0.1).
- * No data type conversions are performed, and there are no usage of any
- * {@code PropertyEditor} classes that have been registered, although
- * a convenient way to access the registered classes themselves is included.
- * <p>
- * For the purposes of this class, five formats for referencing a particular
- * property value of a bean are defined, with the <i>default</i> layout of an
- * identifying String in parentheses. However the notation for these formats
- * and how they are resolved is now (since BeanUtils 1.8.0) controlled by
- * the configured {@link Resolver} implementation:
- * <ul>
- * <li><strong>Simple ({@code name})</strong> - The specified
- *     {@code name} identifies an individual property of a particular
- *     JavaBean.  The name of the actual getter or setter method to be used
- *     is determined using standard JavaBeans introspection, so that (unless
- *     overridden by a {@code BeanInfo} class, a property named "xyz"
- *     will have a getter method named {@code getXyz()} or (for boolean
- *     properties only) {@code isXyz()}, and a setter method named
- *     {@code setXyz()}.</li>
- * <li><strong>Nested ({@code name1.name2.name3})</strong> The first
- *     name element is used to select a property getter, as for simple
- *     references above.  The object returned for this property is then
- *     consulted, using the same approach, for a property getter for a
- *     property named {@code name2}, and so on.  The property value that
- *     is ultimately retrieved or modified is the one identified by the
- *     last name element.</li>
- * <li><strong>Indexed ({@code name[index]})</strong> - The underlying
- *     property value is assumed to be an array, or this JavaBean is assumed
- *     to have indexed property getter and setter methods.  The appropriate
- *     (zero-relative) entry in the array is selected.  {@code List}
- *     objects are now also supported for read/write.  You simply need to define
- *     a getter that returns the {@code List}</li>
- * <li><strong>Mapped ({@code name(key)})</strong> - The JavaBean
- *     is assumed to have an property getter and setter methods with an
- *     additional attribute of type {@code java.lang.String}.</li>
- * <li><strong>Combined ({@code name1.name2[index].name3(key)})</strong> -
- *     Combining mapped, nested, and indexed references is also
- *     supported.</li>
- * </ul>
- *
- * @see Resolver
- * @see PropertyUtils
- * @since 1.7
- */
-public class PropertyUtilsBean {
-
-    private Resolver resolver = new DefaultResolver();
-
-    /**
-     * Gets the PropertyUtils bean instance.
-     * @return The PropertyUtils bean instance
-     */
-    protected static PropertyUtilsBean getInstance() {
-        return BeanUtilsBean.getInstance().getPropertyUtils();
-    }
-
-    /**
-     * The cache of PropertyDescriptor arrays for beans we have already
-     * introspected, keyed by the java.lang.Class of this object.
-     */
-    private final WeakFastHashMap<Class<?>, BeanIntrospectionData> descriptorsCache;
-    private final WeakFastHashMap<Class<?>, Map> mappedDescriptorsCache;
-
-    /** Log instance */
-    private final Log log = LogFactory.getLog(PropertyUtilsBean.class);
-
-    /** The list with BeanIntrospector objects. */
-    private final List<BeanIntrospector> introspectors;
-
-    /** Base constructor */
-    public PropertyUtilsBean() {
-        descriptorsCache = new WeakFastHashMap<>();
-        descriptorsCache.setFast(true);
-        mappedDescriptorsCache = new WeakFastHashMap<>();
-        mappedDescriptorsCache.setFast(true);
-        introspectors = new CopyOnWriteArrayList<>();
-        resetBeanIntrospectors();
-    }
-
-    /**
-     * Gets the configured {@link Resolver} implementation used by BeanUtils.
-     * <p>
-     * The {@link Resolver} handles the <i>property name</i>
-     * expressions and the implementation in use effectively
-     * controls the dialect of the <i>expression language</i>
-     * that BeanUtils recognizes.
-     * <p>
-     * {@link DefaultResolver} is the default implementation used.
-     *
-     * @return resolver The property expression resolver.
-     * @since 1.8.0
-     */
-    public Resolver getResolver() {
-        return resolver;
-    }
-
-    /**
-     * Configure the {@link Resolver} implementation used by BeanUtils.
-     * <p>
-     * The {@link Resolver} handles the <i>property name</i>
-     * expressions and the implementation in use effectively
-     * controls the dialect of the <i>expression language</i>
-     * that BeanUtils recognizes.
-     * <p>
-     * {@link DefaultResolver} is the default implementation used.
-     *
-     * @param resolver The property expression resolver.
-     * @since 1.8.0
-     */
-    public void setResolver(final Resolver resolver) {
-        if (resolver == null) {
-            this.resolver = new DefaultResolver();
-        } else {
-            this.resolver = resolver;
-        }
-    }
-
-    /**
-     * Resets the {@link BeanIntrospector} objects registered at this instance. After this
-     * method was called, only the default {@code BeanIntrospector} is registered.
-     *
-     * @since 1.9
-     */
-    public final void resetBeanIntrospectors() {
-        introspectors.clear();
-        introspectors.add(DefaultBeanIntrospector.INSTANCE);
-        introspectors.add(SuppressPropertiesBeanIntrospector.SUPPRESS_CLASS);
-    }
-
-    /**
-     * Adds a {@code BeanIntrospector}. This object is invoked when the
-     * property descriptors of a class need to be obtained.
-     *
-     * @param introspector the {@code BeanIntrospector} to be added (must
-     *        not be <b>null</b>
-     * @throws IllegalArgumentException if the argument is <b>null</b>
-     * @since 1.9
-     */
-    public void addBeanIntrospector(final BeanIntrospector introspector) {
-        if (introspector == null) {
-            throw new IllegalArgumentException(
-                    "BeanIntrospector must not be null!");
-        }
-        introspectors.add(introspector);
-    }
-
-    /**
-     * Removes the specified {@code BeanIntrospector}.
-     *
-     * @param introspector the {@code BeanIntrospector} to be removed
-     * @return <b>true</b> if the {@code BeanIntrospector} existed and
-     *         could be removed, <b>false</b> otherwise
-     * @since 1.9
-     */
-    public boolean removeBeanIntrospector(final BeanIntrospector introspector) {
-        return introspectors.remove(introspector);
-    }
-
-    /**
-     * Clear any cached property descriptors information for all classes
-     * loaded by any class loaders.  This is useful in cases where class
-     * loaders are thrown away to implement class reloading.
-     */
-    public void clearDescriptors() {
-        descriptorsCache.clear();
-        mappedDescriptorsCache.clear();
-        Introspector.flushCaches();
-    }
-
-    /**
-     * <p>Copy property values from the "origin" bean to the "destination" bean
-     * for all cases where the property names are the same (even though the
-     * actual getter and setter methods might have been customized via
-     * {@code BeanInfo} classes).  No conversions are performed on the
-     * actual property values -- it is assumed that the values retrieved from
-     * the origin bean are assignment-compatible with the types expected by
-     * the destination bean.</p>
-     *
-     * <p>If the origin "bean" is actually a {@code Map}, it is assumed
-     * to contain String-valued <strong>simple</strong> property names as the keys, pointing
-     * at the corresponding property values that will be set in the destination
-     * bean.<strong>Note</strong> that this method is intended to perform
-     * a "shallow copy" of the properties and so complex properties
-     * (for example, nested ones) will not be copied.</p>
-     *
-     * <p>Note, that this method will not copy a List to a List, or an Object[]
-     * to an Object[]. It's specifically for copying JavaBean properties. </p>
-     *
-     * @param dest Destination bean whose properties are modified
-     * @param orig Origin bean whose properties are retrieved
-     *
-     * @throws IllegalAccessException if the caller does not have
-     *  access to the property accessor method
-     * @throws IllegalArgumentException if the {@code dest} or
-     *  {@code orig} argument is null
-     * @throws InvocationTargetException if the property accessor method
-     *  throws an exception
-     * @throws NoSuchMethodException if an accessor method for this
-     *  property cannot be found
-     */
-    public void copyProperties(final Object dest, final Object orig)
-            throws IllegalAccessException, InvocationTargetException,
-            // TODO BEFORE 2.0
-            // MISMATCH between implementation and Javadoc.
-        NoSuchMethodException {
-        if (dest == null) {
-            throw new IllegalArgumentException("No destination bean specified");
-        }
-        if (orig == null) {
-            throw new IllegalArgumentException("No origin bean specified");
-        }
-
-        if (orig instanceof DynaBean) {
-            final DynaProperty[] origDescriptors = ((DynaBean) orig).getDynaClass().getDynaProperties();
-            for (final DynaProperty origDescriptor : origDescriptors) {
-                final String name = origDescriptor.getName();
-                if (isReadable(orig, name) && isWriteable(dest, name)) {
-                    try {
-                        final Object value = ((DynaBean) orig).get(name);
-                        if (dest instanceof DynaBean) {
-                            ((DynaBean) dest).set(name, value);
-                        } else {
-                            setSimpleProperty(dest, name, value);
-                        }
-                    } catch (final NoSuchMethodException e) {
-                        if (log.isDebugEnabled()) {
-                            log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", e);
-                        }
-                    }
-                }
-            }
-        } else if (orig instanceof Map) {
-            for (final Map.Entry<?, ?> entry : ((Map<?, ?>) orig).entrySet()) {
-                final String name = (String) entry.getKey();
-                if (isWriteable(dest, name)) {
-                    try {
-                        if (dest instanceof DynaBean) {
-                            ((DynaBean) dest).set(name, entry.getValue());
-                        } else {
-                            setSimpleProperty(dest, name, entry.getValue());
-                        }
-                    } catch (final NoSuchMethodException e) {
-                        if (log.isDebugEnabled()) {
-                            log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", e);
-                        }
-                    }
-                }
-            }
-        } else /* if (orig is a standard JavaBean) */ {
-            final PropertyDescriptor[] origDescriptors = getPropertyDescriptors(orig);
-            for (final PropertyDescriptor origDescriptor : origDescriptors) {
-                final String name = origDescriptor.getName();
-                if (isReadable(orig, name) && isWriteable(dest, name)) {
-                    try {
-                        final Object value = getSimpleProperty(orig, name);
-                        if (dest instanceof DynaBean) {
-                            ((DynaBean) dest).set(name, value);
-                        } else {
-                            setSimpleProperty(dest, name, value);
-                        }
-                    } catch (final NoSuchMethodException e) {
-                        if (log.isDebugEnabled()) {
-                            log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", e);
-                        }
-                    }
-                }
-            }
-        }
-
-    }
-
-    /**
-     * <p>Return the entire set of properties for which the specified bean
-     * provides a read method.  This map contains the unconverted property
-     * values for all properties for which a read method is provided
-     * (i.e. where the {@code getReadMethod()} returns non-null).</p>
-     *
-     * <p><strong>FIXME</strong> - Does not account for mapped properties.</p>
-     *
-     * @param bean Bean whose properties are to be extracted
-     * @return The set of properties for the bean
-     *
-     * @throws IllegalAccessException if the caller does not have
-     *  access to the property accessor method
-     * @throws IllegalArgumentException if {@code bean} is null
-     * @throws InvocationTargetException if the property accessor method
-     *  throws an exception
-     * @throws NoSuchMethodException if an accessor method for this
-     *  property cannot be found
-     */
-    public Map<String, Object> describe(final Object bean)
-            throws IllegalAccessException, InvocationTargetException,
-            NoSuchMethodException {
-
-        if (bean == null) {
-            throw new IllegalArgumentException("No bean specified");
-        }
-        final Map<String, Object> description = new HashMap<>();
-        if (bean instanceof DynaBean) {
-            final DynaProperty[] descriptors =
-                ((DynaBean) bean).getDynaClass().getDynaProperties();
-            for (final DynaProperty descriptor : descriptors) {
-                final String name = descriptor.getName();
-                description.put(name, getProperty(bean, name));
-            }
-        } else {
-            final PropertyDescriptor[] descriptors =
-                getPropertyDescriptors(bean);
-            for (final PropertyDescriptor descriptor : descriptors) {
-                final String name = descriptor.getName();
-                if (descriptor.getReadMethod() != null) {
-                    description.put(name, getProperty(bean, name));
-                }
-            }
-        }
-        return description;
-    }
-
-    /**
-     * Gets the value of the specified indexed property of the specified
-     * bean, with no type conversions.  The zero-relative index of the
-     * required value must be included (in square brackets) as a suffix to
-     * the property name, or {@code IllegalArgumentException} will be
-     * thrown.  In addition to supporting the JavaBeans specification, this
-     * method has been extended to support {@code List} objects as well.
-     *
-     * @param bean Bean whose property is to be extracted
-     * @param name {@code propertyname[index]} of the property value
-     *  to be extracted
-     * @return the indexed property value
-     *
-     * @throws IndexOutOfBoundsException if the specified index
-     *  is outside the valid range for the underlying array or List
-     * @throws IllegalAccessException if the caller does not have
-     *  access to the property accessor method
-     * @throws IllegalArgumentException if {@code bean} or
-     *  {@code name} is null
-     * @throws InvocationTargetException if the property accessor method
-     *  throws an exception
-     * @throws NoSuchMethodException if an accessor method for this
-     *  property cannot be found
-     */
-    public Object getIndexedProperty(final Object bean, String name)
-            throws IllegalAccessException, InvocationTargetException,
-            NoSuchMethodException {
-        if (bean == null) {
-            throw new IllegalArgumentException("No bean specified");
-        }
-        if (name == null) {
-            throw new IllegalArgumentException("No name specified for bean class '" +
-                    bean.getClass() + "'");
-        }
-
-        // Identify the index of the requested individual property
-        int index = -1;
-        try {
-            index = resolver.getIndex(name);
-        } catch (final IllegalArgumentException e) {
-            throw new IllegalArgumentException("Invalid indexed property '" +
-                    name + "' on bean class '" + bean.getClass() + "' " +
-                    e.getMessage());
-        }
-        if (index < 0) {
-            throw new IllegalArgumentException("Invalid indexed property '" +
-                    name + "' on bean class '" + bean.getClass() + "'");
-        }
-
-        // Isolate the name
-        name = resolver.getProperty(name);
-
-        // Request the specified indexed property value
-        return getIndexedProperty(bean, name, index);
-    }
-
-    /**
-     * Gets the value of the specified indexed property of the specified
-     * bean, with no type conversions.  In addition to supporting the JavaBeans
-     * specification, this method has been extended to support
-     * {@code List} objects as well.
-     *
-     * @param bean Bean whose property is to be extracted
-     * @param name Simple property name of the property value to be extracted
-     * @param index Index of the property value to be extracted
-     * @return the indexed property value
-     *
-     * @throws IndexOutOfBoundsException if the specified index
-     *  is outside the valid range for the underlying property
-     * @throws IllegalAccessException if the caller does not have
-     *  access to the property accessor method
-     * @throws IllegalArgumentException if {@code bean} or
-     *  {@code name} is null
-     * @throws InvocationTargetException if the property accessor method
-     *  throws an exception
-     * @throws NoSuchMethodException if an accessor method for this
-     *  property cannot be found
-     */
-    public Object getIndexedProperty(final Object bean,
-                                            final String name, final int index)
-            throws IllegalAccessException, InvocationTargetException,
-            NoSuchMethodException {
-        if (bean == null) {
-            throw new IllegalArgumentException("No bean specified");
-        }
-        if (name == null || name.isEmpty()) {
-            if (bean.getClass().isArray()) {
-                return Array.get(bean, index);
-            }
-            if (bean instanceof List) {
-                return ((List<?>)bean).get(index);
-            }
-        }
-        if (name == null) {
-            throw new IllegalArgumentException("No name specified for bean class '" +
-                    bean.getClass() + "'");
-        }
-
-        // Handle DynaBean instances specially
-        if (bean instanceof DynaBean) {
-            final DynaProperty descriptor =
-                    ((DynaBean) bean).getDynaClass().getDynaProperty(name);
-            if (descriptor == null) {
-                throw new NoSuchMethodException("Unknown property '" +
-                    name + "' on bean class '" + bean.getClass() + "'");
-            }
-            return ((DynaBean) bean).get(name, index);
-        }
-
-        // Retrieve the property descriptor for the specified property
-        final PropertyDescriptor descriptor =
-                getPropertyDescriptor(bean, name);
-        if (descriptor == null) {
-            throw new NoSuchMethodException("Unknown property '" +
-                    name + "' on bean class '" + bean.getClass() + "'");
-        }
-
-        // Call the indexed getter method if there is one
-        if (descriptor instanceof IndexedPropertyDescriptor) {
-            Method readMethod = ((IndexedPropertyDescriptor) descriptor).
-                    getIndexedReadMethod();
-            readMethod = MethodUtils.getAccessibleMethod(bean.getClass(), readMethod);
-            if (readMethod != null) {
-                final Object[] subscript = new Object[1];
-                subscript[0] = Integer.valueOf(index);
-                try {
-                    return invokeMethod(readMethod,bean, subscript);
-                } catch (final InvocationTargetException e) {
-                    if (e.getTargetException() instanceof
-                            IndexOutOfBoundsException) {
-                        throw (IndexOutOfBoundsException)
-                                e.getTargetException();
-                    }
-                    throw e;
-                }
-            }
-        }
-
-        // Otherwise, the underlying property must be an array
-        final Method readMethod = getReadMethod(bean.getClass(), descriptor);
-        if (readMethod == null) {
-            throw new NoSuchMethodException("Property '" + name + "' has no " +
-                    "getter method on bean class '" + bean.getClass() + "'");
-        }
-
-        // Call the property getter and return the value
-        final Object value = invokeMethod(readMethod, bean, BeanUtils.EMPTY_OBJECT_ARRAY);
-        if (!value.getClass().isArray()) {
-            if (!(value instanceof java.util.List)) {
-                throw new IllegalArgumentException("Property '" + name +
-                        "' is not indexed on bean class '" + bean.getClass() + "'");
-            }
-            //get the List's value
-            return ((java.util.List<?>) value).get(index);
-        }
-        //get the array's value
-        try {
-            return Array.get(value, index);
-        } catch (final ArrayIndexOutOfBoundsException e) {
-            throw new ArrayIndexOutOfBoundsException("Index: " +
-                    index + ", Size: " + Array.getLength(value) +
-                    " for property '" + name + "'");
-        }
-    }
-
-    /**
-     * Gets the value of the specified mapped property of the
-     * specified bean, with no type conversions.  The key of the
-     * required value must be included (in brackets) as a suffix to
-     * the property name, or {@code IllegalArgumentException} will be
-     * thrown.
-     *
-     * @param bean Bean whose property is to be extracted
-     * @param name {@code propertyname(key)} of the property value
-     *  to be extracted
-     * @return the mapped property value
-     *
-     * @throws IllegalAccessException if the caller does not have
-     *  access to the property accessor method
-     * @throws InvocationTargetException if the property accessor method
-     *  throws an exception
-     * @throws NoSuchMethodException if an accessor method for this
-     *  property cannot be found
-     */
-    public Object getMappedProperty(final Object bean, String name)
-            throws IllegalAccessException, InvocationTargetException,
-            NoSuchMethodException {
-        if (bean == null) {
-            throw new IllegalArgumentException("No bean specified");
-        }
-        if (name == null) {
-            throw new IllegalArgumentException("No name specified for bean class '" +
-                    bean.getClass() + "'");
-        }
-
-        // Identify the key of the requested individual property
-        String key  = null;
-        try {
-            key = resolver.getKey(name);
-        } catch (final IllegalArgumentException e) {
-            throw new IllegalArgumentException
-                    ("Invalid mapped property '" + name +
-                    "' on bean class '" + bean.getClass() + "' " + e.getMessage());
-        }
-        if (key == null) {
-            throw new IllegalArgumentException("Invalid mapped property '" +
-                    name + "' on bean class '" + bean.getClass() + "'");
-        }
-
-        // Isolate the name
-        name = resolver.getProperty(name);
-
-        // Request the specified indexed property value
-        return getMappedProperty(bean, name, key);
-    }
-
-    /**
-     * Gets the value of the specified mapped property of the specified
-     * bean, with no type conversions.
-     *
-     * @param bean Bean whose property is to be extracted
-     * @param name Mapped property name of the property value to be extracted
-     * @param key Key of the property value to be extracted
-     * @return the mapped property value
-     *
-     * @throws IllegalAccessException if the caller does not have
-     *  access to the property accessor method
-     * @throws InvocationTargetException if the property accessor method
-     *  throws an exception
-     * @throws NoSuchMethodException if an accessor method for this
-     *  property cannot be found
-     */
-    public Object getMappedProperty(final Object bean,
-                                           final String name, final String key)
-            throws IllegalAccessException, InvocationTargetException,
-            NoSuchMethodException {
-        if (bean == null) {
-            throw new IllegalArgumentException("No bean specified");
-        }
-        if (name == null) {
-            throw new IllegalArgumentException("No name specified for bean class '" +
-                    bean.getClass() + "'");
-        }
-        if (key == null) {
-            throw new IllegalArgumentException("No key specified for property '" +
-                    name + "' on bean class " + bean.getClass() + "'");
-        }
-
-        // Handle DynaBean instances specially
-        if (bean instanceof DynaBean) {
-            final DynaProperty descriptor =
-                    ((DynaBean) bean).getDynaClass().getDynaProperty(name);
-            if (descriptor == null) {
-                throw new NoSuchMethodException("Unknown property '" +
-                        name + "'+ on bean class '" + bean.getClass() + "'");
-            }
-            return ((DynaBean) bean).get(name, key);
-        }
-
-        Object result = null;
-
-        // Retrieve the property descriptor for the specified property
-        final PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
-        if (descriptor == null) {
-            throw new NoSuchMethodException("Unknown property '" +
-                    name + "'+ on bean class '" + bean.getClass() + "'");
-        }
-
-        if (descriptor instanceof MappedPropertyDescriptor) {
-            // Call the keyed getter method if there is one
-            Method readMethod = ((MappedPropertyDescriptor) descriptor).
-                    getMappedReadMethod();
-            readMethod = MethodUtils.getAccessibleMethod(bean.getClass(), readMethod);
-            if (readMethod == null) {
-                throw new NoSuchMethodException("Property '" + name +
-                        "' has no mapped getter method on bean class '" +
-                        bean.getClass() + "'");
-            }
-            final Object[] keyArray = new Object[1];
-            keyArray[0] = key;
-            result = invokeMethod(readMethod, bean, keyArray);
-        } else {
-          /* means that the result has to be retrieved from a map */
-          final Method readMethod = getReadMethod(bean.getClass(), descriptor);
-          if (readMethod == null) {
-            throw new NoSuchMethodException("Property '" + name +
-                    "' has no mapped getter method on bean class '" +
-                    bean.getClass() + "'");
-          }
-        final Object invokeResult = invokeMethod(readMethod, bean, BeanUtils.EMPTY_OBJECT_ARRAY);
-        /* test and fetch from the map */
-        if (invokeResult instanceof java.util.Map) {
-          result = ((java.util.Map<?, ?>)invokeResult).get(key);
-        }
-        }
-        return result;
-    }
-
-    /**
-     * <p>Return the mapped property descriptors for this bean class.</p>
-     *
-     * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
-     *
-     * @param beanClass Bean class to be introspected
-     * @return the mapped property descriptors
-     */
-    Map<Class<?>, Map> getMappedPropertyDescriptors(final Class<?> beanClass) {
-        if (beanClass == null) {
-            return null;
-        }
-
-        // Look up any cached descriptors for this bean class
-        return mappedDescriptorsCache.get(beanClass);
-    }
-
-    /**
-     * <p>Return the mapped property descriptors for this bean.</p>
-     *
-     * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
-     *
-     * @param bean Bean to be introspected
-     * @return the mapped property descriptors
-     */
-    Map getMappedPropertyDescriptors(final Object bean) {
-        if (bean == null) {
-            return null;
-        }
-        return getMappedPropertyDescriptors(bean.getClass());
-    }
-
-    /**
-     * Gets the value of the (possibly nested) property of the specified
-     * name, for the specified bean, with no type conversions.
-     *
-     * @param bean Bean whose property is to be extracted
-     * @param name Possibly nested name of the property to be extracted
-     * @return the nested property value
-     *
-     * @throws IllegalAccessException if the caller does not have
-     *  access to the property accessor method
-     * @throws IllegalArgumentException if {@code bean} or
-     *  {@code name} is null
-     * @throws NestedNullException if a nested reference to a
-     *  property returns null
-     * @throws InvocationTargetException
-     * if the property accessor method throws an exception
-     * @throws NoSuchMethodException if an accessor method for this
-     *  property cannot be found
-     */
-    public Object getNestedProperty(Object bean, String name)
-            throws IllegalAccessException, InvocationTargetException,
-            NoSuchMethodException {
-        if (bean == null) {
-            throw new IllegalArgumentException("No bean specified");
-        }
-        if (name == null) {
-            throw new IllegalArgumentException("No name specified for bean class '" +
-                    bean.getClass() + "'");
-        }
-
-        // Resolve nested references
-        while (resolver.hasNested(name)) {
-            final String next = resolver.next(name);
-            Object nestedBean = null;
-            if (bean instanceof Map) {
-                nestedBean = getPropertyOfMapBean((Map<?, ?>) bean, next);
-            } else if (resolver.isMapped(next)) {
-                nestedBean = getMappedProperty(bean, next);
-            } else if (resolver.isIndexed(next)) {
-                nestedBean = getIndexedProperty(bean, next);
-            } else {
-                nestedBean = getSimpleProperty(bean, next);
-            }
-            if (nestedBean == null) {
-                throw new NestedNullException
-                        ("Null property value for '" + name +
-                        "' on bean class '" + bean.getClass() + "'");
-            }
-            bean = nestedBean;
-            name = resolver.remove(name);
-        }
-
-        if (bean instanceof Map) {
-            bean = getPropertyOfMapBean((Map<?, ?>) bean, name);
-        } else if (resolver.isMapped(name)) {
-            bean = getMappedProperty(bean, name);
-        } else if (resolver.isIndexed(name)) {
-            bean = getIndexedProperty(bean, name);
-        } else {
-            bean = getSimpleProperty(bean, name);
-        }
-        return bean;
-    }
-
-    /**
-     * This method is called by getNestedProperty and setNestedProperty to
-     * define what it means to get a property from an object which implements
-     * Map. See setPropertyOfMapBean for more information.
-     *
-     * @param bean Map bean
-     * @param propertyName The property name
-     * @return the property value
-     *
-     * @throws IllegalArgumentException when the propertyName is regarded as
-     * being invalid.
-     *
-     * @throws IllegalAccessException just in case subclasses override this
-     * method to try to access real getter methods and find permission is denied.
-     *
-     * @throws InvocationTargetException just in case subclasses override this
-     * method to try to access real getter methods, and find it throws an
-     * exception when invoked.
-     *
-     * @throws NoSuchMethodException just in case subclasses override this
-     * method to try to access real getter methods, and want to fail if
-     * no simple method is available.
-     * @since 1.8.0
-     */
-    protected Object getPropertyOfMapBean(final Map<?, ?> bean, String propertyName)
-        throws IllegalArgumentException, IllegalAccessException,
-        InvocationTargetException, NoSuchMethodException {
-
-        if (resolver.isMapped(propertyName)) {
-            final String name = resolver.getProperty(propertyName);
-            if (name == null || name.isEmpty()) {
-                propertyName = resolver.getKey(propertyName);
-            }
-        }
-
-        if (resolver.isIndexed(propertyName) ||
-            resolver.isMapped(propertyName)) {
-            throw new IllegalArgumentException(
-                    "Indexed or mapped properties are not supported on"
-                    + " objects of type Map: " + propertyName);
-        }
-
-        return bean.get(propertyName);
-    }
-
-    /**
-     * Gets the value of the specified property of the specified bean,
-     * no matter which property reference format is used, with no
-     * type conversions.
-     *
-     * @param bean Bean whose property is to be extracted
-     * @param name Possibly indexed and/or nested name of the property
-     *  to be extracted
-     * @return the property value
-     *
-     * @throws IllegalAccessException if the caller does not have
-     *  access to the property accessor method
-     * @throws IllegalArgumentException if {@code bean} or
-     *  {@code name} is null
-     * @throws InvocationTargetException if the property accessor method
-     *  throws an exception
-     * @throws NoSuchMethodException if an accessor method for this
-     *  property cannot be found
-     */
-    public Object getProperty(final Object bean, final String name)
-            throws IllegalAccessException, InvocationTargetException,
-            NoSuchMethodException {
-        return getNestedProperty(bean, name);
-    }
-
-    /**
-     * <p>Retrieve the property descriptor for the specified property of the
-     * specified bean, or return {@code null} if there is no such
-     * descriptor.  This method resolves indexed and nested property
-     * references in the same manner as other methods in this class, except
-     * that if the last (or only) name element is indexed, the descriptor
-     * for the last resolved property itself is returned.</p>
-     *
-     * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
-     *
-     * <p>Note that for Java 8 and above, this method no longer return
-     * IndexedPropertyDescriptor for {@link List}-typed properties, only for
-     * properties typed as native array. (BEANUTILS-492).
-     *
-     * @param bean Bean for which a property descriptor is requested
-     * @param name Possibly indexed and/or nested name of the property for
-     *  which a property descriptor is requested
-     * @return the property descriptor
-     *
-     * @throws IllegalAccessException if the caller does not have
-     *  access to the property accessor method
-     * @throws IllegalArgumentException if {@code bean} or
-     *  {@code name} is null
-     * @throws IllegalArgumentException if a nested reference to a
-     *  property returns null
-     * @throws InvocationTargetException if the property accessor method
-     *  throws an exception
-     * @throws NoSuchMethodException if an accessor method for this
-     *  property cannot be found
-     */
-    public PropertyDescriptor getPropertyDescriptor(Object bean,
-                                                           String name)
-            throws IllegalAccessException, InvocationTargetException,
-            NoSuchMethodException {
-        if (bean == null) {
-            throw new IllegalArgumentException("No bean specified");
-        }
-        if (name == null) {
-            throw new IllegalArgumentException("No name specified for bean class '" +
-                    bean.getClass() + "'");
-        }
-
-        // Resolve nested references
-        while (resolver.hasNested(name)) {
-            final String next = resolver.next(name);
-            final Object nestedBean = getProperty(bean, next);
-            if (nestedBean == null) {
-                throw new NestedNullException
-                        ("Null property value for '" + next +
-                        "' on bean class '" + bean.getClass() + "'");
-            }
-            bean = nestedBean;
-            name = resolver.remove(name);
-        }
-
-        // Remove any subscript from the final name value
-        name = resolver.getProperty(name);
-
-        // Look up and return this property from our cache
-        // creating and adding it to the cache if not found.
-        if (name == null) {
-            return null;
-        }
-
-        final BeanIntrospectionData data = getIntrospectionData(bean.getClass());
-        PropertyDescriptor result = data.getDescriptor(name);
-        if (result != null) {
-            return result;
-        }
-
-        Map mappedDescriptors = getMappedPropertyDescriptors(bean);
-        if (mappedDescriptors == null) {
-            mappedDescriptors = new ConcurrentHashMap<Class<?>, Map>();
-            mappedDescriptorsCache.put(bean.getClass(), mappedDescriptors);
-        }
-        result = (PropertyDescriptor) mappedDescriptors.get(name);
-        if (result == null) {
-            // not found, try to create it
-            try {
-                result = new MappedPropertyDescriptor(name, bean.getClass());
-            } catch (final IntrospectionException ie) {
-                /* Swallow IntrospectionException
-                 * TODO: Why?
-                 */
-            }
-            if (result != null) {
-                mappedDescriptors.put(name, result);
-            }
-        }
-
-        return result;
-    }
-
-    /**
-     * <p>Retrieve the property descriptors for the specified class,
-     * introspecting and caching them the first time a particular bean class
-     * is encountered.</p>
-     *
-     * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
-     *
-     * @param beanClass Bean class for which property descriptors are requested
-     * @return the property descriptors
-     *
-     * @throws IllegalArgumentException if {@code beanClass} is null
-     */
-    public PropertyDescriptor[]
-            getPropertyDescriptors(final Class<?> beanClass) {
-        return getIntrospectionData(beanClass).getDescriptors();
-    }
-
-    /**
-     * <p>Retrieve the property descriptors for the specified bean,
-     * introspecting and caching them the first time a particular bean class
-     * is encountered.</p>
-     *
-     * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
-     *
-     * @param bean Bean for which property descriptors are requested
-     * @return the property descriptors
-     *
-     * @throws IllegalArgumentException if {@code bean} is null
-     */
-    public PropertyDescriptor[] getPropertyDescriptors(final Object bean) {
-        if (bean == null) {
-            throw new IllegalArgumentException("No bean specified");
-        }
-        return getPropertyDescriptors(bean.getClass());
-    }
-
-    /**
-     * <p>Return the Java Class repesenting the property editor class that has
-     * been registered for this property (if any).  This method follows the
-     * same name resolution rules used by {@code getPropertyDescriptor()},
-     * so if the last element of a name reference is indexed, the property
-     * editor for the underlying property's class is returned.</p>
-     *
-     * <p>Note that {@code null} will be returned if there is no property,
-     * or if there is no registered property editor class.  Because this
-     * return value is ambiguous, you should determine the existence of the
-     * property itself by other means.</p>
-     *
-     * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
-     *
-     * @param bean Bean for which a property descriptor is requested
-     * @param name Possibly indexed and/or nested name of the property for
-     *  which a property descriptor is requested
-     * @return the property editor class
-     *
-     * @throws IllegalAccessException if the caller does not have
-     *  access to the property accessor method
-     * @throws IllegalArgumentException if {@code bean} or
-     *  {@code name} is null
-     * @throws IllegalArgumentException if a nested reference to a
-     *  property returns null
-     * @throws InvocationTargetException if the property accessor method
-     *  throws an exception
-     * @throws NoSuchMethodException if an accessor method for this
-     *  property cannot be found
-     */
-    public Class<?> getPropertyEditorClass(final Object bean, final String name)
-            throws IllegalAccessException, InvocationTargetException,
-            NoSuchMethodException {
-        if (bean == null) {
-            throw new IllegalArgumentException("No bean specified");
-        }
-        if (name == null) {
-            throw new IllegalArgumentException("No name specified for bean class '" +
-                    bean.getClass() + "'");
-        }
-
-        final PropertyDescriptor descriptor =
-                getPropertyDescriptor(bean, name);
-        if (descriptor != null) {
-            return descriptor.getPropertyEditorClass();
-        }
-        return null;
-    }
-
-    /**
-     * Gets the Java Class representing the property type of the specified
-     * property, or {@code null} if there is no such property for the
-     * specified bean.  This method follows the same name resolution rules
-     * used by {@code getPropertyDescriptor()}, so if the last element
-     * of a name reference is indexed, the type of the property itself will
-     * be returned.  If the last (or only) element has no property with the
-     * specified name, {@code null} is returned.
-     * <p>
-     * If the property is an indexed property (e.g. {@code String[]}),
-     * this method will return the type of the items within that array.
-     * Note that from Java 8 and newer, this method do not support
-     * such index types from items within an Collection, and will
-     * instead return the collection type (e.g. java.util.List) from the
-     * getter method.
-     *
-     * @param bean Bean for which a property descriptor is requested
-     * @param name Possibly indexed and/or nested name of the property for
-     *  which a property descriptor is requested
-     * @return The property type
-     *
-     * @throws IllegalAccessException if the caller does not have
-     *  access to the property accessor method
-     * @throws IllegalArgumentException if {@code bean} or
-     *  {@code name} is null
-     * @throws IllegalArgumentException if a nested reference to a
-     *  property returns null
-     * @throws InvocationTargetException if the property accessor method
-     *  throws an exception
-     * @throws NoSuchMethodException if an accessor method for this
-     *  property cannot be found
-     */
-    public Class<?> getPropertyType(Object bean, String name)
-            throws IllegalAccessException, InvocationTargetException,
-            NoSuchMethodException {
-        if (bean == null) {
-            throw new IllegalArgumentException("No bean specified");
-        }
-        if (name == null) {
-            throw new IllegalArgumentException("No name specified for bean class '" +
-                    bean.getClass() + "'");
-        }
-
-        // Resolve nested references
-        while (resolver.hasNested(name)) {
-            final String next = resolver.next(name);
-            final Object nestedBean = getProperty(bean, next);
-            if (nestedBean == null) {
-                throw new NestedNullException
-                        ("Null property value for '" + next +
-                        "' on bean class '" + bean.getClass() + "'");
-            }
-            bean = nestedBean;
-            name = resolver.remove(name);
-        }
-
-        // Remove any subscript from the final name value
-        name = resolver.getProperty(name);
-
-        // Special handling for DynaBeans
-        if (bean instanceof DynaBean) {
-            final DynaProperty descriptor =
-                    ((DynaBean) bean).getDynaClass().getDynaProperty(name);
-            if (descriptor == null) {
-                return null;
-            }
-            final Class<?> type = descriptor.getType();
-            if (type == null) {
-                return null;
-            }
-            if (type.isArray()) {
-                return type.getComponentType();
-            }
-            return type;
-        }
-
-        final PropertyDescriptor descriptor =
-                getPropertyDescriptor(bean, name);
-        if (descriptor == null) {
-            return null;
-        }
-        if (descriptor instanceof IndexedPropertyDescriptor) {
-            return ((IndexedPropertyDescriptor) descriptor).
-                    getIndexedPropertyType();
-        }
-        if (descriptor instanceof MappedPropertyDescriptor) {
-            return ((MappedPropertyDescriptor) descriptor).
-                    getMappedPropertyType();
-        }
-        return descriptor.getPropertyType();
-    }
-
-    /**
-     * <p>Return an accessible property getter method for this property,
-     * if there is one; otherwise return {@code null}.</p>
-     *
-     * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
-     *
-     * @param descriptor Property descriptor to return a getter for
-     * @return The read method
-     */
-    public Method getReadMethod(final PropertyDescriptor descriptor) {
-        return MethodUtils.getAccessibleMethod(descriptor.getReadMethod());
-    }
-
-    /**
-     * <p>Return an accessible property getter method for this property,
-     * if there is one; otherwise return {@code null}.</p>
-     *
-     * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
-     *
-     * @param clazz The class of the read method will be invoked on
-     * @param descriptor Property descriptor to return a getter for
-     * @return The read method
-     */
-    Method getReadMethod(final Class<?> clazz, final PropertyDescriptor descriptor) {
-        return MethodUtils.getAccessibleMethod(clazz, descriptor.getReadMethod());
-    }
-
-    /**
-     * Gets the value of the specified simple property of the specified
-     * bean, with no type conversions.
-     *
-     * @param bean Bean whose property is to be extracted
-     * @param name Name of the property to be extracted
-     * @return The property value
-     *
-     * @throws IllegalAccessException if the caller does not have
-     *  access to the property accessor method
-     * @throws IllegalArgumentException if {@code bean} or
-     *  {@code name} is null
-     * @throws IllegalArgumentException if the property name
-     *  is nested or indexed
-     * @throws InvocationTargetException if the property accessor method
-     *  throws an exception
-     * @throws NoSuchMethodException if an accessor method for this
-     *  property cannot be found
-     */
-    public Object getSimpleProperty(final Object bean, final String name)
-            throws IllegalAccessException, InvocationTargetException,
-            NoSuchMethodException {
-        if (bean == null) {
-            throw new IllegalArgumentException("No bean specified");
-        }
-        if (name == null) {
-            throw new IllegalArgumentException("No name specified for bean class '" +
-                    bean.getClass() + "'");
-        }
-
-        // Validate the syntax of the property name
-        if (resolver.hasNested(name)) {
-            throw new IllegalArgumentException
-                    ("Nested property names are not allowed: Property '" +
-                    name + "' on bean class '" + bean.getClass() + "'");
-        }
-        if (resolver.isIndexed(name)) {
-            throw new IllegalArgumentException
-                    ("Indexed property names are not allowed: Property '" +
-                    name + "' on bean class '" + bean.getClass() + "'");
-        }
-        if (resolver.isMapped(name)) {
-            throw new IllegalArgumentException
-                    ("Mapped property names are not allowed: Property '" +
-                    name + "' on bean class '" + bean.getClass() + "'");
-        }
-
-        // Handle DynaBean instances specially
-        if (bean instanceof DynaBean) {
-            final DynaProperty descriptor =
-                    ((DynaBean) bean).getDynaClass().getDynaProperty(name);
-            if (descriptor == null) {
-                throw new NoSuchMethodException("Unknown property '" +
-                        name + "' on dynaclass '" +
-                        ((DynaBean) bean).getDynaClass() + "'" );
-            }
-            return ((DynaBean) bean).get(name);
-        }
-
-        // Retrieve the property getter method for the specified property
-        final PropertyDescriptor descriptor =
-                getPropertyDescriptor(bean, name);
-        if (descriptor == null) {
-            throw new NoSuchMethodException("Unknown property '" +
-                    name + "' on class '" + bean.getClass() + "'" );
-        }
-        final Method readMethod = getReadMethod(bean.getClass(), descriptor);
-        if (readMethod == null) {
-            throw new NoSuchMethodException("Property '" + name +
-                    "' has no getter method in class '" + bean.getClass() + "'");
-        }
-
-        // Call the property getter and return the value
-        return invokeMethod(readMethod, bean, BeanUtils.EMPTY_OBJECT_ARRAY);
-    }
-
-    /**
-     * <p>Return an accessible property setter method for this property,
-     * if there is one; otherwise return {@code null}.</p>
-     *
-     * <p><em>Note:</em> This method does not work correctly with custom bean
-     * introspection under certain circumstances. It may return {@code null}
-     * even if a write method is defined for the property in question. Use
-     * {@link #getWriteMethod(Class, PropertyDescriptor)} to be sure that the
-     * correct result is returned.</p>
-     * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
-     *
-     * @param descriptor Property descriptor to return a setter for
-     * @return The write method
-     */
-    public Method getWriteMethod(final PropertyDescriptor descriptor) {
-        return MethodUtils.getAccessibleMethod(descriptor.getWriteMethod());
-    }
-
-    /**
-     * <p>Return an accessible property setter method for this property,
-     * if there is one; otherwise return {@code null}.</p>
-     *
-     * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
-     *
-     * @param clazz The class of the read method will be invoked on
-     * @param descriptor Property descriptor to return a setter for
-     * @return The write method
-     * @since 1.9.1
-     */
-    public Method getWriteMethod(final Class<?> clazz, final PropertyDescriptor descriptor) {
-        final BeanIntrospectionData data = getIntrospectionData(clazz);
-        return MethodUtils.getAccessibleMethod(clazz,
-                data.getWriteMethod(clazz, descriptor));
-    }
-
-    /**
-     * <p>Return {@code true} if the specified property name identifies
-     * a readable property on the specified bean; otherwise, return
-     * {@code false}.
-     *
-     * @param bean Bean to be examined (may be a {@link DynaBean}
-     * @param name Property name to be evaluated
-     * @return {@code true} if the property is readable,
-     * otherwise {@code false}
-     *
-     * @throws IllegalArgumentException if {@code bean}
-     *  or {@code name</code> is <code>null}
-     *
-     * @since BeanUtils 1.6
-     */
-    public boolean isReadable(Object bean, String name) {
-        // Validate method parameters
-        if (bean == null) {
-            throw new IllegalArgumentException("No bean specified");
-        }
-        if (name == null) {
-            throw new IllegalArgumentException("No name specified for bean class '" +
-                    bean.getClass() + "'");
-        }
-
-        // Resolve nested references
-        while (resolver.hasNested(name)) {
-            final String next = resolver.next(name);
-            Object nestedBean = null;
-            try {
-                nestedBean = getProperty(bean, next);
-            } catch (final IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
-                return false;
-            }
-            if (nestedBean == null) {
-                throw new NestedNullException
-                        ("Null property value for '" + next +
-                        "' on bean class '" + bean.getClass() + "'");
-            }
-            bean = nestedBean;
-            name = resolver.remove(name);
-        }
-
-        // Remove any subscript from the final name value
-        name = resolver.getProperty(name);
-
-        // Treat WrapDynaBean as special case - may be a write-only property
-        // (see Jira issue# BEANUTILS-61)
-        if (bean instanceof WrapDynaBean) {
-            bean = ((WrapDynaBean)bean).getInstance();
-        }
-
-        // Return the requested result
-        if (bean instanceof DynaBean) {
-            // All DynaBean properties are readable
-            return ((DynaBean) bean).getDynaClass().getDynaProperty(name) != null;
-        }
-        try {
-            final PropertyDescriptor desc =
-                getPropertyDescriptor(bean, name);
-            if (desc != null) {
-                Method readMethod = getReadMethod(bean.getClass(), desc);
-                if (readMethod == null) {
-                    if (desc instanceof IndexedPropertyDescriptor) {
-                        readMethod = ((IndexedPropertyDescriptor) desc).getIndexedReadMethod();
-                    } else if (desc instanceof MappedPropertyDescriptor) {
-                        readMethod = ((MappedPropertyDescriptor) desc).getMappedReadMethod();
-                    }
-                    readMethod = MethodUtils.getAccessibleMethod(bean.getClass(), readMethod);
-                }
-                return readMethod != null;
-            }
-            return false;
-        } catch (final IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
-            return false;
-        }
-    }
-
-    /**
-     * <p>Return {@code true} if the specified property name identifies
-     * a writable property on the specified bean; otherwise, return
-     * {@code false}.
-     *
-     * @param bean Bean to be examined (may be a {@link DynaBean}
-     * @param name Property name to be evaluated
-     * @return {@code true} if the property is writable,
-     * otherwise {@code false}
-     *
-     * @throws IllegalArgumentException if {@code bean}
-     *  or {@code name</code> is <code>null}
-     *
-     * @since BeanUtils 1.6
-     */
-    public boolean isWriteable(Object bean, String name) {
-        // Validate method parameters
-        if (bean == null) {
-            throw new IllegalArgumentException("No bean specified");
-        }
-        if (name == null) {
-            throw new IllegalArgumentException("No name specified for bean class '" +
-                    bean.getClass() + "'");
-        }
-
-        // Resolve nested references
-        while (resolver.hasNested(name)) {
-            final String next = resolver.next(name);
-            Object nestedBean = null;
-            try {
-                nestedBean = getProperty(bean, next);
-            } catch (final IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
-                return false;
-            }
-            if (nestedBean == null) {
-                throw new NestedNullException
-                        ("Null property value for '" + next +
-                        "' on bean class '" + bean.getClass() + "'");
-            }
-            bean = nestedBean;
-            name = resolver.remove(name);
-        }
-
-        // Remove any subscript from the final name value
-        name = resolver.getProperty(name);
-
-        // Treat WrapDynaBean as special case - may be a read-only property
-        // (see Jira issue# BEANUTILS-61)
-        if (bean instanceof WrapDynaBean) {
-            bean = ((WrapDynaBean)bean).getInstance();
-        }
-
-        // Return the requested result
-        if (bean instanceof DynaBean) {
-            // All DynaBean properties are writable
-            return ((DynaBean) bean).getDynaClass().getDynaProperty(name) != null;
-        }
-        try {
-            final PropertyDescriptor desc =
-                getPropertyDescriptor(bean, name);
-            if (desc != null) {
-                Method writeMethod = getWriteMethod(bean.getClass(), desc);
-                if (writeMethod == null) {
-                    if (desc instanceof IndexedPropertyDescriptor) {
-                        writeMethod = ((IndexedPropertyDescriptor) desc).getIndexedWriteMethod();
-                    } else if (desc instanceof MappedPropertyDescriptor) {
-                        writeMethod = ((MappedPropertyDescriptor) desc).getMappedWriteMethod();
-                    }
-                    writeMethod = MethodUtils.getAccessibleMethod(bean.getClass(), writeMethod);
-                }
-                return writeMethod != null;
-            }
-            return false;
-        } catch (final IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Sets the value of the specified indexed property of the specified
-     * bean, with no type conversions.  The zero-relative index of the
-     * required value must be included (in square brackets) as a suffix to
-     * the property name, or {@code IllegalArgumentException} will be
-     * thrown.  In addition to supporting the JavaBeans specification, this
-     * method has been extended to support {@code List} objects as well.
-     *
-     * @param bean Bean whose property is to be modified
-     * @param name {@code propertyname[index]} of the property value
-     *  to be modified
-     * @param value Value to which the specified property element
-     *  should be set
-     *
-     * @throws IndexOutOfBoundsException if the specified index
-     *  is outside the valid range for the underlying property
-     * @throws IllegalAccessException if the caller does not have
-     *  access to the property accessor method
-     * @throws IllegalArgumentException if {@code bean} or
-     *  {@code name} is null
-     * @throws InvocationTargetException if the property accessor method
-     *  throws an exception
-     * @throws NoSuchMethodException if an accessor method for this
-     *  property cannot be found
-     */
-    public void setIndexedProperty(final Object bean, String name,
-                                          final Object value)
-            throws IllegalAccessException, InvocationTargetException,
-            NoSuchMethodException {
-        if (bean == null) {
-            throw new IllegalArgumentException("No bean specified");
-        }
-        if (name == null) {
-            throw new IllegalArgumentException("No name specified for bean class '" +
-                    bean.getClass() + "'");
-        }
-
-        // Identify the index of the requested individual property
-        int index = -1;
-        try {
-            index = resolver.getIndex(name);
-        } catch (final IllegalArgumentException e) {
-            throw new IllegalArgumentException("Invalid indexed property '" +
-                    name + "' on bean class '" + bean.getClass() + "'");
-        }
-        if (index < 0) {
-            throw new IllegalArgumentException("Invalid indexed property '" +
-                    name + "' on bean class '" + bean.getClass() + "'");
-        }
-
-        // Isolate the name
-        name = resolver.getProperty(name);
-
-        // Set the specified indexed property value
-        setIndexedProperty(bean, name, index, value);
-    }
-
-    /**
-     * Sets the value of the specified indexed property of the specified
-     * bean, with no type conversions.  In addition to supporting the JavaBeans
-     * specification, this method has been extended to support
-     * {@code List} objects as well.
-     *
-     * @param bean Bean whose property is to be set
-     * @param name Simple property name of the property value to be set
-     * @param index Index of the property value to be set
-     * @param value Value to which the indexed property element is to be set
-     *
-     * @throws IndexOutOfBoundsException if the specified index
-     *  is outside the valid range for the underlying property
-     * @throws IllegalAccessException if the caller does not have
-     *  access to the property accessor method
-     * @throws IllegalArgumentException if {@code bean} or
-     *  {@code name} is null
-     * @throws InvocationTargetException if the property accessor method
-     *  throws an exception
-     * @throws NoSuchMethodException if an accessor method for this
-     *  property cannot be found
-     */
-    public void setIndexedProperty(final Object bean, final String name,
-                                          final int index, final Object value)
-            throws IllegalAccessException, InvocationTargetException,
-            NoSuchMethodException {
-        if (bean == null) {
-            throw new IllegalArgumentException("No bean specified");
-        }
-        if (name == null || name.isEmpty()) {
-            if (bean.getClass().isArray()) {
-                Array.set(bean, index, value);
-                return;
-            }
-            if (bean instanceof List) {
-                final List<Object> list = toObjectList(bean);
-                list.set(index, value);
-                return;
-            }
-        }
-        if (name == null) {
-            throw new IllegalArgumentException("No name specified for bean class '" +
-                    bean.getClass() + "'");
-        }
-
-        // Handle DynaBean instances specially
-        if (bean instanceof DynaBean) {
-            final DynaProperty descriptor =
-                    ((DynaBean) bean).getDynaClass().getDynaProperty(name);
-            if (descriptor == null) {
-                throw new NoSuchMethodException("Unknown property '" +
-                        name + "' on bean class '" + bean.getClass() + "'");
-            }
-            ((DynaBean) bean).set(name, index, value);
-            return;
-        }
-
-        // Retrieve the property descriptor for the specified property
-        final PropertyDescriptor descriptor =
-                getPropertyDescriptor(bean, name);
-        if (descriptor == null) {
-            throw new NoSuchMethodException("Unknown property '" +
-                    name + "' on bean class '" + bean.getClass() + "'");
-        }
-
-        // Call the indexed setter method if there is one
-        if (descriptor instanceof IndexedPropertyDescriptor) {
-            Method writeMethod = ((IndexedPropertyDescriptor) descriptor).
-                    getIndexedWriteMethod();
-            writeMethod = MethodUtils.getAccessibleMethod(bean.getClass(), writeMethod);
-            if (writeMethod != null) {
-                final Object[] subscript = new Object[2];
-                subscript[0] = Integer.valueOf(index);
-                subscript[1] = value;
-                try {
-                    if (log.isTraceEnabled()) {
-                        final String valueClassName =
-                            value == null ? "<null>"
-                                          : value.getClass().getName();
-                        log.trace("setSimpleProperty: Invoking method "
-                                  + writeMethod +" with index=" + index
-                                  + ", value=" + value
-                                  + " (class " + valueClassName+ ")");
-                    }
-                    invokeMethod(writeMethod, bean, subscript);
-                } catch (final InvocationTargetException e) {
-                    if (e.getTargetException() instanceof
-                            IndexOutOfBoundsException) {
-                        throw (IndexOutOfBoundsException)
-                                e.getTargetException();
-                    }
-                    throw e;
-                }
-                return;
-            }
-        }
-
-        // Otherwise, the underlying property must be an array or a list
-        final Method readMethod = getReadMethod(bean.getClass(), descriptor);
-        if (readMethod == null) {
-            throw new NoSuchMethodException("Property '" + name +
-                    "' has no getter method on bean class '" + bean.getClass() + "'");
-        }
-
-        // Call the property getter to get the array or list
-        final Object array = invokeMethod(readMethod, bean, BeanUtils.EMPTY_OBJECT_ARRAY);
-        if (!array.getClass().isArray()) {
-            if (!(array instanceof List)) {
-                throw new IllegalArgumentException("Property '" + name +
-                        "' is not indexed on bean class '" + bean.getClass() + "'");
-            }
-            // Modify the specified value in the List
-            final List<Object> list = toObjectList(array);
-            list.set(index, value);
-        } else {
-            // Modify the specified value in the array
-            Array.set(array, index, value);
-        }
-    }
-
-    /**
-     * Sets the value of the specified mapped property of the
-     * specified bean, with no type conversions.  The key of the
-     * value to set must be included (in brackets) as a suffix to
-     * the property name, or {@code IllegalArgumentException} will be
-     * thrown.
-     *
-     * @param bean Bean whose property is to be set
-     * @param name {@code propertyname(key)} of the property value
-     *  to be set
-     * @param value The property value to be set
-     *
-     * @throws IllegalAccessException if the caller does not have
-     *  access to the property accessor method
-     * @throws InvocationTargetException if the property accessor method
-     *  throws an exception
-     * @throws NoSuchMethodException if an accessor method for this
-     *  property cannot be found
-     */
-    public void setMappedProperty(final Object bean, String name,
-                                         final Object value)
-            throws IllegalAccessException, InvocationTargetException,
-            NoSuchMethodException {
-        if (bean == null) {
-            throw new IllegalArgumentException("No bean specified");
-        }
-        if (name == null) {
-            throw new IllegalArgumentException("No name specified for bean class '" +
-                    bean.getClass() + "'");
-        }
-
-        // Identify the key of the requested individual property
-        String key  = null;
-        try {
-            key = resolver.getKey(name);
-        } catch (final IllegalArgumentException e) {
-            throw new IllegalArgumentException
-                    ("Invalid mapped property '" + name +
-                    "' on bean class '" + bean.getClass() + "'");
-        }
-        if (key == null) {
-            throw new IllegalArgumentException
-                    ("Invalid mapped property '" + name +
-                    "' on bean class '" + bean.getClass() + "'");
-        }
-
-        // Isolate the name
-        name = resolver.getProperty(name);
-
-        // Request the specified indexed property value
-        setMappedProperty(bean, name, key, value);
-    }
-
-    /**
-     * Sets the value of the specified mapped property of the specified
-     * bean, with no type conversions.
-     *
-     * @param bean Bean whose property is to be set
-     * @param name Mapped property name of the property value to be set
-     * @param key Key of the property value to be set
-     * @param value The property value to be set
-     *
-     * @throws IllegalAccessException if the caller does not have
-     *  access to the property accessor method
-     * @throws InvocationTargetException if the property accessor method
-     *  throws an exception
-     * @throws NoSuchMethodException if an accessor method for this
-     *  property cannot be found
-     */
-    public void setMappedProperty(final Object bean, final String name,
-                                         final String key, final Object value)
-            throws IllegalAccessException, InvocationTargetException,
-            NoSuchMethodException {
-        if (bean == null) {
-            throw new IllegalArgumentException("No bean specified");
-        }
-        if (name == null) {
-            throw new IllegalArgumentException("No name specified for bean class '" +
-                    bean.getClass() + "'");
-        }
-        if (key == null) {
-            throw new IllegalArgumentException("No key specified for property '" +
-                    name + "' on bean class '" + bean.getClass() + "'");
-        }
-
-        // Handle DynaBean instances specially
-        if (bean instanceof DynaBean) {
-            final DynaProperty descriptor =
-                    ((DynaBean) bean).getDynaClass().getDynaProperty(name);
-            if (descriptor == null) {
-                throw new NoSuchMethodException("Unknown property '" +
-                        name + "' on bean class '" + bean.getClass() + "'");
-            }
-            ((DynaBean) bean).set(name, key, value);
-            return;
-        }
-
-        // Retrieve the property descriptor for the specified property
-        final PropertyDescriptor descriptor =
-                getPropertyDescriptor(bean, name);
-        if (descriptor == null) {
-            throw new NoSuchMethodException("Unknown property '" +
-                    name + "' on bean class '" + bean.getClass() + "'");
-        }
-
-        if (descriptor instanceof MappedPropertyDescriptor) {
-            // Call the keyed setter method if there is one
-            Method mappedWriteMethod =
-                    ((MappedPropertyDescriptor) descriptor).
-                    getMappedWriteMethod();
-            mappedWriteMethod = MethodUtils.getAccessibleMethod(bean.getClass(), mappedWriteMethod);
-            if (mappedWriteMethod == null) {
-                throw new NoSuchMethodException
-                    ("Property '" + name + "' has no mapped setter method" +
-                     "on bean class '" + bean.getClass() + "'");
-            }
-            final Object[] params = new Object[2];
-            params[0] = key;
-            params[1] = value;
-            if (log.isTraceEnabled()) {
-                final String valueClassName =
-                    value == null ? "<null>" : value.getClass().getName();
-                log.trace("setSimpleProperty: Invoking method "
-                          + mappedWriteMethod + " with key=" + key
-                          + ", value=" + value
-                          + " (class " + valueClassName +")");
-            }
-            invokeMethod(mappedWriteMethod, bean, params);
-        } else {
-          /* means that the result has to be retrieved from a map */
-          final Method readMethod = getReadMethod(bean.getClass(), descriptor);
-          if (readMethod == null) {
-            throw new NoSuchMethodException("Property '" + name +
-                    "' has no mapped getter method on bean class '" +
-                    bean.getClass() + "'");
-          }
-        final Object invokeResult = invokeMethod(readMethod, bean, BeanUtils.EMPTY_OBJECT_ARRAY);
-        /* test and fetch from the map */
-        if (invokeResult instanceof java.util.Map) {
-          final java.util.Map<String, Object> map = toPropertyMap(invokeResult);
-          map.put(key, value);
-        }
-        }
-    }
-
-    /**
-     * Sets the value of the (possibly nested) property of the specified
-     * name, for the specified bean, with no type conversions.
-     * <p>
-     * Example values for parameter "name" are:
-     * <ul>
-     * <li> "a" -- sets the value of property a of the specified bean </li>
-     * <li> "a.b" -- gets the value of property a of the specified bean,
-     * then on that object sets the value of property b.</li>
-     * <li> "a(key)" -- sets a value of mapped-property a on the specified
-     * bean. This effectively means bean.setA("key").</li>
-     * <li> "a[3]" -- sets a value of indexed-property a on the specified
-     * bean. This effectively means bean.setA(3).</li>
-     * </ul>
-     *
-     * @param bean Bean whose property is to be modified
-     * @param name Possibly nested name of the property to be modified
-     * @param value Value to which the property is to be set
-     *
-     * @throws IllegalAccessException if the caller does not have
-     *  access to the property accessor method
-     * @throws IllegalArgumentException if {@code bean} or
-     *  {@code name} is null
-     * @throws IllegalArgumentException if a nested reference to a
-     *  property returns null
-     * @throws InvocationTargetException if the property accessor method
-     *  throws an exception
-     * @throws NoSuchMethodException if an accessor method for this
-     *  property cannot be found
-     */
-    public void setNestedProperty(Object bean,
-                                         String name, final Object value)
-            throws IllegalAccessException, InvocationTargetException,
-            NoSuchMethodException {
-        if (bean == null) {
-            throw new IllegalArgumentException("No bean specified");
-        }
-        if (name == null) {
-            throw new IllegalArgumentException("No name specified for bean class '" +
-                    bean.getClass() + "'");
-        }
-
-        // Resolve nested references
-        while (resolver.hasNested(name)) {
-            final String next = resolver.next(name);
-            Object nestedBean = null;
-            if (bean instanceof Map) {
-                nestedBean = getPropertyOfMapBean((Map<?, ?>)bean, next);
-            } else if (resolver.isMapped(next)) {
-                nestedBean = getMappedProperty(bean, next);
-            } else if (resolver.isIndexed(next)) {
-                nestedBean = getIndexedProperty(bean, next);
-            } else {
-                nestedBean = getSimpleProperty(bean, next);
-            }
-            if (nestedBean == null) {
-                throw new NestedNullException
-                        ("Null property value for '" + name +
-                         "' on bean class '" + bean.getClass() + "'");
-            }
-            bean = nestedBean;
-            name = resolver.remove(name);
-        }
-
-        if (bean instanceof Map) {
-            setPropertyOfMapBean(toPropertyMap(bean), name, value);
-        } else if (resolver.isMapped(name)) {
-            setMappedProperty(bean, name, value);
-        } else if (resolver.isIndexed(name)) {
-            setIndexedProperty(bean, name, value);
-        } else {
-            setSimpleProperty(bean, name, value);
-        }
-    }
-
-    /**
-     * This method is called by method setNestedProperty when the current bean
-     * is found to be a Map object, and defines how to deal with setting
-     * a property on a Map.
-     * <p>
-     * The standard implementation here is to:
-     * <ul>
-     * <li>call bean.set(propertyName) for all propertyName values.</li>
-     * <li>throw an IllegalArgumentException if the property specifier
-     * contains MAPPED_DELIM or INDEXED_DELIM, as Map entries are essentially
-     * simple properties; mapping and indexing operations do not make sense
-     * when accessing a map (even thought the returned object may be a Map
-     * or an Array).</li>
-     * </ul>
-     * <p>
-     * The default behavior of beanutils 1.7.1 or later is for assigning to
-     * "a.b" to mean a.put(b, obj) always. However the behavior of beanutils
-     * version 1.6.0, 1.6.1, 1.7.0 was for "a.b" to mean a.setB(obj) if such
-     * a method existed, and a.put(b, obj) otherwise. In version 1.5 it meant
-     * a.put(b, obj) always (ie the same as the behavior in the current version).
-     * In versions prior to 1.5 it meant a.setB(obj) always. [yes, this is
-     * all <i>very</i> unfortunate]
-     * <p>
-     * Users who would like to customize the meaning of "a.b" in method
-     * setNestedProperty when a is a Map can create a custom subclass of
-     * this class and override this method to implement the behavior of
-     * their choice, such as restoring the pre-1.4 behavior of this class
-     * if they wish. When overriding this method, do not forget to deal
-     * with MAPPED_DELIM and INDEXED_DELIM characters in the propertyName.
-     * <p>
-     * Note, however, that the recommended solution for objects that
-     * implement Map but want their simple properties to come first is
-     * for <i>those</i> objects to override their get/put methods to implement
-     * that behavior, and <i>not</i> to solve the problem by modifying the
-     * default behavior of the PropertyUtilsBean class by overriding this
-     * method.
-     *
-     * @param bean Map bean
-     * @param propertyName The property name
-     * @param value the property value
-     *
-     * @throws IllegalArgumentException when the propertyName is regarded as
-     * being invalid.
-     *
-     * @throws IllegalAccessException just in case subclasses override this
-     * method to try to access real setter methods and find permission is denied.
-     *
-     * @throws InvocationTargetException just in case subclasses override this
-     * method to try to access real setter methods, and find it throws an
-     * exception when invoked.
-     *
-     * @throws NoSuchMethodException just in case subclasses override this
-     * method to try to access real setter methods, and want to fail if
-     * no simple method is available.
-     * @since 1.8.0
-     */
-    protected void setPropertyOfMapBean(final Map<String, Object> bean, String propertyName, final Object value)
-        throws IllegalArgumentException, IllegalAccessException,
-        InvocationTargetException, NoSuchMethodException {
-        if (resolver.isMapped(propertyName)) {
-            final String name = resolver.getProperty(propertyName);
-            if (name == null || name.isEmpty()) {
-                propertyName = resolver.getKey(propertyName);
-            }
-        }
-
-        if (resolver.isIndexed(propertyName) ||
-            resolver.isMapped(propertyName)) {
-            throw new IllegalArgumentException(
-                    "Indexed or mapped properties are not supported on"
-                    + " objects of type Map: " + propertyName);
-        }
-
-        bean.put(propertyName, value);
-    }
-
-    /**
-     * Sets the value of the specified property of the specified bean,
-     * no matter which property reference format is used, with no
-     * type conversions.
-     *
-     * @param bean Bean whose property is to be modified
-     * @param name Possibly indexed and/or nested name of the property
-     *  to be modified
-     * @param value Value to which this property is to be set
-     *
-     * @throws IllegalAccessException if the caller does not have
-     *  access to the property accessor method
-     * @throws IllegalArgumentException if {@code bean} or
-     *  {@code name} is null
-     * @throws InvocationTargetException if the property accessor method
-     *  throws an exception
-     * @throws NoSuchMethodException if an accessor method for this
-     *  property cannot be found
-     */
-    public void setProperty(final Object bean, final String name, final Object value)
-            throws IllegalAccessException, InvocationTargetException,
-            NoSuchMethodException {
-        setNestedProperty(bean, name, value);
-    }
-
-    /**
-     * Sets the value of the specified simple property of the specified bean,
-     * with no type conversions.
-     *
-     * @param bean Bean whose property is to be modified
-     * @param name Name of the property to be modified
-     * @param value Value to which the property should be set
-     *
-     * @throws IllegalAccessException if the caller does not have
-     *  access to the property accessor method
-     * @throws IllegalArgumentException if {@code bean} or
-     *  {@code name} is null
-     * @throws IllegalArgumentException if the property name is
-     *  nested or indexed
-     * @throws InvocationTargetException if the property accessor method
-     *  throws an exception
-     * @throws NoSuchMethodException if an accessor method for this
-     *  property cannot be found
-     */
-    public void setSimpleProperty(final Object bean,
-                                         final String name, final Object value)
-            throws IllegalAccessException, InvocationTargetException,
-        NoSuchMethodException {
-        if (bean == null) {
-            throw new IllegalArgumentException("No bean specified");
-        }
-        final Class<?> beanClass = bean.getClass();
-        if (name == null) {
-            throw new IllegalArgumentException("No name specified for bean class '" + beanClass + "'");
-        }
-
-        // Validate the syntax of the property name
-        if (resolver.hasNested(name)) {
-            throw new IllegalArgumentException("Nested property names are not allowed: Property '" + name + "' on bean class '" + beanClass + "'");
-        }
-        if (resolver.isIndexed(name)) {
-            throw new IllegalArgumentException("Indexed property names are not allowed: Property '" + name + "' on bean class '" + beanClass + "'");
-        }
-        if (resolver.isMapped(name)) {
-            throw new IllegalArgumentException("Mapped property names are not allowed: Property '" + name + "' on bean class '" + beanClass + "'");
-        }
-
-        // Handle DynaBean instances specially
-        if (bean instanceof DynaBean) {
-            final DynaProperty descriptor = ((DynaBean) bean).getDynaClass().getDynaProperty(name);
-            if (descriptor == null) {
-                throw new NoSuchMethodException("Unknown property '" + name + "' on dynaclass '" + ((DynaBean) bean).getDynaClass() + "'");
-            }
-            ((DynaBean) bean).set(name, value);
-            return;
-        }
-
-        // Retrieve the property setter method for the specified property
-        final PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
-        if (descriptor == null) {
-            throw new NoSuchMethodException("Unknown property '" + name + "' on class '" + beanClass + "'");
-        }
-        final Method writeMethod = getWriteMethod(beanClass, descriptor);
-        if (writeMethod == null) {
-            throw new NoSuchMethodException("Property '" + name + "' has no setter method in class '" + beanClass + "'");
-        }
-
-        // Call the property setter method
-        final Object[] values = new Object[1];
-        values[0] = value;
-        if (log.isTraceEnabled()) {
-            final String valueClassName = value == null ? "<null>" : value.getClass().getName();
-            log.trace("setSimpleProperty: Invoking method " + writeMethod + " with value " + value + " (class " + valueClassName + ")");
-        }
-        invokeMethod(writeMethod, bean, values);
-    }
-
-    /** This just catches and wraps IllegalArgumentException. */
-    private Object invokeMethod(
-                        final Method method,
-                        final Object bean,
-                        final Object[] values)
-                            throws
-                                IllegalAccessException,
-                                InvocationTargetException {
-        if (bean == null) {
-            throw new IllegalArgumentException("No bean specified " +
-                "- this should have been checked before reaching this method");
-        }
-
-        try {
-
-            return method.invoke(bean, values);
-
-        } catch (final NullPointerException | IllegalArgumentException cause) {
-            // JDK 1.3 and JDK 1.4 throw NullPointerException if an argument is
-            // null for a primitive value (JDK 1.5+ throw IllegalArgumentException)
-            final StringBuilder valueString = new StringBuilder();
-            if (values != null) {
-                for (int i = 0; i < values.length; i++) {
-                    if (i>0) {
-                        valueString.append(", ");
-                    }
-                    if (values[i] == null) {
-                        valueString.append("<null>");
-                    } else {
-                        valueString.append(values[i].getClass().getName());
-                    }
-                }
-            }
-            final StringBuilder expectedString = new StringBuilder();
-            final Class<?>[] parTypes = method.getParameterTypes();
-            if (parTypes != null) {
-                for (int i = 0; i < parTypes.length; i++) {
-                    if (i > 0) {
-                        expectedString.append(", ");
-                    }
-                    expectedString.append(parTypes[i].getName());
-                }
-            }
-            final IllegalArgumentException e = new IllegalArgumentException(
-                "Cannot invoke " + method.getDeclaringClass().getName() + "."
-                + method.getName() + " on bean class '" + bean.getClass() +
-                "' - " + cause.getMessage()
-                // as per https://issues.apache.org/jira/browse/BEANUTILS-224
-                + " - had objects of type \"" + valueString
-                + "\" but expected signature \""
-                +   expectedString + "\""
-                );
-            if (!BeanUtils.initCause(e, cause)) {
-                log.error("Method invocation failed", cause);
-            }
-            throw e;
-        }
-    }
-
-    /**
-     * Obtains the {@code BeanIntrospectionData} object describing the specified bean
-     * class. This object is looked up in the internal cache. If necessary, introspection
-     * is performed now on the affected bean class, and the results object is created.
-     *
-     * @param beanClass the bean class in question
-     * @return the {@code BeanIntrospectionData} object for this class
-     * @throws IllegalArgumentException if the bean class is <b>null</b>
-     */
-    private BeanIntrospectionData getIntrospectionData(final Class<?> beanClass) {
-        if (beanClass == null) {
-            throw new IllegalArgumentException("No bean class specified");
-        }
-
-        // Look up any cached information for this bean class
-        BeanIntrospectionData data = descriptorsCache.get(beanClass);
-        if (data == null) {
-            data = fetchIntrospectionData(beanClass);
-            descriptorsCache.put(beanClass, data);
-        }
-
-        return data;
-    }
-
-    /**
-     * Performs introspection on the specified class. This method invokes all {@code BeanIntrospector} objects that were
-     * added to this instance.
-     *
-     * @param beanClass the class to be inspected
-     * @return a data object with the results of introspection
-     */
-    private BeanIntrospectionData fetchIntrospectionData(final Class<?> beanClass) {
-        final DefaultIntrospectionContext ictx = new DefaultIntrospectionContext(beanClass);
-
-        for (final BeanIntrospector bi : introspectors) {
-            try {
-                bi.introspect(ictx);
-            } catch (final IntrospectionException iex) {
-                log.error("Exception during introspection", iex);
-            }
-        }
-
-        return new BeanIntrospectionData(ictx.getPropertyDescriptors());
-    }
-
-    /**
-     * Converts an object to a list of objects. This method is used when dealing
-     * with indexed properties. It assumes that indexed properties are stored as
-     * lists of objects.
-     *
-     * @param obj the object to be converted
-     * @return the resulting list of objects
-     */
-    private static List<Object> toObjectList(final Object obj) {
-        @SuppressWarnings("unchecked")
-        final
-        // indexed properties are stored in lists of objects
-        List<Object> list = (List<Object>) obj;
-        return list;
-    }
-
-    /**
-     * Converts an object to a map with property values. This method is used
-     * when dealing with mapped properties. It assumes that mapped properties
-     * are stored in a Map&lt;String, Object&gt;.
-     *
-     * @param obj the object to be converted
-     * @return the resulting properties map
-     */
-    private static Map<String, Object> toPropertyMap(final Object obj) {
-        @SuppressWarnings("unchecked")
-        final
-        // mapped properties are stores in maps of type <String, Object>
-        Map<String, Object> map = (Map<String, Object>) obj;
-        return map;
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.beanutils2;
+
+import java.beans.IndexedPropertyDescriptor;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.commons.beanutils2.expression.DefaultResolver;
+import org.apache.commons.beanutils2.expression.Resolver;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Utility methods for using Java Reflection APIs to facilitate generic
+ * property getter and setter operations on Java objects.  Much of this
+ * code was originally included in {@code BeanUtils}, but has been
+ * separated because of the volume of code involved.
+ * <p>
+ * In general, the objects that are examined and modified using these
+ * methods are expected to conform to the property getter and setter method
+ * naming conventions described in the JavaBeans Specification (Version 1.0.1).
+ * No data type conversions are performed, and there are no usage of any
+ * {@code PropertyEditor} classes that have been registered, although
+ * a convenient way to access the registered classes themselves is included.
+ * <p>
+ * For the purposes of this class, five formats for referencing a particular
+ * property value of a bean are defined, with the <i>default</i> layout of an
+ * identifying String in parentheses. However the notation for these formats
+ * and how they are resolved is now (since BeanUtils 1.8.0) controlled by
+ * the configured {@link Resolver} implementation:
+ * <ul>
+ * <li><strong>Simple ({@code name})</strong> - The specified
+ *     {@code name} identifies an individual property of a particular
+ *     JavaBean.  The name of the actual getter or setter method to be used
+ *     is determined using standard JavaBeans introspection, so that (unless
+ *     overridden by a {@code BeanInfo} class, a property named "xyz"
+ *     will have a getter method named {@code getXyz()} or (for boolean
+ *     properties only) {@code isXyz()}, and a setter method named
+ *     {@code setXyz()}.</li>
+ * <li><strong>Nested ({@code name1.name2.name3})</strong> The first
+ *     name element is used to select a property getter, as for simple
+ *     references above.  The object returned for this property is then
+ *     consulted, using the same approach, for a property getter for a
+ *     property named {@code name2}, and so on.  The property value that
+ *     is ultimately retrieved or modified is the one identified by the
+ *     last name element.</li>
+ * <li><strong>Indexed ({@code name[index]})</strong> - The underlying
+ *     property value is assumed to be an array, or this JavaBean is assumed
+ *     to have indexed property getter and setter methods.  The appropriate
+ *     (zero-relative) entry in the array is selected.  {@code List}
+ *     objects are now also supported for read/write.  You simply need to define
+ *     a getter that returns the {@code List}</li>
+ * <li><strong>Mapped ({@code name(key)})</strong> - The JavaBean
+ *     is assumed to have an property getter and setter methods with an
+ *     additional attribute of type {@code java.lang.String}.</li>
+ * <li><strong>Combined ({@code name1.name2[index].name3(key)})</strong> -
+ *     Combining mapped, nested, and indexed references is also
+ *     supported.</li>
+ * </ul>
+ *
+ * @see Resolver
+ * @see PropertyUtils
+ * @since 1.7
+ */
+public class PropertyUtilsBean {
+
+    private Resolver resolver = new DefaultResolver();
+
+    /**
+     * Gets the PropertyUtils bean instance.
+     * @return The PropertyUtils bean instance
+     */
+    protected static PropertyUtilsBean getInstance() {
+        return BeanUtilsBean.getInstance().getPropertyUtils();
+    }
+
+    /**
+     * The cache of PropertyDescriptor arrays for beans we have already
+     * introspected, keyed by the java.lang.Class of this object.
+     */
+    private final WeakFastHashMap<Class<?>, BeanIntrospectionData> descriptorsCache;
+    private final WeakFastHashMap<Class<?>, Map> mappedDescriptorsCache;
+
+    /** Log instance */
+    private final Log log = LogFactory.getLog(PropertyUtilsBean.class);
+
+    /** The list with BeanIntrospector objects. */
+    private final List<BeanIntrospector> introspectors;
+
+    /** Base constructor */
+    public PropertyUtilsBean() {
+        descriptorsCache = new WeakFastHashMap<>();
+        descriptorsCache.setFast(true);
+        mappedDescriptorsCache = new WeakFastHashMap<>();
+        mappedDescriptorsCache.setFast(true);
+        introspectors = new CopyOnWriteArrayList<>();
+        resetBeanIntrospectors();
+    }
+
+    /**
+     * Gets the configured {@link Resolver} implementation used by BeanUtils.
+     * <p>
+     * The {@link Resolver} handles the <i>property name</i>
+     * expressions and the implementation in use effectively
+     * controls the dialect of the <i>expression language</i>
+     * that BeanUtils recognizes.
+     * <p>
+     * {@link DefaultResolver} is the default implementation used.
+     *
+     * @return resolver The property expression resolver.
+     * @since 1.8.0
+     */
+    public Resolver getResolver() {
+        return resolver;
+    }
+
+    /**
+     * Configure the {@link Resolver} implementation used by BeanUtils.
+     * <p>
+     * The {@link Resolver} handles the <i>property name</i>
+     * expressions and the implementation in use effectively
+     * controls the dialect of the <i>expression language</i>
+     * that BeanUtils recognizes.
+     * <p>
+     * {@link DefaultResolver} is the default implementation used.
+     *
+     * @param resolver The property expression resolver.
+     * @since 1.8.0
+     */
+    public void setResolver(final Resolver resolver) {
+        if (resolver == null) {
+            this.resolver = new DefaultResolver();
+        } else {
+            this.resolver = resolver;
+        }
+    }
+
+    /**
+     * Resets the {@link BeanIntrospector} objects registered at this instance. After this
+     * method was called, only the default {@code BeanIntrospector} is registered.
+     *
+     * @since 1.9
+     */
+    public final void resetBeanIntrospectors() {
+        introspectors.clear();
+        introspectors.add(DefaultBeanIntrospector.INSTANCE);
+        introspectors.add(SuppressPropertiesBeanIntrospector.SUPPRESS_CLASS);
+    }
+
+    /**
+     * Adds a {@code BeanIntrospector}. This object is invoked when the
+     * property descriptors of a class need to be obtained.
+     *
+     * @param introspector the {@code BeanIntrospector} to be added (must
+     *        not be <b>null</b>
+     * @throws IllegalArgumentException if the argument is <b>null</b>
+     * @since 1.9
+     */
+    public void addBeanIntrospector(final BeanIntrospector introspector) {
+        if (introspector == null) {
+            throw new IllegalArgumentException(
+                    "BeanIntrospector must not be null!");
+        }
+        introspectors.add(introspector);
+    }
+
+    /**
+     * Removes the specified {@code BeanIntrospector}.
+     *
+     * @param introspector the {@code BeanIntrospector} to be removed
+     * @return <b>true</b> if the {@code BeanIntrospector} existed and
+     *         could be removed, <b>false</b> otherwise
+     * @since 1.9
+     */
+    public boolean removeBeanIntrospector(final BeanIntrospector introspector) {
+        return introspectors.remove(introspector);
+    }
+
+    /**
+     * Clear any cached property descriptors information for all classes
+     * loaded by any class loaders.  This is useful in cases where class
+     * loaders are thrown away to implement class reloading.
+     */
+    public void clearDescriptors() {
+        descriptorsCache.clear();
+        mappedDescriptorsCache.clear();
+        Introspector.flushCaches();
+    }
+
+    /**
+     * <p>Copy property values from the "origin" bean to the "destination" bean
+     * for all cases where the property names are the same (even though the
+     * actual getter and setter methods might have been customized via
+     * {@code BeanInfo} classes).  No conversions are performed on the
+     * actual property values -- it is assumed that the values retrieved from
+     * the origin bean are assignment-compatible with the types expected by
+     * the destination bean.</p>
+     *
+     * <p>If the origin "bean" is actually a {@code Map}, it is assumed
+     * to contain String-valued <strong>simple</strong> property names as the keys, pointing
+     * at the corresponding property values that will be set in the destination
+     * bean.<strong>Note</strong> that this method is intended to perform
+     * a "shallow copy" of the properties and so complex properties
+     * (for example, nested ones) will not be copied.</p>
+     *
+     * <p>Note, that this method will not copy a List to a List, or an Object[]
+     * to an Object[]. It's specifically for copying JavaBean properties. </p>
+     *
+     * @param dest Destination bean whose properties are modified
+     * @param orig Origin bean whose properties are retrieved
+     *
+     * @throws IllegalAccessException if the caller does not have
+     *  access to the property accessor method
+     * @throws IllegalArgumentException if the {@code dest} or
+     *  {@code orig} argument is null
+     * @throws InvocationTargetException if the property accessor method
+     *  throws an exception
+     * @throws NoSuchMethodException if an accessor method for this
+     *  property cannot be found
+     */
+    public void copyProperties(final Object dest, final Object orig)
+            throws IllegalAccessException, InvocationTargetException,
+            // TODO BEFORE 2.0
+            // MISMATCH between implementation and Javadoc.
+        NoSuchMethodException {
+        if (dest == null) {
+            throw new IllegalArgumentException("No destination bean specified");
+        }
+        if (orig == null) {
+            throw new IllegalArgumentException("No origin bean specified");
+        }
+
+        if (orig instanceof DynaBean) {
+            final DynaProperty[] origDescriptors = ((DynaBean) orig).getDynaClass().getDynaProperties();
+            for (final DynaProperty origDescriptor : origDescriptors) {
+                final String name = origDescriptor.getName();
+                if (isReadable(orig, name) && isWriteable(dest, name)) {
+                    try {
+                        final Object value = ((DynaBean) orig).get(name);
+                        if (dest instanceof DynaBean) {
+                            ((DynaBean) dest).set(name, value);
+                        } else {
+                            setSimpleProperty(dest, name, value);
+                        }
+                    } catch (final NoSuchMethodException e) {
+                        if (log.isDebugEnabled()) {
+                            log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", e);
+                        }
+                    }
+                }
+            }
+        } else if (orig instanceof Map) {
+            for (final Map.Entry<?, ?> entry : ((Map<?, ?>) orig).entrySet()) {
+                final String name = (String) entry.getKey();
+                if (isWriteable(dest, name)) {
+                    try {
+                        if (dest instanceof DynaBean) {
+                            ((DynaBean) dest).set(name, entry.getValue());
+                        } else {
+                            setSimpleProperty(dest, name, entry.getValue());
+                        }
+                    } catch (final NoSuchMethodException e) {
+                        if (log.isDebugEnabled()) {
+                            log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", e);
+                        }
+                    }
+                }
+            }
+        } else /* if (orig is a standard JavaBean) */ {
+            final PropertyDescriptor[] origDescriptors = getPropertyDescriptors(orig);
+            for (final PropertyDescriptor origDescriptor : origDescriptors) {
+                final String name = origDescriptor.getName();
+                if (isReadable(orig, name) && isWriteable(dest, name)) {
+                    try {
+                        final Object value = getSimpleProperty(orig, name);
+                        if (dest instanceof DynaBean) {
+                            ((DynaBean) dest).set(name, value);
+                        } else {
+                            setSimpleProperty(dest, name, value);
+                        }
+                    } catch (final NoSuchMethodException e) {
+                        if (log.isDebugEnabled()) {
+                            log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", e);
+                        }
+                    }
+                }
+            }
+        }
+
+    }
+
+    /**
+     * <p>Return the entire set of properties for which the specified bean
+     * provides a read method.  This map contains the unconverted property
+     * values for all properties for which a read method is provided
+     * (i.e. where the {@code getReadMethod()} returns non-null).</p>
+     *
+     * <p><strong>FIXME</strong> - Does not account for mapped properties.</p>
+     *
+     * @param bean Bean whose properties are to be extracted
+     * @return The set of properties for the bean
+     *
+     * @throws IllegalAccessException if the caller does not have
+     *  access to the property accessor method
+     * @throws IllegalArgumentException if {@code bean} is null
+     * @throws InvocationTargetException if the property accessor method
+     *  throws an exception
+     * @throws NoSuchMethodException if an accessor method for this
+     *  property cannot be found
+     */
+    public Map<String, Object> describe(final Object bean)
+            throws IllegalAccessException, InvocationTargetException,
+            NoSuchMethodException {
+
+        if (bean == null) {
+            throw new IllegalArgumentException("No bean specified");
+        }
+        final Map<String, Object> description = new HashMap<>();
+        if (bean instanceof DynaBean) {
+            final DynaProperty[] descriptors =
+                ((DynaBean) bean).getDynaClass().getDynaProperties();
+            for (final DynaProperty descriptor : descriptors) {
+                final String name = descriptor.getName();
+                description.put(name, getProperty(bean, name));
+            }
+        } else {
+            final PropertyDescriptor[] descriptors =
+                getPropertyDescriptors(bean);
+            for (final PropertyDescriptor descriptor : descriptors) {
+                final String name = descriptor.getName();
+                if (descriptor.getReadMethod() != null) {
+                    description.put(name, getProperty(bean, name));
+                }
+            }
+        }
+        return description;
+    }
+
+    /**
+     * Gets the value of the specified indexed property of the specified
+     * bean, with no type conversions.  The zero-relative index of the
+     * required value must be included (in square brackets) as a suffix to
+     * the property name, or {@code IllegalArgumentException} will be
+     * thrown.  In addition to supporting the JavaBeans specification, this
+     * method has been extended to support {@code List} objects as well.
+     *
+     * @param bean Bean whose property is to be extracted
+     * @param name {@code propertyname[index]} of the property value
+     *  to be extracted
+     * @return the indexed property value
+     *
+     * @throws IndexOutOfBoundsException if the specified index
+     *  is outside the valid range for the underlying array or List
+     * @throws IllegalAccessException if the caller does not have
+     *  access to the property accessor method
+     * @throws IllegalArgumentException if {@code bean} or
+     *  {@code name} is null
+     * @throws InvocationTargetException if the property accessor method
+     *  throws an exception
+     * @throws NoSuchMethodException if an accessor method for this
+     *  property cannot be found
+     */
+    public Object getIndexedProperty(final Object bean, String name)
+            throws IllegalAccessException, InvocationTargetException,
+            NoSuchMethodException {
+        if (bean == null) {
+            throw new IllegalArgumentException("No bean specified");
+        }
+        if (name == null) {
+            throw new IllegalArgumentException("No name specified for bean class '" +
+                    bean.getClass() + "'");
+        }
+
+        // Identify the index of the requested individual property
+        int index = -1;
+        try {
+            index = resolver.getIndex(name);
+        } catch (final IllegalArgumentException e) {
+            throw new IllegalArgumentException("Invalid indexed property '" +
+                    name + "' on bean class '" + bean.getClass() + "' " +
+                    e.getMessage());
+        }
+        if (index < 0) {
+            throw new IllegalArgumentException("Invalid indexed property '" +
+                    name + "' on bean class '" + bean.getClass() + "'");
+        }
+
+        // Isolate the name
+        name = resolver.getProperty(name);
+
+        // Request the specified indexed property value
+        return getIndexedProperty(bean, name, index);
+    }
+
+    /**
+     * Gets the value of the specified indexed property of the specified
+     * bean, with no type conversions.  In addition to supporting the JavaBeans
+     * specification, this method has been extended to support
+     * {@code List} objects as well.
+     *
+     * @param bean Bean whose property is to be extracted
+     * @param name Simple property name of the property value to be extracted
+     * @param index Index of the property value to be extracted
+     * @return the indexed property value
+     *
+     * @throws IndexOutOfBoundsException if the specified index
+     *  is outside the valid range for the underlying property
+     * @throws IllegalAccessException if the caller does not have
+     *  access to the property accessor method
+     * @throws IllegalArgumentException if {@code bean} or
+     *  {@code name} is null
+     * @throws InvocationTargetException if the property accessor method
+     *  throws an exception
+     * @throws NoSuchMethodException if an accessor method for this
+     *  property cannot be found
+     */
+    public Object getIndexedProperty(final Object bean,
+                                            final String name, final int index)
+            throws IllegalAccessException, InvocationTargetException,
+            NoSuchMethodException {
+        if (bean == null) {
+            throw new IllegalArgumentException("No bean specified");
+        }
+        if (name == null || name.isEmpty()) {
+            if (bean.getClass().isArray()) {
+                return Array.get(bean, index);
+            }
+            if (bean instanceof List) {
+                return ((List<?>)bean).get(index);
+            }
+        }
+        if (name == null) {
+            throw new IllegalArgumentException("No name specified for bean class '" +
+                    bean.getClass() + "'");
+        }
+
+        // Handle DynaBean instances specially
+        if (bean instanceof DynaBean) {
+            final DynaProperty descriptor =
+                    ((DynaBean) bean).getDynaClass().getDynaProperty(name);
+            if (descriptor == null) {
+                throw new NoSuchMethodException("Unknown property '" +
+                    name + "' on bean class '" + bean.getClass() + "'");
+            }
+            return ((DynaBean) bean).get(name, index);
+        }
+
+        // Retrieve the property descriptor for the specified property
+        final PropertyDescriptor descriptor =
+                getPropertyDescriptor(bean, name);
+        if (descriptor == null) {
+            throw new NoSuchMethodException("Unknown property '" +
+                    name + "' on bean class '" + bean.getClass() + "'");
+        }
+
+        // Call the indexed getter method if there is one
+        if (descriptor instanceof IndexedPropertyDescriptor) {
+            Method readMethod = ((IndexedPropertyDescriptor) descriptor).
+                    getIndexedReadMethod();
+            readMethod = MethodUtils.getAccessibleMethod(bean.getClass(), readMethod);
+            if (readMethod != null) {
+                final Object[] subscript = new Object[1];
+                subscript[0] = Integer.valueOf(index);
+                try {
+                    return invokeMethod(readMethod,bean, subscript);
+                } catch (final InvocationTargetException e) {
+                    if (e.getTargetException() instanceof
+                            IndexOutOfBoundsException) {
+                        throw (IndexOutOfBoundsException)
+                                e.getTargetException();
+                    }
+                    throw e;
+                }
+            }
+        }
+
+        // Otherwise, the underlying property must be an array
+        final Method readMethod = getReadMethod(bean.getClass(), descriptor);
+        if (readMethod == null) {
+            throw new NoSuchMethodException("Property '" + name + "' has no " +
+                    "getter method on bean class '" + bean.getClass() + "'");
+        }
+
+        // Call the property getter and return the value
+        final Object value = invokeMethod(readMethod, bean, BeanUtils.EMPTY_OBJECT_ARRAY);
+        if (!value.getClass().isArray()) {
+            if (!(value instanceof java.util.List)) {
+                throw new IllegalArgumentException("Property '" + name +
+                        "' is not indexed on bean class '" + bean.getClass() + "'");
+            }
+            //get the List's value
+            return ((java.util.List<?>) value).get(index);
+        }
+        //get the array's value
+        try {
+            return Array.get(value, index);
+        } catch (final ArrayIndexOutOfBoundsException e) {
+            throw new ArrayIndexOutOfBoundsException("Index: " +
+                    index + ", Size: " + Array.getLength(value) +
+                    " for property '" + name + "'");
+        }
+    }
+
+    /**
+     * Gets the value of the specified mapped property of the
+     * specified bean, with no type conversions.  The key of the
+     * required value must be included (in brackets) as a suffix to
+     * the property name, or {@code IllegalArgumentException} will be
+     * thrown.
+     *
+     * @param bean Bean whose property is to be extracted
+     * @param name {@code propertyname(key)} of the property value
+     *  to be extracted
+     * @return the mapped property value
+     *
+     * @throws IllegalAccessException if the caller does not have
+     *  access to the property accessor method
+     * @throws InvocationTargetException if the property accessor method
+     *  throws an exception
+     * @throws NoSuchMethodException if an accessor method for this
+     *  property cannot be found
+     */
+    public Object getMappedProperty(final Object bean, String name)
+            throws IllegalAccessException, InvocationTargetException,
+            NoSuchMethodException {
+        if (bean == null) {
+            throw new IllegalArgumentException("No bean specified");
+        }
+        if (name == null) {
+            throw new IllegalArgumentException("No name specified for bean class '" +
+                    bean.getClass() + "'");
+        }
+
+        // Identify the key of the requested individual property
+        String key  = null;
+        try {
+            key = resolver.getKey(name);
+        } catch (final IllegalArgumentException e) {
+            throw new IllegalArgumentException
+                    ("Invalid mapped property '" + name +
+                    "' on bean class '" + bean.getClass() + "' " + e.getMessage());
+        }
+        if (key == null) {
+            throw new IllegalArgumentException("Invalid mapped property '" +
+                    name + "' on bean class '" + bean.getClass() + "'");
+        }
+
+        // Isolate the name
+        name = resolver.getProperty(name);
+
+        // Request the specified indexed property value
+        return getMappedProperty(bean, name, key);
+    }
+
+    /**
+     * Gets the value of the specified mapped property of the specified
+     * bean, with no type conversions.
+     *
+     * @param bean Bean whose property is to be extracted
+     * @param name Mapped property name of the property value to be extracted
+     * @param key Key of the property value to be extracted
+     * @return the mapped property value
+     *
+     * @throws IllegalAccessException if the caller does not have
+     *  access to the property accessor method
+     * @throws InvocationTargetException if the property accessor method
+     *  throws an exception
+     * @throws NoSuchMethodException if an accessor method for this
+     *  property cannot be found
+     */
+    public Object getMappedProperty(final Object bean,
+                                           final String name, final String key)
+            throws IllegalAccessException, InvocationTargetException,
+            NoSuchMethodException {
+        if (bean == null) {
+            throw new IllegalArgumentException("No bean specified");
+        }
+        if (name == null) {
+            throw new IllegalArgumentException("No name specified for bean class '" +
+                    bean.getClass() + "'");
+        }
+        if (key == null) {
+            throw new IllegalArgumentException("No key specified for property '" +
+                    name + "' on bean class " + bean.getClass() + "'");
+        }
+
+        // Handle DynaBean instances specially
+        if (bean instanceof DynaBean) {
+            final DynaProperty descriptor =
+                    ((DynaBean) bean).getDynaClass().getDynaProperty(name);
+            if (descriptor == null) {
+                throw new NoSuchMethodException("Unknown property '" +
+                        name + "'+ on bean class '" + bean.getClass() + "'");
+            }
+            return ((DynaBean) bean).get(name, key);
+        }
+
+        Object result = null;
+
+        // Retrieve the property descriptor for the specified property
+        final PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
+        if (descriptor == null) {
+            throw new NoSuchMethodException("Unknown property '" +
+                    name + "'+ on bean class '" + bean.getClass() + "'");
+        }
+
+        if (descriptor instanceof MappedPropertyDescriptor) {
+            // Call the keyed getter method if there is one
+            Method readMethod = ((MappedPropertyDescriptor) descriptor).
+                    getMappedReadMethod();
+            readMethod = MethodUtils.getAccessibleMethod(bean.getClass(), readMethod);
+            if (readMethod == null) {
+                throw new NoSuchMethodException("Property '" + name +
+                        "' has no mapped getter method on bean class '" +
+                        bean.getClass() + "'");
+            }
+            final Object[] keyArray = new Object[1];
+            keyArray[0] = key;
+            result = invokeMethod(readMethod, bean, keyArray);
+        } else {
+          /* means that the result has to be retrieved from a map */
+          final Method readMethod = getReadMethod(bean.getClass(), descriptor);
+          if (readMethod == null) {
+            throw new NoSuchMethodException("Property '" + name +
+                    "' has no mapped getter method on bean class '" +
+                    bean.getClass() + "'");
+          }
+        final Object invokeResult = invokeMethod(readMethod, bean, BeanUtils.EMPTY_OBJECT_ARRAY);
+        /* test and fetch from the map */
+        if (invokeResult instanceof java.util.Map) {
+          result = ((java.util.Map<?, ?>)invokeResult).get(key);
+        }
+        }
+        return result;
+    }
+
+    /**
+     * <p>Return the mapped property descriptors for this bean class.</p>
+     *
+     * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
+     *
+     * @param beanClass Bean class to be introspected
+     * @return the mapped property descriptors
+     */
+    Map<Class<?>, Map> getMappedPropertyDescriptors(final Class<?> beanClass) {
+        if (beanClass == null) {
+            return null;
+        }
+
+        // Look up any cached descriptors for this bean class
+        return mappedDescriptorsCache.get(beanClass);
+    }
+
+    /**
+     * <p>Return the mapped property descriptors for this bean.</p>
+     *
+     * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
+     *
+     * @param bean Bean to be introspected
+     * @return the mapped property descriptors
+     */
+    Map getMappedPropertyDescriptors(final Object bean) {
+        if (bean == null) {
+            return null;
+        }
+        return getMappedPropertyDescriptors(bean.getClass());
+    }
+
+    /**
+     * Gets the value of the (possibly nested) property of the specified
+     * name, for the specified bean, with no type conversions.
+     *
+     * @param bean Bean whose property is to be extracted
+     * @param name Possibly nested name of the property to be extracted
+     * @return the nested property value
+     *
+     * @throws IllegalAccessException if the caller does not have
+     *  access to the property accessor method
+     * @throws IllegalArgumentException if {@code bean} or
+     *  {@code name} is null
+     * @throws NestedNullException if a nested reference to a
+     *  property returns null
+     * @throws InvocationTargetException
+     * if the property accessor method throws an exception
+     * @throws NoSuchMethodException if an accessor method for this
+     *  property cannot be found
+     */
+    public Object getNestedProperty(Object bean, String name)
+            throws IllegalAccessException, InvocationTargetException,
+            NoSuchMethodException {
+        if (bean == null) {
+            throw new IllegalArgumentException("No bean specified");
+        }
+        if (name == null) {
+            throw new IllegalArgumentException("No name specified for bean class '" +
+                    bean.getClass() + "'");
+        }
+
+        // Resolve nested references
+        while (resolver.hasNested(name)) {
+            final String next = resolver.next(name);
+            Object nestedBean = null;
+            if (bean instanceof Map) {
+                nestedBean = getPropertyOfMapBean((Map<?, ?>) bean, next);
+            } else if (resolver.isMapped(next)) {
+                nestedBean = getMappedProperty(bean, next);
+            } else if (resolver.isIndexed(next)) {
+                nestedBean = getIndexedProperty(bean, next);
+            } else {
+                nestedBean = getSimpleProperty(bean, next);
+            }
+            if (nestedBean == null) {
+                throw new NestedNullException
+                        ("Null property value for '" + name +
+                        "' on bean class '" + bean.getClass() + "'");
+            }
+            bean = nestedBean;
+            name = resolver.remove(name);
+        }
+
+        if (bean instanceof Map) {
+            bean = getPropertyOfMapBean((Map<?, ?>) bean, name);
+        } else if (resolver.isMapped(name)) {
+            bean = getMappedProperty(bean, name);
+        } else if (resolver.isIndexed(name)) {
+            bean = getIndexedProperty(bean, name);
+        } else {
+            bean = getSimpleProperty(bean, name);
+        }
+        return bean;
+    }
+
+    /**
+     * This method is called by getNestedProperty and setNestedProperty to
+     * define what it means to get a property from an object which implements
+     * Map. See setPropertyOfMapBean for more information.
+     *
+     * @param bean Map bean
+     * @param propertyName The property name
+     * @return the property value
+     *
+     * @throws IllegalArgumentException when the propertyName is regarded as
+     * being invalid.
+     *
+     * @throws IllegalAccessException just in case subclasses override this
+     * method to try to access real getter methods and find permission is denied.
+     *
+     * @throws InvocationTargetException just in case subclasses override this
+     * method to try to access real getter methods, and find it throws an
+     * exception when invoked.
+     *
+     * @throws NoSuchMethodException just in case subclasses override this
+     * method to try to access real getter methods, and want to fail if
+     * no simple method is available.
+     * @since 1.8.0
+     */
+    protected Object getPropertyOfMapBean(final Map<?, ?> bean, String propertyName)
+        throws IllegalArgumentException, IllegalAccessException,
+        InvocationTargetException, NoSuchMethodException {
+
+        if (resolver.isMapped(propertyName)) {
+            final String name = resolver.getProperty(propertyName);
+            if (name == null || name.isEmpty()) {
+                propertyName = resolver.getKey(propertyName);
+            }
+        }
+
+        if (resolver.isIndexed(propertyName) ||
+            resolver.isMapped(propertyName)) {
+            throw new IllegalArgumentException(
+                    "Indexed or mapped properties are not supported on"
+                    + " objects of type Map: " + propertyName);
+        }
+
+        return bean.get(propertyName);
+    }
+
+    /**
+     * Gets the value of the specified property of the specified bean,
+     * no matter which property reference format is used, with no
+     * type conversions.
+     *
+     * @param bean Bean whose property is to be extracted
+     * @param name Possibly indexed and/or nested name of the property
+     *  to be extracted
+     * @return the property value
+     *
+     * @throws IllegalAccessException if the caller does not have
+     *  access to the property accessor method
+     * @throws IllegalArgumentException if {@code bean} or
+     *  {@code name} is null
+     * @throws InvocationTargetException if the property accessor method
+     *  throws an exception
+     * @throws NoSuchMethodException if an accessor method for this
+     *  property cannot be found
+     */
+    public Object getProperty(final Object bean, final String name)
+            throws IllegalAccessException, InvocationTargetException,
+            NoSuchMethodException {
+        return getNestedProperty(bean, name);
+    }
+
+    /**
+     * <p>Retrieve the property descriptor for the specified property of the
+     * specified bean, or return {@code null} if there is no such
+     * descriptor.  This method resolves indexed and nested property
+     * references in the same manner as other methods in this class, except
+     * that if the last (or only) name element is indexed, the descriptor
+     * for the last resolved property itself is returned.</p>
+     *
+     * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
+     *
+     * <p>Note that for Java 8 and above, this method no longer return
+     * IndexedPropertyDescriptor for {@link List}-typed properties, only for
+     * properties typed as native array. (BEANUTILS-492).
+     *
+     * @param bean Bean for which a property descriptor is requested
+     * @param name Possibly indexed and/or nested name of the property for
+     *  which a property descriptor is requested
+     * @return the property descriptor
+     *
+     * @throws IllegalAccessException if the caller does not have
+     *  access to the property accessor method
+     * @throws IllegalArgumentException if {@code bean} or
+     *  {@code name} is null
+     * @throws IllegalArgumentException if a nested reference to a
+     *  property returns null
+     * @throws InvocationTargetException if the property accessor method
+     *  throws an exception
+     * @throws NoSuchMethodException if an accessor method for this
+     *  property cannot be found
+     */
+    public PropertyDescriptor getPropertyDescriptor(Object bean,
+                                                           String name)
+            throws IllegalAccessException, InvocationTargetException,
+            NoSuchMethodException {
+        if (bean == null) {
+            throw new IllegalArgumentException("No bean specified");
+        }
+        if (name == null) {
+            throw new IllegalArgumentException("No name specified for bean class '" +
+                    bean.getClass() + "'");
+        }
+
+        // Resolve nested references
+        while (resolver.hasNested(name)) {
+            final String next = resolver.next(name);
+            final Object nestedBean = getProperty(bean, next);
+            if (nestedBean == null) {
+                throw new NestedNullException
+                        ("Null property value for '" + next +
+                        "' on bean class '" + bean.getClass() + "'");
+            }
+            bean = nestedBean;
+            name = resolver.remove(name);
+        }
+
+        // Remove any subscript from the final name value
+        name = resolver.getProperty(name);
+
+        // Look up and return this property from our cache
+        // creating and adding it to the cache if not found.
+        if (name == null) {
+            return null;
+        }
+
+        final BeanIntrospectionData data = getIntrospectionData(bean.getClass());
+        PropertyDescriptor result = data.getDescriptor(name);
+        if (result != null) {
+            return result;
+        }
+
+        Map mappedDescriptors = getMappedPropertyDescriptors(bean);
+        if (mappedDescriptors == null) {
+            mappedDescriptors = new ConcurrentHashMap<Class<?>, Map>();
+            mappedDescriptorsCache.put(bean.getClass(), mappedDescriptors);
+        }
+        result = (PropertyDescriptor) mappedDescriptors.get(name);
+        if (result == null) {
+            // not found, try to create it
+            try {
+                result = new MappedPropertyDescriptor(name, bean.getClass());
+            } catch (final IntrospectionException ie) {
+                /* Swallow IntrospectionException
+                 * TODO: Why?
+                 */
+            }
+            if (result != null) {
+                mappedDescriptors.put(name, result);
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * <p>Retrieve the property descriptors for the specified class,
+     * introspecting and caching them the first time a particular bean class
+     * is encountered.</p>
+     *
+     * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
+     *
+     * @param beanClass Bean class for which property descriptors are requested
+     * @return the property descriptors
+     *
+     * @throws IllegalArgumentException if {@code beanClass} is null
+     */
+    public PropertyDescriptor[]
+            getPropertyDescriptors(final Class<?> beanClass) {
+        return getIntrospectionData(beanClass).getDescriptors();
+    }
+
+    /**
+     * <p>Retrieve the property descriptors for the specified bean,
+     * introspecting and caching them the first time a particular bean class
+     * is encountered.</p>
+     *
+     * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
+     *
+     * @param bean Bean for which property descriptors are requested
+     * @return the property descriptors
+     *
+     * @throws IllegalArgumentException if {@code bean} is null
+     */
+    public PropertyDescriptor[] getPropertyDescriptors(final Object bean) {
+        if (bean == null) {
+            throw new IllegalArgumentException("No bean specified");
+        }
+        return getPropertyDescriptors(bean.getClass());
+    }
+
+    /**
+     * <p>Return the Java Class repesenting the property editor class that has
+     * been registered for this property (if any).  This method follows the
+     * same name resolution rules used by {@code getPropertyDescriptor()},
+     * so if the last element of a name reference is indexed, the property
+     * editor for the underlying property's class is returned.</p>
+     *
+     * <p>Note that {@code null} will be returned if there is no property,
+     * or if there is no registered property editor class.  Because this
+     * return value is ambiguous, you should determine the existence of the
+     * property itself by other means.</p>
+     *
+     * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
+     *
+     * @param bean Bean for which a property descriptor is requested
+     * @param name Possibly indexed and/or nested name of the property for
+     *  which a property descriptor is requested
+     * @return the property editor class
+     *
+     * @throws IllegalAccessException if the caller does not have
+     *  access to the property accessor method
+     * @throws IllegalArgumentException if {@code bean} or
+     *  {@code name} is null
+     * @throws IllegalArgumentException if a nested reference to a
+     *  property returns null
+     * @throws InvocationTargetException if the property accessor method
+     *  throws an exception
+     * @throws NoSuchMethodException if an accessor method for this
+     *  property cannot be found
+     */
+    public Class<?> getPropertyEditorClass(final Object bean, final String name)
+            throws IllegalAccessException, InvocationTargetException,
+            NoSuchMethodException {
+        if (bean == null) {
+            throw new IllegalArgumentException("No bean specified");
+        }
+        if (name == null) {
+            throw new IllegalArgumentException("No name specified for bean class '" +
+                    bean.getClass() + "'");
+        }
+
+        final PropertyDescriptor descriptor =
+                getPropertyDescriptor(bean, name);
+        if (descriptor != null) {
+            return descriptor.getPropertyEditorClass();
+        }
+        return null;
+    }
+
+    /**
+     * Gets the Java Class representing the property type of the specified
+     * property, or {@code null} if there is no such property for the
+     * specified bean.  This method follows the same name resolution rules
+     * used by {@code getPropertyDescriptor()}, so if the last element
+     * of a name reference is indexed, the type of the property itself will
+     * be returned.  If the last (or only) element has no property with the
+     * specified name, {@code null} is returned.
+     * <p>
+     * If the property is an indexed property (e.g. {@code String[]}),
+     * this method will return the type of the items within that array.
+     * Note that from Java 8 and newer, this method do not support
+     * such index types from items within an Collection, and will
+     * instead return the collection type (e.g. java.util.List) from the
+     * getter method.
+     *
+     * @param bean Bean for which a property descriptor is requested
+     * @param name Possibly indexed and/or nested name of the property for
+     *  which a property descriptor is requested
+     * @return The property type
+     *
+     * @throws IllegalAccessException if the caller does not have
+     *  access to the property accessor method
+     * @throws IllegalArgumentException if {@code bean} or
+     *  {@code name} is null
+     * @throws IllegalArgumentException if a nested reference to a
+     *  property returns null
+     * @throws InvocationTargetException if the property accessor method
+     *  throws an exception
+     * @throws NoSuchMethodException if an accessor method for this
+     *  property cannot be found
+     */
+    public Class<?> getPropertyType(Object bean, String name)
+            throws IllegalAccessException, InvocationTargetException,
+            NoSuchMethodException {
+        if (bean == null) {
+            throw new IllegalArgumentException("No bean specified");
+        }
+        if (name == null) {
+            throw new IllegalArgumentException("No name specified for bean class '" +
+                    bean.getClass() + "'");
+        }
+
+        // Resolve nested references
+        while (resolver.hasNested(name)) {
+            final String next = resolver.next(name);
+            final Object nestedBean = getProperty(bean, next);
+            if (nestedBean == null) {
+                throw new NestedNullException
+                        ("Null property value for '" + next +
+                        "' on bean class '" + bean.getClass() + "'");
+            }
+            bean = nestedBean;
+            name = resolver.remove(name);
+        }
+
+        // Remove any subscript from the final name value
+        name = resolver.getProperty(name);
+
+        // Special handling for DynaBeans
+        if (bean instanceof DynaBean) {
+            final DynaProperty descriptor =
+                    ((DynaBean) bean).getDynaClass().getDynaProperty(name);
+            if (descriptor == null) {
+                return null;
+            }
+            final Class<?> type = descriptor.getType();
+            if (type == null) {
+                return null;
+            }
+            if (type.isArray()) {
+                return type.getComponentType();
+            }
+            return type;
+        }
+
+        final PropertyDescriptor descriptor =
+                getPropertyDescriptor(bean, name);
+        if (descriptor == null) {
+            return null;
+        }
+        if (descriptor instanceof IndexedPropertyDescriptor) {
+            return ((IndexedPropertyDescriptor) descriptor).
+                    getIndexedPropertyType();
+        }
+        if (descriptor instanceof MappedPropertyDescriptor) {
+            return ((MappedPropertyDescriptor) descriptor).
+                    getMappedPropertyType();
+        }
+        return descriptor.getPropertyType();
+    }
+
+    /**
+     * <p>Return an accessible property getter method for this property,
+     * if there is one; otherwise return {@code null}.</p>
+     *
+     * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
+     *
+     * @param descriptor Property descriptor to return a getter for
+     * @return The read method
+     */
+    public Method getReadMethod(final PropertyDescriptor descriptor) {
+        return MethodUtils.getAccessibleMethod(descriptor.getReadMethod());
+    }
+
+    /**
+     * <p>Return an accessible property getter method for this property,
+     * if there is one; otherwise return {@code null}.</p>
+     *
+     * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
+     *
+     * @param clazz The class of the read method will be invoked on
+     * @param descriptor Property descriptor to return a getter for
+     * @return The read method
+     */
+    Method getReadMethod(final Class<?> clazz, final PropertyDescriptor descriptor) {
+        return MethodUtils.getAccessibleMethod(clazz, descriptor.getReadMethod());
+    }
+
+    /**
+     * Gets the value of the specified simple property of the specified
+     * bean, with no type conversions.
+     *
+     * @param bean Bean whose property is to be extracted
+     * @param name Name of the property to be extracted
+     * @return The property value
+     *
+     * @throws IllegalAccessException if the caller does not have
+     *  access to the property accessor method
+     * @throws IllegalArgumentException if {@code bean} or
+     *  {@code name} is null
+     * @throws IllegalArgumentException if the property name
+     *  is nested or indexed
+     * @throws InvocationTargetException if the property accessor method
+     *  throws an exception
+     * @throws NoSuchMethodException if an accessor method for this
+     *  property cannot be found
+     */
+    public Object getSimpleProperty(final Object bean, final String name)
+            throws IllegalAccessException, InvocationTargetException,
+            NoSuchMethodException {
+        if (bean == null) {
+            throw new IllegalArgumentException("No bean specified");
+        }
+        if (name == null) {
+            throw new IllegalArgumentException("No name specified for bean class '" +
+                    bean.getClass() + "'");
+        }
+
+        // Validate the syntax of the property name
+        if (resolver.hasNested(name)) {
+            throw new IllegalArgumentException
+                    ("Nested property names are not allowed: Property '" +
+                    name + "' on bean class '" + bean.getClass() + "'");
+        }
+        if (resolver.isIndexed(name)) {
+            throw new IllegalArgumentException
+                    ("Indexed property names are not allowed: Property '" +
+                    name + "' on bean class '" + bean.getClass() + "'");
+        }
+        if (resolver.isMapped(name)) {
+            throw new IllegalArgumentException
+                    ("Mapped property names are not allowed: Property '" +
+                    name + "' on bean class '" + bean.getClass() + "'");
+        }
+
+        // Handle DynaBean instances specially
+        if (bean instanceof DynaBean) {
+            final DynaProperty descriptor =
+                    ((DynaBean) bean).getDynaClass().getDynaProperty(name);
+            if (descriptor == null) {
+                throw new NoSuchMethodException("Unknown property '" +
+                        name + "' on dynaclass '" +
+                        ((DynaBean) bean).getDynaClass() + "'" );
+            }
+            return ((DynaBean) bean).get(name);
+        }
+
+        // Retrieve the property getter method for the specified property
+        final PropertyDescriptor descriptor =
+                getPropertyDescriptor(bean, name);
+        if (descriptor == null) {
+            throw new NoSuchMethodException("Unknown property '" +
+                    name + "' on class '" + bean.getClass() + "'" );
+        }
+        final Method readMethod = getReadMethod(bean.getClass(), descriptor);
+        if (readMethod == null) {
+            throw new NoSuchMethodException("Property '" + name +
+                    "' has no getter method in class '" + bean.getClass() + "'");
+        }
+
+        // Call the property getter and return the value
+        return invokeMethod(readMethod, bean, BeanUtils.EMPTY_OBJECT_ARRAY);
+    }
+
+    /**
+     * <p>Return an accessible property setter method for this property,
+     * if there is one; otherwise return {@code null}.</p>
+     *
+     * <p><em>Note:</em> This method does not work correctly with custom bean
+     * introspection under certain circumstances. It may return {@code null}
+     * even if a write method is defined for the property in question. Use
+     * {@link #getWriteMethod(Class, PropertyDescriptor)} to be sure that the
+     * correct result is returned.</p>
+     * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
+     *
+     * @param descriptor Property descriptor to return a setter for
+     * @return The write method
+     */
+    public Method getWriteMethod(final PropertyDescriptor descriptor) {
+        return MethodUtils.getAccessibleMethod(descriptor.getWriteMethod());
+    }
+
+    /**
+     * <p>Return an accessible property setter method for this property,
+     * if there is one; otherwise return {@code null}.</p>
+     *
+     * <p><strong>FIXME</strong> - Does not work with DynaBeans.</p>
+     *
+     * @param clazz The class of the read method will be invoked on
+     * @param descriptor Property descriptor to return a setter for
+     * @return The write method
+     * @since 1.9.1
+     */
+    public Method getWriteMethod(final Class<?> clazz, final PropertyDescriptor descriptor) {
+        final BeanIntrospectionData data = getIntrospectionData(clazz);
+        return MethodUtils.getAccessibleMethod(clazz,
+                data.getWriteMethod(clazz, descriptor));
+    }
+
+    /**
+     * <p>Return {@code true} if the specified property name identifies
+     * a readable property on the specified bean; otherwise, return
+     * {@code false}.
+     *
+     * @param bean Bean to be examined (may be a {@link DynaBean}
+     * @param name Property name to be evaluated
+     * @return {@code true} if the property is readable,
+     * otherwise {@code false}
+     *
+     * @throws IllegalArgumentException if {@code bean}
+     *  or {@code name</code> is <code>null}
+     *
+     * @since 1.6
+     */
+    public boolean isReadable(Object bean, String name) {
+        // Validate method parameters
+        if (bean == null) {
+            throw new IllegalArgumentException("No bean specified");
+        }
+        if (name == null) {
+            throw new IllegalArgumentException("No name specified for bean class '" +
+                    bean.getClass() + "'");
+        }
+
+        // Resolve nested references
+        while (resolver.hasNested(name)) {
+            final String next = resolver.next(name);
+            Object nestedBean = null;
+            try {
+                nestedBean = getProperty(bean, next);
+            } catch (final IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
+                return false;
+            }
+            if (nestedBean == null) {
+                throw new NestedNullException
+                        ("Null property value for '" + next +
+                        "' on bean class '" + bean.getClass() + "'");
+            }
+            bean = nestedBean;
+            name = resolver.remove(name);
+        }
+
+        // Remove any subscript from the final name value
+        name = resolver.getProperty(name);
+
+        // Treat WrapDynaBean as special case - may be a write-only property
+        // (see Jira issue# BEANUTILS-61)
+        if (bean instanceof WrapDynaBean) {
+            bean = ((WrapDynaBean)bean).getInstance();
+        }
+
+        // Return the requested result
+        if (bean instanceof DynaBean) {
+            // All DynaBean properties are readable
+            return ((DynaBean) bean).getDynaClass().getDynaProperty(name) != null;
+        }
+        try {
+            final PropertyDescriptor desc =
+                getPropertyDescriptor(bean, name);
+            if (desc != null) {
+                Method readMethod = getReadMethod(bean.getClass(), desc);
+                if (readMethod == null) {
+                    if (desc instanceof IndexedPropertyDescriptor) {
+                        readMethod = ((IndexedPropertyDescriptor) desc).getIndexedReadMethod();
+                    } else if (desc instanceof MappedPropertyDescriptor) {
+                        readMethod = ((MappedPropertyDescriptor) desc).getMappedReadMethod();
+                    }
+                    readMethod = MethodUtils.getAccessibleMethod(bean.getClass(), readMethod);
+                }
+                return readMethod != null;
+            }
+            return false;
+        } catch (final IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
+            return false;
+        }
+    }
+
+    /**
+     * <p>Return {@code true} if the specified property name identifies
+     * a writable property on the specified bean; otherwise, return
+     * {@code false}.
+     *
+     * @param bean Bean to be examined (may be a {@link DynaBean}
+     * @param name Property name to be evaluated
+     * @return {@code true} if the property is writable,
+     * otherwise {@code false}
+     *
+     * @throws IllegalArgumentException if {@code bean}
+     *  or {@code name</code> is <code>null}
+     *
+     * @since 1.6
+     */
+    public boolean isWriteable(Object bean, String name) {
+        // Validate method parameters
+        if (bean == null) {
+            throw new IllegalArgumentException("No bean specified");
+        }
+        if (name == null) {
+            throw new IllegalArgumentException("No name specified for bean class '" +
+                    bean.getClass() + "'");
+        }
+
+        // Resolve nested references
+        while (resolver.hasNested(name)) {
+            final String next = resolver.next(name);
+            Object nestedBean = null;
+            try {
+                nestedBean = getProperty(bean, next);
+            } catch (final IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
+                return false;
+            }
+            if (nestedBean == null) {
+                throw new NestedNullException
+                        ("Null property value for '" + next +
+                        "' on bean class '" + bean.getClass() + "'");
+            }
+            bean = nestedBean;
+            name = resolver.remove(name);
+        }
+
+        // Remove any subscript from the final name value
+        name = resolver.getProperty(name);
+
+        // Treat WrapDynaBean as special case - may be a read-only property
+        // (see Jira issue# BEANUTILS-61)
+        if (bean instanceof WrapDynaBean) {
+            bean = ((WrapDynaBean)bean).getInstance();
+        }
+
+        // Return the requested result
+        if (bean instanceof DynaBean) {
+            // All DynaBean properties are writable
+            return ((DynaBean) bean).getDynaClass().getDynaProperty(name) != null;
+        }
+        try {
+            final PropertyDescriptor desc =
+                getPropertyDescriptor(bean, name);
+            if (desc != null) {
+                Method writeMethod = getWriteMethod(bean.getClass(), desc);
+                if (writeMethod == null) {
+                    if (desc instanceof IndexedPropertyDescriptor) {
+                        writeMethod = ((IndexedPropertyDescriptor) desc).getIndexedWriteMethod();
+                    } else if (desc instanceof MappedPropertyDescriptor) {
+                        writeMethod = ((MappedPropertyDescriptor) desc).getMappedWriteMethod();
+                    }
+                    writeMethod = MethodUtils.getAccessibleMethod(bean.getClass(), writeMethod);
+                }
+                return writeMethod != null;
+            }
+            return false;
+        } catch (final IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Sets the value of the specified indexed property of the specified
+     * bean, with no type conversions.  The zero-relative index of the
+     * required value must be included (in square brackets) as a suffix to
+     * the property name, or {@code IllegalArgumentException} will be
+     * thrown.  In addition to supporting the JavaBeans specification, this
+     * method has been extended to support {@code List} objects as well.
+     *
+     * @param bean Bean whose property is to be modified
+     * @param name {@code propertyname[index]} of the property value
+     *  to be modified
+     * @param value Value to which the specified property element
+     *  should be set
+     *
+     * @throws IndexOutOfBoundsException if the specified index
+     *  is outside the valid range for the underlying property
+     * @throws IllegalAccessException if the caller does not have
+     *  access to the property accessor method
+     * @throws IllegalArgumentException if {@code bean} or
+     *  {@code name} is null
+     * @throws InvocationTargetException if the property accessor method
+     *  throws an exception
+     * @throws NoSuchMethodException if an accessor method for this
+     *  property cannot be found
+     */
+    public void setIndexedProperty(final Object bean, String name,
+                                          final Object value)
+            throws IllegalAccessException, InvocationTargetException,
+            NoSuchMethodException {
+        if (bean == null) {
+            throw new IllegalArgumentException("No bean specified");
+        }
+        if (name == null) {
+            throw new IllegalArgumentException("No name specified for bean class '" +
+                    bean.getClass() + "'");
+        }
+
+        // Identify the index of the requested individual property
+        int index = -1;
+        try {
+            index = resolver.getIndex(name);
+        } catch (final IllegalArgumentException e) {
+            throw new IllegalArgumentException("Invalid indexed property '" +
+                    name + "' on bean class '" + bean.getClass() + "'");
+        }
+        if (index < 0) {
+            throw new IllegalArgumentException("Invalid indexed property '" +
+                    name + "' on bean class '" + bean.getClass() + "'");
+        }
+
+        // Isolate the name
+        name = resolver.getProperty(name);
+
+        // Set the specified indexed property value
+        setIndexedProperty(bean, name, index, value);
+    }
+
+    /**
+     * Sets the value of the specified indexed property of the specified
+     * bean, with no type conversions.  In addition to supporting the JavaBeans
+     * specification, this method has been extended to support
+     * {@code List} objects as well.
+     *
+     * @param bean Bean whose property is to be set
+     * @param name Simple property name of the property value to be set
+     * @param index Index of the property value to be set
+     * @param value Value to which the indexed property element is to be set
+     *
+     * @throws IndexOutOfBoundsException if the specified index
+     *  is outside the valid range for the underlying property
+     * @throws IllegalAccessException if the caller does not have
+     *  access to the property accessor method
+     * @throws IllegalArgumentException if {@code bean} or
+     *  {@code name} is null
+     * @throws InvocationTargetException if the property accessor method
+     *  throws an exception
+     * @throws NoSuchMethodException if an accessor method for this
+     *  property cannot be found
+     */
+    public void setIndexedProperty(final Object bean, final String name,
+                                          final int index, final Object value)
+            throws IllegalAccessException, InvocationTargetException,
+            NoSuchMethodException {
+        if (bean == null) {
+            throw new IllegalArgumentException("No bean specified");
+        }
+        if (name == null || name.isEmpty()) {
+            if (bean.getClass().isArray()) {
+                Array.set(bean, index, value);
+                return;
+            }
+            if (bean instanceof List) {
+                final List<Object> list = toObjectList(bean);
+                list.set(index, value);
+                return;
+            }
+        }
+        if (name == null) {
+            throw new IllegalArgumentException("No name specified for bean class '" +
+                    bean.getClass() + "'");
+        }
+
+        // Handle DynaBean instances specially
+        if (bean instanceof DynaBean) {
+            final DynaProperty descriptor =
+                    ((DynaBean) bean).getDynaClass().getDynaProperty(name);
+            if (descriptor == null) {
+                throw new NoSuchMethodException("Unknown property '" +
+                        name + "' on bean class '" + bean.getClass() + "'");
+            }
+            ((DynaBean) bean).set(name, index, value);
+            return;
+        }
+
+        // Retrieve the property descriptor for the specified property
+        final PropertyDescriptor descriptor =
+                getPropertyDescriptor(bean, name);
+        if (descriptor == null) {
+            throw new NoSuchMethodException("Unknown property '" +
+                    name + "' on bean class '" + bean.getClass() + "'");
+        }
+
+        // Call the indexed setter method if there is one
+        if (descriptor instanceof IndexedPropertyDescriptor) {
+            Method writeMethod = ((IndexedPropertyDescriptor) descriptor).
+                    getIndexedWriteMethod();
+            writeMethod = MethodUtils.getAccessibleMethod(bean.getClass(), writeMethod);
+            if (writeMethod != null) {
+                final Object[] subscript = new Object[2];
+                subscript[0] = Integer.valueOf(index);
+                subscript[1] = value;
+                try {
+                    if (log.isTraceEnabled()) {
+                        final String valueClassName =
+                            value == null ? "<null>"
+                                          : value.getClass().getName();
+                        log.trace("setSimpleProperty: Invoking method "
+                                  + writeMethod +" with index=" + index
+                                  + ", value=" + value
+                                  + " (class " + valueClassName+ ")");
+                    }
+                    invokeMethod(writeMethod, bean, subscript);
+                } catch (final InvocationTargetException e) {
+                    if (e.getTargetException() instanceof
+                            IndexOutOfBoundsException) {
+                        throw (IndexOutOfBoundsException)
+                                e.getTargetException();
+                    }
+                    throw e;
+                }
+                return;
+            }
+        }
+
+        // Otherwise, the underlying property must be an array or a list
+        final Method readMethod = getReadMethod(bean.getClass(), descriptor);
+        if (readMethod == null) {
+            throw new NoSuchMethodException("Property '" + name +
+                    "' has no getter method on bean class '" + bean.getClass() + "'");
+        }
+
+        // Call the property getter to get the array or list
+        final Object array = invokeMethod(readMethod, bean, BeanUtils.EMPTY_OBJECT_ARRAY);
+        if (!array.getClass().isArray()) {
+            if (!(array instanceof List)) {
+                throw new IllegalArgumentException("Property '" + name +
+                        "' is not indexed on bean class '" + bean.getClass() + "'");
+            }
+            // Modify the specified value in the List
+            final List<Object> list = toObjectList(array);
+            list.set(index, value);
+        } else {
+            // Modify the specified value in the array
+            Array.set(array, index, value);
+        }
+    }
+
+    /**
+     * Sets the value of the specified mapped property of the
+     * specified bean, with no type conversions.  The key of the
+     * value to set must be included (in brackets) as a suffix to
+     * the property name, or {@code IllegalArgumentException} will be
+     * thrown.
+     *
+     * @param bean Bean whose property is to be set
+     * @param name {@code propertyname(key)} of the property value
+     *  to be set
+     * @param value The property value to be set
+     *
+     * @throws IllegalAccessException if the caller does not have
+     *  access to the property accessor method
+     * @throws InvocationTargetException if the property accessor method
+     *  throws an exception
+     * @throws NoSuchMethodException if an accessor method for this
+     *  property cannot be found
+     */
+    public void setMappedProperty(final Object bean, String name,
+                                         final Object value)
+            throws IllegalAccessException, InvocationTargetException,
+            NoSuchMethodException {
+        if (bean == null) {
+            throw new IllegalArgumentException("No bean specified");
+        }
+        if (name == null) {
+            throw new IllegalArgumentException("No name specified for bean class '" +
+                    bean.getClass() + "'");
+        }
+
+        // Identify the key of the requested individual property
+        String key  = null;
+        try {
+            key = resolver.getKey(name);
+        } catch (final IllegalArgumentException e) {
+            throw new IllegalArgumentException
+                    ("Invalid mapped property '" + name +
+                    "' on bean class '" + bean.getClass() + "'");
+        }
+        if (key == null) {
+            throw new IllegalArgumentException
+                    ("Invalid mapped property '" + name +
+                    "' on bean class '" + bean.getClass() + "'");
+        }
+
+        // Isolate the name
+        name = resolver.getProperty(name);
+
+        // Request the specified indexed property value
+        setMappedProperty(bean, name, key, value);
+    }
+
+    /**
+     * Sets the value of the specified mapped property of the specified
+     * bean, with no type conversions.
+     *
+     * @param bean Bean whose property is to be set
+     * @param name Mapped property name of the property value to be set
+     * @param key Key of the property value to be set
+     * @param value The property value to be set
+     *
+     * @throws IllegalAccessException if the caller does not have
+     *  access to the property accessor method
+     * @throws InvocationTargetException if the property accessor method
+     *  throws an exception
+     * @throws NoSuchMethodException if an accessor method for this
+     *  property cannot be found
+     */
+    public void setMappedProperty(final Object bean, final String name,
+                                         final String key, final Object value)
+            throws IllegalAccessException, InvocationTargetException,
+            NoSuchMethodException {
+        if (bean == null) {
+            throw new IllegalArgumentException("No bean specified");
+        }
+        if (name == null) {
+            throw new IllegalArgumentException("No name specified for bean class '" +
+                    bean.getClass() + "'");
+        }
+        if (key == null) {
+            throw new IllegalArgumentException("No key specified for property '" +
+                    name + "' on bean class '" + bean.getClass() + "'");
+        }
+
+        // Handle DynaBean instances specially
+        if (bean instanceof DynaBean) {
+            final DynaProperty descriptor =
+                    ((DynaBean) bean).getDynaClass().getDynaProperty(name);
+            if (descriptor == null) {
+                throw new NoSuchMethodException("Unknown property '" +
+                        name + "' on bean class '" + bean.getClass() + "'");
+            }
+            ((DynaBean) bean).set(name, key, value);
+            return;
+        }
+
+        // Retrieve the property descriptor for the specified property
+        final PropertyDescriptor descriptor =
+                getPropertyDescriptor(bean, name);
+        if (descriptor == null) {
+            throw new NoSuchMethodException("Unknown property '" +
+                    name + "' on bean class '" + bean.getClass() + "'");
+        }
+
+        if (descriptor instanceof MappedPropertyDescriptor) {
+            // Call the keyed setter method if there is one
+            Method mappedWriteMethod =
+                    ((MappedPropertyDescriptor) descriptor).
+                    getMappedWriteMethod();
+            mappedWriteMethod = MethodUtils.getAccessibleMethod(bean.getClass(), mappedWriteMethod);
+            if (mappedWriteMethod == null) {
+                throw new NoSuchMethodException
+                    ("Property '" + name + "' has no mapped setter method" +
+                     "on bean class '" + bean.getClass() + "'");
+            }
+            final Object[] params = new Object[2];
+            params[0] = key;
+            params[1] = value;
+            if (log.isTraceEnabled()) {
+                final String valueClassName =
+                    value == null ? "<null>" : value.getClass().getName();
+                log.trace("setSimpleProperty: Invoking method "
+                          + mappedWriteMethod + " with key=" + key
+                          + ", value=" + value
+                          + " (class " + valueClassName +")");
+            }
+            invokeMethod(mappedWriteMethod, bean, params);
+        } else {
+          /* means that the result has to be retrieved from a map */
+          final Method readMethod = getReadMethod(bean.getClass(), descriptor);
+          if (readMethod == null) {
+            throw new NoSuchMethodException("Property '" + name +
+                    "' has no mapped getter method on bean class '" +
+                    bean.getClass() + "'");
+          }
+        final Object invokeResult = invokeMethod(readMethod, bean, BeanUtils.EMPTY_OBJECT_ARRAY);
+        /* test and fetch from the map */
+        if (invokeResult instanceof java.util.Map) {
+          final java.util.Map<String, Object> map = toPropertyMap(invokeResult);
+          map.put(key, value);
+        }
+        }
+    }
+
+    /**
+     * Sets the value of the (possibly nested) property of the specified
+     * name, for the specified bean, with no type conversions.
+     * <p>
+     * Example values for parameter "name" are:
+     * <ul>
+     * <li> "a" -- sets the value of property a of the specified bean </li>
+     * <li> "a.b" -- gets the value of property a of the specified bean,
+     * then on that object sets the value of property b.</li>
+     * <li> "a(key)" -- sets a value of mapped-property a on the specified
+     * bean. This effectively means bean.setA("key").</li>
+     * <li> "a[3]" -- sets a value of indexed-property a on the specified
+     * bean. This effectively means bean.setA(3).</li>
+     * </ul>
+     *
+     * @param bean Bean whose property is to be modified
+     * @param name Possibly nested name of the property to be modified
+     * @param value Value to which the property is to be set
+     *
+     * @throws IllegalAccessException if the caller does not have
+     *  access to the property accessor method
+     * @throws IllegalArgumentException if {@code bean} or
+     *  {@code name} is null
+     * @throws IllegalArgumentException if a nested reference to a
+     *  property returns null
+     * @throws InvocationTargetException if the property accessor method
+     *  throws an exception
+     * @throws NoSuchMethodException if an accessor method for this
+     *  property cannot be found
+     */
+    public void setNestedProperty(Object bean,
+                                         String name, final Object value)
+            throws IllegalAccessException, InvocationTargetException,
+            NoSuchMethodException {
+        if (bean == null) {
+            throw new IllegalArgumentException("No bean specified");
+        }
+        if (name == null) {
+            throw new IllegalArgumentException("No name specified for bean class '" +
+                    bean.getClass() + "'");
+        }
+
+        // Resolve nested references
+        while (resolver.hasNested(name)) {
+            final String next = resolver.next(name);
+            Object nestedBean = null;
+            if (bean instanceof Map) {
+                nestedBean = getPropertyOfMapBean((Map<?, ?>)bean, next);
+            } else if (resolver.isMapped(next)) {
+                nestedBean = getMappedProperty(bean, next);
+            } else if (resolver.isIndexed(next)) {
+                nestedBean = getIndexedProperty(bean, next);
+            } else {
+                nestedBean = getSimpleProperty(bean, next);
+            }
+            if (nestedBean == null) {
+                throw new NestedNullException
+                        ("Null property value for '" + name +
+                         "' on bean class '" + bean.getClass() + "'");
+            }
+            bean = nestedBean;
+            name = resolver.remove(name);
+        }
+
+        if (bean instanceof Map) {
+            setPropertyOfMapBean(toPropertyMap(bean), name, value);
+        } else if (resolver.isMapped(name)) {
+            setMappedProperty(bean, name, value);
+        } else if (resolver.isIndexed(name)) {
+            setIndexedProperty(bean, name, value);
+        } else {
+            setSimpleProperty(bean, name, value);
+        }
+    }
+
+    /**
+     * This method is called by method setNestedProperty when the current bean
+     * is found to be a Map object, and defines how to deal with setting
+     * a property on a Map.
+     * <p>
+     * The standard implementation here is to:
+     * <ul>
+     * <li>call bean.set(propertyName) for all propertyName values.</li>
+     * <li>throw an IllegalArgumentException if the property specifier
+     * contains MAPPED_DELIM or INDEXED_DELIM, as Map entries are essentially
+     * simple properties; mapping and indexing operations do not make sense
+     * when accessing a map (even thought the returned object may be a Map
+     * or an Array).</li>
+     * </ul>
+     * <p>
+     * The default behavior of beanutils 1.7.1 or later is for assigning to
+     * "a.b" to mean a.put(b, obj) always. However the behavior of beanutils
+     * version 1.6.0, 1.6.1, 1.7.0 was for "a.b" to mean a.setB(obj) if such
+     * a method existed, and a.put(b, obj) otherwise. In version 1.5 it meant
+     * a.put(b, obj) always (ie the same as the behavior in the current version).
+     * In versions prior to 1.5 it meant a.setB(obj) always. [yes, this is
+     * all <i>very</i> unfortunate]
+     * <p>
+     * Users who would like to customize the meaning of "a.b" in method
+     * setNestedProperty when a is a Map can create a custom subclass of
+     * this class and override this method to implement the behavior of
+     * their choice, such as restoring the pre-1.4 behavior of this class
+     * if they wish. When overriding this method, do not forget to deal
+     * with MAPPED_DELIM and INDEXED_DELIM characters in the propertyName.
+     * <p>
+     * Note, however, that the recommended solution for objects that
+     * implement Map but want their simple properties to come first is
+     * for <i>those</i> objects to override their get/put methods to implement
+     * that behavior, and <i>not</i> to solve the problem by modifying the
+     * default behavior of the PropertyUtilsBean class by overriding this
+     * method.
+     *
+     * @param bean Map bean
+     * @param propertyName The property name
+     * @param value the property value
+     *
+     * @throws IllegalArgumentException when the propertyName is regarded as
+     * being invalid.
+     *
+     * @throws IllegalAccessException just in case subclasses override this
+     * method to try to access real setter methods and find permission is denied.
+     *
+     * @throws InvocationTargetException just in case subclasses override this
+     * method to try to access real setter methods, and find it throws an
+     * exception when invoked.
+     *
+     * @throws NoSuchMethodException just in case subclasses override this
+     * method to try to access real setter methods, and want to fail if
+     * no simple method is available.
+     * @since 1.8.0
+     */
+    protected void setPropertyOfMapBean(final Map<String, Object> bean, String propertyName, final Object value)
+        throws IllegalArgumentException, IllegalAccessException,
+        InvocationTargetException, NoSuchMethodException {
+        if (resolver.isMapped(propertyName)) {
+            final String name = resolver.getProperty(propertyName);
+            if (name == null || name.isEmpty()) {
+                propertyName = resolver.getKey(propertyName);
+            }
+        }
+
+        if (resolver.isIndexed(propertyName) ||
+            resolver.isMapped(propertyName)) {
+            throw new IllegalArgumentException(
+                    "Indexed or mapped properties are not supported on"
+                    + " objects of type Map: " + propertyName);
+        }
+
+        bean.put(propertyName, value);
+    }
+
+    /**
+     * Sets the value of the specified property of the specified bean,
+     * no matter which property reference format is used, with no
+     * type conversions.
+     *
+     * @param bean Bean whose property is to be modified
+     * @param name Possibly indexed and/or nested name of the property
+     *  to be modified
+     * @param value Value to which this property is to be set
+     *
+     * @throws IllegalAccessException if the caller does not have
+     *  access to the property accessor method
+     * @throws IllegalArgumentException if {@code bean} or
+     *  {@code name} is null
+     * @throws InvocationTargetException if the property accessor method
+     *  throws an exception
+     * @throws NoSuchMethodException if an accessor method for this
+     *  property cannot be found
+     */
+    public void setProperty(final Object bean, final String name, final Object value)
+            throws IllegalAccessException, InvocationTargetException,
+            NoSuchMethodException {
+        setNestedProperty(bean, name, value);
+    }
+
+    /**
+     * Sets the value of the specified simple property of the specified bean,
+     * with no type conversions.
+     *
+     * @param bean Bean whose property is to be modified
+     * @param name Name of the property to be modified
+     * @param value Value to which the property should be set
+     *
+     * @throws IllegalAccessException if the caller does not have
+     *  access to the property accessor method
+     * @throws IllegalArgumentException if {@code bean} or
+     *  {@code name} is null
+     * @throws IllegalArgumentException if the property name is
+     *  nested or indexed
+     * @throws InvocationTargetException if the property accessor method
+     *  throws an exception
+     * @throws NoSuchMethodException if an accessor method for this
+     *  property cannot be found
+     */
+    public void setSimpleProperty(final Object bean,
+                                         final String name, final Object value)
+            throws IllegalAccessException, InvocationTargetException,
+        NoSuchMethodException {
+        if (bean == null) {
+            throw new IllegalArgumentException("No bean specified");
+        }
+        final Class<?> beanClass = bean.getClass();
+        if (name == null) {
+            throw new IllegalArgumentException("No name specified for bean class '" + beanClass + "'");
+        }
+
+        // Validate the syntax of the property name
+        if (resolver.hasNested(name)) {
+            throw new IllegalArgumentException("Nested property names are not allowed: Property '" + name + "' on bean class '" + beanClass + "'");
+        }
+        if (resolver.isIndexed(name)) {
+            throw new IllegalArgumentException("Indexed property names are not allowed: Property '" + name + "' on bean class '" + beanClass + "'");
+        }
+        if (resolver.isMapped(name)) {
+            throw new IllegalArgumentException("Mapped property names are not allowed: Property '" + name + "' on bean class '" + beanClass + "'");
+        }
+
+        // Handle DynaBean instances specially
+        if (bean instanceof DynaBean) {
+            final DynaProperty descriptor = ((DynaBean) bean).getDynaClass().getDynaProperty(name);
+            if (descriptor == null) {
+                throw new NoSuchMethodException("Unknown property '" + name + "' on dynaclass '" + ((DynaBean) bean).getDynaClass() + "'");
+            }
+            ((DynaBean) bean).set(name, value);
+            return;
+        }
+
+        // Retrieve the property setter method for the specified property
+        final PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
+        if (descriptor == null) {
+            throw new NoSuchMethodException("Unknown property '" + name + "' on class '" + beanClass + "'");
+        }
+        final Method writeMethod = getWriteMethod(beanClass, descriptor);
+        if (writeMethod == null) {
+            throw new NoSuchMethodException("Property '" + name + "' has no setter method in class '" + beanClass + "'");
+        }
+
+        // Call the property setter method
+        final Object[] values = new Object[1];
+        values[0] = value;
+        if (log.isTraceEnabled()) {
+            final String valueClassName = value == null ? "<null>" : value.getClass().getName();
+            log.trace("setSimpleProperty: Invoking method " + writeMethod + " with value " + value + " (class " + valueClassName + ")");
+        }
+        invokeMethod(writeMethod, bean, values);
+    }
+
+    /** This just catches and wraps IllegalArgumentException. */
+    private Object invokeMethod(
+                        final Method method,
+                        final Object bean,
+                        final Object[] values)
+                            throws
+                                IllegalAccessException,
+                                InvocationTargetException {
+        if (bean == null) {
+            throw new IllegalArgumentException("No bean specified " +
+                "- this should have been checked before reaching this method");
+        }
+
+        try {
+
+            return method.invoke(bean, values);
+
+        } catch (final NullPointerException | IllegalArgumentException cause) {
+            // JDK 1.3 and JDK 1.4 throw NullPointerException if an argument is
+            // null for a primitive value (JDK 1.5+ throw IllegalArgumentException)
+            final StringBuilder valueString = new StringBuilder();
+            if (values != null) {
+                for (int i = 0; i < values.length; i++) {
+                    if (i>0) {
+                        valueString.append(", ");
+                    }
+                    if (values[i] == null) {
+                        valueString.append("<null>");
+                    } else {
+                        valueString.append(values[i].getClass().getName());
+                    }
+                }
+            }
+            final StringBuilder expectedString = new StringBuilder();
+            final Class<?>[] parTypes = method.getParameterTypes();
+            if (parTypes != null) {
+                for (int i = 0; i < parTypes.length; i++) {
+                    if (i > 0) {
+                        expectedString.append(", ");
+                    }
+                    expectedString.append(parTypes[i].getName());
+                }
+            }
+            final IllegalArgumentException e = new IllegalArgumentException(
+                "Cannot invoke " + method.getDeclaringClass().getName() + "."
+                + method.getName() + " on bean class '" + bean.getClass() +
+                "' - " + cause.getMessage()
+                // as per https://issues.apache.org/jira/browse/BEANUTILS-224
+                + " - had objects of type \"" + valueString
+                + "\" but expected signature \""
+                +   expectedString + "\""
+                );
+            if (!BeanUtils.initCause(e, cause)) {
+                log.error("Method invocation failed", cause);
+            }
+            throw e;
+        }
+    }
+
+    /**
+     * Obtains the {@code BeanIntrospectionData} object describing the specified bean
+     * class. This object is looked up in the internal cache. If necessary, introspection
+     * is performed now on the affected bean class, and the results object is created.
+     *
+     * @param beanClass the bean class in question
+     * @return the {@code BeanIntrospectionData} object for this class
+     * @throws IllegalArgumentException if the bean class is <b>null</b>
+     */
+    private BeanIntrospectionData getIntrospectionData(final Class<?> beanClass) {
+        if (beanClass == null) {
+            throw new IllegalArgumentException("No bean class specified");
+        }
+
+        // Look up any cached information for this bean class
+        BeanIntrospectionData data = descriptorsCache.get(beanClass);
+        if (data == null) {
+            data = fetchIntrospectionData(beanClass);
+            descriptorsCache.put(beanClass, data);
+        }
+
+        return data;
+    }
+
+    /**
+     * Performs introspection on the specified class. This method invokes all {@code BeanIntrospector} objects that were
+     * added to this instance.
+     *
+     * @param beanClass the class to be inspected
+     * @return a data object with the results of introspection
+     */
+    private BeanIntrospectionData fetchIntrospectionData(final Class<?> beanClass) {
+        final DefaultIntrospectionContext ictx = new DefaultIntrospectionContext(beanClass);
+
+        for (final BeanIntrospector bi : introspectors) {
+            try {
+                bi.introspect(ictx);
+            } catch (final IntrospectionException iex) {
+                log.error("Exception during introspection", iex);
+            }
+        }
+
+        return new BeanIntrospectionData(ictx.getPropertyDescriptors());
+    }
+
+    /**
+     * Converts an object to a list of objects. This method is used when dealing
+     * with indexed properties. It assumes that indexed properties are stored as
+     * lists of objects.
+     *
+     * @param obj the object to be converted
+     * @return the resulting list of objects
+     */
+    private static List<Object> toObjectList(final Object obj) {
+        @SuppressWarnings("unchecked")
+        final
+        // indexed properties are stored in lists of objects
+        List<Object> list = (List<Object>) obj;
+        return list;
+    }
+
+    /**
+     * Converts an object to a map with property values. This method is used
+     * when dealing with mapped properties. It assumes that mapped properties
+     * are stored in a Map&lt;String, Object&gt;.
+     *
+     * @param obj the object to be converted
+     * @return the resulting properties map
+     */
+    private static Map<String, Object> toPropertyMap(final Object obj) {
+        @SuppressWarnings("unchecked")
+        final
+        // mapped properties are stores in maps of type <String, Object>
+        Map<String, Object> map = (Map<String, Object>) obj;
+        return map;
+    }
+}