You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by br...@apache.org on 2013/02/02 19:07:23 UTC

svn commit: r1441781 - in /commons/sandbox/beanutils2/trunk/src: changes/ main/java/org/apache/commons/beanutils2/ test/java/org/apache/commons/beanutils2/ test/java/org/apache/commons/beanutils2/testbeans/

Author: britter
Date: Sat Feb  2 18:07:23 2013
New Revision: 1441781

URL: http://svn.apache.org/viewvc?rev=1441781&view=rev
Log:
[SANDBOX-436] - Implement BeanAccessor.getMapped

Added:
    commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/DefaultMappedPropertyGetterAccessor.java   (with props)
    commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/MappedPropertyDescriptor.java   (with props)
    commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/MethodUtil.java   (with props)
    commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/GetMappedPropertyTestCase.java   (with props)
    commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/MappedPropertyTestCase.java   (with props)
    commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/MethodUtilTest.java   (with props)
    commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyChildBean.java   (with props)
    commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyChildInterface.java   (with props)
    commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyTestBean.java   (with props)
    commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyTestInterface.java   (with props)
Modified:
    commons/sandbox/beanutils2/trunk/src/changes/changes.xml
    commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/BeanProperties.java
    commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/DefaultBeanAccessor.java
    commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/DefaultBeanProperties.java
    commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/PropertyDescriptorsRegistry.java
    commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/ThrowingExceptionBean.java

Modified: commons/sandbox/beanutils2/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/changes/changes.xml?rev=1441781&r1=1441780&r2=1441781&view=diff
==============================================================================
--- commons/sandbox/beanutils2/trunk/src/changes/changes.xml (original)
+++ commons/sandbox/beanutils2/trunk/src/changes/changes.xml Sat Feb  2 18:07:23 2013
@@ -23,6 +23,9 @@
   </properties>
   <body>
   <release version="0.1" date="201?-??-??" description="First release.">
+    <action dev="britter" type="add" issue="SANDBOX-436" due-to="Benedikt Ritter">
+      Implement BeanAccessor.getMapped
+    </action>
     <action dev="britter" type="update" issue="SANDBOX-433" due-to="Benedikt Ritter">
       Setting properties or calling methods very often results in a NullPointerException
     </action>

Modified: commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/BeanProperties.java
URL: http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/BeanProperties.java?rev=1441781&r1=1441780&r2=1441781&view=diff
==============================================================================
--- commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/BeanProperties.java (original)
+++ commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/BeanProperties.java Sat Feb  2 18:07:23 2013
@@ -121,6 +121,12 @@ public interface BeanProperties<B>
     Method getIndexedWriteMethod( String propertyName )
         throws IntrospectionException;
 
+    MappedPropertyDescriptor getMappedPropertyDescriptor( String propertyName )
+        throws IntrospectionException;
+
+    Method getMappedReadMethod( String propertyName )
+        throws IntrospectionException;
+
     /**
      *
      * @return
@@ -128,5 +134,4 @@ public interface BeanProperties<B>
      */
     Map<String, PropertyDescriptor> getPropertiesIndex()
         throws IntrospectionException;
-
 }

Modified: commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/DefaultBeanAccessor.java
URL: http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/DefaultBeanAccessor.java?rev=1441781&r1=1441780&r2=1441781&view=diff
==============================================================================
--- commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/DefaultBeanAccessor.java (original)
+++ commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/DefaultBeanAccessor.java Sat Feb  2 18:07:23 2013
@@ -103,8 +103,17 @@ final class DefaultBeanAccessor<B>
      */
     public MappedPropertyGetterAccessor getMapped( String propertyName )
     {
-        // TODO
-        return null;
+        checkNotNull( propertyName, "Parameter 'propertyName' must not be null!" );
+        Method mappedReadMethod;
+        try
+        {
+            mappedReadMethod = properties.getMappedReadMethod( propertyName );
+        }
+        catch ( IntrospectionException e )
+        {
+            throw new NoSuchPropertyException( propertyName, getBeanClass(), e );
+        }
+        return new DefaultMappedPropertyGetterAccessor<B>( bean, propertyName, mappedReadMethod );
     }
 
     /**

Modified: commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/DefaultBeanProperties.java
URL: http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/DefaultBeanProperties.java?rev=1441781&r1=1441780&r2=1441781&view=diff
==============================================================================
--- commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/DefaultBeanProperties.java (original)
+++ commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/DefaultBeanProperties.java Sat Feb  2 18:07:23 2013
@@ -189,6 +189,29 @@ final class DefaultBeanProperties<B>
     /**
      * {@inheritDoc}
      */
+    public MappedPropertyDescriptor getMappedPropertyDescriptor( String propertyName )
+        throws IntrospectionException
+    {
+        PropertyDescriptor propertyDescriptor = getPropertyDescriptor( propertyName );
+        checkArgument( propertyDescriptor instanceof MappedPropertyDescriptor,
+                       "Property '%s' in bean of type %s is not a mapped property", propertyName, beanClass.getName() );
+        return (MappedPropertyDescriptor) propertyDescriptor;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Method getMappedReadMethod( String propertyName )
+        throws IntrospectionException
+    {
+        MappedPropertyDescriptor mappedPropertyDescriptor = getMappedPropertyDescriptor( propertyName );
+
+        return checkGetterMethod( mappedPropertyDescriptor.getMappedReadMethod(), propertyName );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
     public Map<String, PropertyDescriptor> getPropertiesIndex()
         throws IntrospectionException
     {

Added: commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/DefaultMappedPropertyGetterAccessor.java
URL: http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/DefaultMappedPropertyGetterAccessor.java?rev=1441781&view=auto
==============================================================================
--- commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/DefaultMappedPropertyGetterAccessor.java (added)
+++ commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/DefaultMappedPropertyGetterAccessor.java Sat Feb  2 18:07:23 2013
@@ -0,0 +1,86 @@
+package org.apache.commons.beanutils2;
+
+/*
+ * 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.
+ */
+
+import static org.apache.commons.beanutils2.Assertions.checkNotNull;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+class DefaultMappedPropertyGetterAccessor<B>
+    implements MappedPropertyGetterAccessor
+{
+
+    private final B bean;
+    private final String propertyName;
+    private final Method mappedReadMethod;
+
+    /**
+     * Constructs a new instance of DefaultMappedPropertyGetterAccessor.
+     *
+     * @param bean
+     * @param propertyName
+     * @param mappedReadMethod
+     */
+    public DefaultMappedPropertyGetterAccessor( B bean, String propertyName, Method mappedReadMethod )
+    {
+        this.bean = bean;
+        this.propertyName = propertyName;
+        this.mappedReadMethod = mappedReadMethod;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public BeanAccessor<?> of( String key )
+    {
+        checkNotNull( key, "Can not get mapped property '%s' in bean of type '%s' for null!", propertyName,
+                      bean.getClass().getName() );
+        Object mappedValue = invokeGetter( key );
+        return wrapInAccessor( mappedValue );
+    }
+
+    private BeanAccessor<?> wrapInAccessor( Object mappedValue )
+    {
+        if ( mappedValue != null )
+        {
+            return new DefaultBeanAccessor<Object>( mappedValue );
+        }
+        else
+        {
+            return new NullBeanAccessor<Object>( bean.getClass().getName(), mappedReadMethod.getName() );
+        }
+    }
+
+    private Object invokeGetter( String key )
+    {
+        try
+        {
+            return mappedReadMethod.invoke( bean, key );
+        }
+        catch ( IllegalAccessException e )
+        {
+            throw new PropertyGetterNotAccessibleException( propertyName, mappedReadMethod.getName(), bean.getClass(), e );
+        }
+        catch ( InvocationTargetException e )
+        {
+            throw new PropertyGetterInvocationException( propertyName, mappedReadMethod.getName(), bean.getClass(), e );
+        }
+    }
+
+}

Propchange: commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/DefaultMappedPropertyGetterAccessor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/MappedPropertyDescriptor.java
URL: http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/MappedPropertyDescriptor.java?rev=1441781&view=auto
==============================================================================
--- commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/MappedPropertyDescriptor.java (added)
+++ commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/MappedPropertyDescriptor.java Sat Feb  2 18:07:23 2013
@@ -0,0 +1,578 @@
+/*
+ * 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.IntrospectionException;
+import java.beans.PropertyDescriptor;
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * EXTRACTED FROM BeanUtils1
+ *
+ * A MappedPropertyDescriptor describes one mapped property. Mapped properties are multivalued properties like indexed
+ * properties but that are accessed with a String key instead of an index. Such property values are typically stored in
+ * a Map collection. For this class to work properly, a mapped value must have getter and setter methods of the form
+ * <p>
+ * <code>get<strong>Property</strong>(String key)<code> and
+ * <p><code>set<strong>Property</strong>(String key, Object value)<code>,
+ * <p>where <code><strong>Property</strong></code> must be replaced by the name of the property.
+ *
+ * @see java.beans.PropertyDescriptor
+ * @author Rey Francois
+ * @author Gregor Rayman
+ * @version $Revision: 806915 $ $Date: 2009-08-23 02:50:23 +0200 (Sun, 23 Aug 2009) $
+ */
+
+public class MappedPropertyDescriptor
+    extends PropertyDescriptor
+{
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The underlying data type of the property we are describing.
+     */
+    private Reference<Class<?>> mappedPropertyTypeRef;
+
+    /**
+     * The reader method for this property (if any).
+     */
+    private MappedMethodReference mappedReadMethodRef;
+
+    /**
+     * The writer method for this property (if any).
+     */
+    private MappedMethodReference mappedWriteMethodRef;
+
+    /**
+     * The parameter types array for the reader method signature.
+     */
+    @SuppressWarnings( "unchecked" )
+    // we can not create a generic array
+    private static final Class<String>[] STRING_CLASS_PARAMETER = new Class[] { String.class };
+
+    // ----------------------------------------------------------- Constructors
+
+    /**
+     * Constructs a MappedPropertyDescriptor for a property that follows the standard Java convention by having getFoo
+     * and setFoo accessor methods, with the addition of a String parameter (the key). Thus if the argument name is
+     * "fred", it will assume that the writer method is "setFred" and the reader method is "getFred". Note that the
+     * property name should start with a lower case character, which will be capitalized in the method names.
+     *
+     * @param propertyName The programmatic name of the property.
+     * @param beanClass The Class object for the target bean. For example sun.beans.OurButton.class.
+     * @exception IntrospectionException if an exception occurs during introspection.
+     */
+    public MappedPropertyDescriptor( String propertyName, Class<?> beanClass )
+        throws IntrospectionException
+    {
+
+        super( propertyName, null, null );
+
+        if ( propertyName == null || propertyName.length() == 0 )
+        {
+            throw new IntrospectionException( "bad property name: " + propertyName + " on class: "
+                + beanClass.getClass().getName() );
+        }
+
+        setName( propertyName );
+        String base = capitalizePropertyName( propertyName );
+
+        // Look for mapped read method and matching write method
+        Method mappedReadMethod = null;
+        Method mappedWriteMethod = null;
+        try
+        {
+            try
+            {
+                mappedReadMethod = getMethod( beanClass, "get" + base, STRING_CLASS_PARAMETER );
+            }
+            catch ( IntrospectionException e )
+            {
+                mappedReadMethod = getMethod( beanClass, "is" + base, STRING_CLASS_PARAMETER );
+            }
+            Class<?>[] params = { String.class, mappedReadMethod.getReturnType() };
+            mappedWriteMethod = getMethod( beanClass, "set" + base, params );
+        }
+        catch ( IntrospectionException e )
+        {
+            /*
+             * Swallow IntrospectionException TODO: Why?
+             */
+        }
+
+        // If there's no read method, then look for just a write method
+        if ( mappedReadMethod == null )
+        {
+            mappedWriteMethod = getMethod( beanClass, "set" + base, 2 );
+        }
+
+        if ( ( mappedReadMethod == null ) && ( mappedWriteMethod == null ) )
+        {
+            throw new IntrospectionException( "Property '" + propertyName + "' not found on " + beanClass.getName() );
+        }
+        mappedReadMethodRef = new MappedMethodReference( mappedReadMethod );
+        mappedWriteMethodRef = new MappedMethodReference( mappedWriteMethod );
+
+        findMappedPropertyType();
+    }
+
+    /**
+     * This constructor takes the name of a mapped property, and method names for reading and writing the property.
+     *
+     * @param propertyName The programmatic name of the property.
+     * @param beanClass The Class object for the target bean. For example sun.beans.OurButton.class.
+     * @param mappedGetterName The name of the method used for reading one of the property values. May be null if the
+     *            property is write-only.
+     * @param mappedSetterName The name of the method used for writing one of the property values. May be null if the
+     *            property is read-only.
+     * @exception IntrospectionException if an exception occurs during introspection.
+     */
+    public MappedPropertyDescriptor( String propertyName, Class<?> beanClass, String mappedGetterName,
+                                     String mappedSetterName )
+        throws IntrospectionException
+    {
+
+        super( propertyName, null, null );
+
+        if ( propertyName == null || propertyName.length() == 0 )
+        {
+            throw new IntrospectionException( "bad property name: " + propertyName );
+        }
+        setName( propertyName );
+
+        // search the mapped get and set methods
+        Method mappedReadMethod = null;
+        Method mappedWriteMethod = null;
+        mappedReadMethod = getMethod( beanClass, mappedGetterName, STRING_CLASS_PARAMETER );
+
+        if ( mappedReadMethod != null )
+        {
+            Class<?>[] params = { String.class, mappedReadMethod.getReturnType() };
+            mappedWriteMethod = getMethod( beanClass, mappedSetterName, params );
+        }
+        else
+        {
+            mappedWriteMethod = getMethod( beanClass, mappedSetterName, 2 );
+        }
+        mappedReadMethodRef = new MappedMethodReference( mappedReadMethod );
+        mappedWriteMethodRef = new MappedMethodReference( mappedWriteMethod );
+
+        findMappedPropertyType();
+    }
+
+    /**
+     * This constructor takes the name of a mapped property, and Method objects for reading and writing the property.
+     *
+     * @param propertyName The programmatic name of the property.
+     * @param mappedGetter The method used for reading one of the property values. May be be null if the property is
+     *            write-only.
+     * @param mappedSetter The method used for writing one the property values. May be null if the property is
+     *            read-only.
+     * @exception IntrospectionException if an exception occurs during introspection.
+     */
+    public MappedPropertyDescriptor( String propertyName, Method mappedGetter, Method mappedSetter )
+        throws IntrospectionException
+    {
+
+        super( propertyName, mappedGetter, mappedSetter );
+
+        if ( propertyName == null || propertyName.length() == 0 )
+        {
+            throw new IntrospectionException( "bad property name: " + propertyName );
+        }
+
+        setName( propertyName );
+        mappedReadMethodRef = new MappedMethodReference( mappedGetter );
+        mappedWriteMethodRef = new MappedMethodReference( mappedSetter );
+        findMappedPropertyType();
+    }
+
+    // -------------------------------------------------------- Public Methods
+
+    /**
+     * Gets the Class object for the property values.
+     *
+     * @return The Java type info for the property values. Note that the "Class" object may describe a built-in Java
+     *         type such as "int". The result may be "null" if this is a mapped property that does not support non-keyed
+     *         access.
+     *         <p>
+     *         This is the type that will be returned by the mappedReadMethod.
+     */
+    public Class<?> getMappedPropertyType()
+    {
+        return mappedPropertyTypeRef.get();
+    }
+
+    /**
+     * Gets the method that should be used to read one of the property value.
+     *
+     * @return The method that should be used to read the property value. May return null if the property can't be read.
+     */
+    public Method getMappedReadMethod()
+    {
+        return mappedReadMethodRef.get();
+    }
+
+    /**
+     * Sets the method that should be used to read one of the property value.
+     *
+     * @param mappedGetter The mapped getter method.
+     * @throws IntrospectionException If an error occurs finding the mapped property
+     */
+    public void setMappedReadMethod( Method mappedGetter )
+        throws IntrospectionException
+    {
+        mappedReadMethodRef = new MappedMethodReference( mappedGetter );
+        findMappedPropertyType();
+    }
+
+    /**
+     * Gets the method that should be used to write one of the property value.
+     *
+     * @return The method that should be used to write one of the property value. May return null if the property can't
+     *         be written.
+     */
+    public Method getMappedWriteMethod()
+    {
+        return mappedWriteMethodRef.get();
+    }
+
+    /**
+     * Sets the method that should be used to write the property value.
+     *
+     * @param mappedSetter The mapped setter method.
+     * @throws IntrospectionException If an error occurs finding the mapped property
+     */
+    public void setMappedWriteMethod( Method mappedSetter )
+        throws IntrospectionException
+    {
+        mappedWriteMethodRef = new MappedMethodReference( mappedSetter );
+        findMappedPropertyType();
+    }
+
+    // ------------------------------------------------------- Private Methods
+
+    /**
+     * Introspect our bean class to identify the corresponding getter and setter methods.
+     */
+    private void findMappedPropertyType()
+        throws IntrospectionException
+    {
+        try
+        {
+            Method mappedReadMethod = getMappedReadMethod();
+            Method mappedWriteMethod = getMappedWriteMethod();
+            Class<?> mappedPropertyType = null;
+            if ( mappedReadMethod != null )
+            {
+                if ( mappedReadMethod.getParameterTypes().length != 1 )
+                {
+                    throw new IntrospectionException( "bad mapped read method arg count" );
+                }
+                mappedPropertyType = mappedReadMethod.getReturnType();
+                if ( mappedPropertyType == Void.TYPE )
+                {
+                    throw new IntrospectionException( "mapped read method " + mappedReadMethod.getName()
+                        + " returns void" );
+                }
+            }
+
+            if ( mappedWriteMethod != null )
+            {
+                Class<?>[] params = mappedWriteMethod.getParameterTypes();
+                if ( params.length != 2 )
+                {
+                    throw new IntrospectionException( "bad mapped write method arg count" );
+                }
+                if ( mappedPropertyType != null && mappedPropertyType != params[1] )
+                {
+                    throw new IntrospectionException( "type mismatch between mapped read and write methods" );
+                }
+                mappedPropertyType = params[1];
+            }
+            mappedPropertyTypeRef = new SoftReference<Class<?>>( mappedPropertyType );
+        }
+        catch ( IntrospectionException ex )
+        {
+            throw ex;
+        }
+    }
+
+    /**
+     * Return a capitalized version of the specified property name.
+     *
+     * @param s The property name
+     */
+    private static String capitalizePropertyName( String s )
+    {
+        if ( s.length() == 0 )
+        {
+            return s;
+        }
+
+        char[] chars = s.toCharArray();
+        chars[0] = Character.toUpperCase( chars[0] );
+        return new String( chars );
+    }
+
+    /**
+     * Find a method on a class with a specified number of parameters.
+     */
+    private static Method internalGetMethod( Class<?> initial, String methodName, int parameterCount )
+    {
+        // For overridden methods we need to find the most derived version.
+        // So we start with the given class and walk up the superclass chain.
+        for ( Class<?> clazz = initial; clazz != null; clazz = clazz.getSuperclass() )
+        {
+            Method[] methods = clazz.getDeclaredMethods();
+            for ( int i = 0; i < methods.length; i++ )
+            {
+                Method method = methods[i];
+                if ( method == null )
+                {
+                    continue;
+                }
+                // skip static methods.
+                int mods = method.getModifiers();
+                if ( !Modifier.isPublic( mods ) || Modifier.isStatic( mods ) )
+                {
+                    continue;
+                }
+                if ( method.getName().equals( methodName ) && method.getParameterTypes().length == parameterCount )
+                {
+                    return method;
+                }
+            }
+        }
+
+        // Now check any inherited interfaces. This is necessary both when
+        // the argument class is itself an interface, and when the argument
+        // class is an abstract class.
+        Class<?>[] interfaces = initial.getInterfaces();
+        for ( int i = 0; i < interfaces.length; i++ )
+        {
+            Method method = internalGetMethod( interfaces[i], methodName, parameterCount );
+            if ( method != null )
+            {
+                return method;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Find a method on a class with a specified number of parameters.
+     */
+    private static Method getMethod( Class<?> clazz, String methodName, int parameterCount )
+        throws IntrospectionException
+    {
+        if ( methodName == null )
+        {
+            return null;
+        }
+
+        Method method = internalGetMethod( clazz, methodName, parameterCount );
+        if ( method != null )
+        {
+            return method;
+        }
+
+        // No Method found
+        throw new IntrospectionException( "No method \"" + methodName + "\" with " + parameterCount + " parameter(s)" );
+    }
+
+    /**
+     * Find a method on a class with a specified parameter list.
+     */
+    private static Method getMethod( Class<?> clazz, String methodName, Class<?>[] parameterTypes )
+        throws IntrospectionException
+    {
+        if ( methodName == null )
+        {
+            return null;
+        }
+
+        Method method = AccessibleObjectsRegistry.getMethodsRegistry().get( true, clazz, methodName, parameterTypes );
+        if ( method != null )
+        {
+            return method;
+        }
+
+        int parameterCount = ( parameterTypes == null ) ? 0 : parameterTypes.length;
+
+        // No Method found
+        throw new IntrospectionException( "No method \"" + methodName + "\" with " + parameterCount
+            + " parameter(s) of matching types." );
+    }
+
+    /**
+     * Holds a {@link Method} in a {@link SoftReference} so that it it doesn't prevent any ClassLoader being garbage
+     * collected, but tries to re-create the method if the method reference has been released. See
+     * http://issues.apache.org/jira/browse/BEANUTILS-291
+     */
+    private static class MappedMethodReference
+    {
+        private String className;
+
+        private String methodName;
+
+        private Reference<Method> methodRef;
+
+        private Reference<Class<?>> classRef;
+
+        private Reference<Class<?>> writeParamTypeRef0;
+
+        private Reference<Class<?>> writeParamTypeRef1;
+
+        private String[] writeParamClassNames;
+
+        MappedMethodReference( Method m )
+        {
+            if ( m != null )
+            {
+                className = m.getDeclaringClass().getName();
+                methodName = m.getName();
+                methodRef = new SoftReference<Method>( m );
+                classRef = new WeakReference<Class<?>>( m.getDeclaringClass() );
+                Class<?>[] types = m.getParameterTypes();
+                if ( types.length == 2 )
+                {
+                    writeParamTypeRef0 = new WeakReference<Class<?>>( types[0] );
+                    writeParamTypeRef1 = new WeakReference<Class<?>>( types[1] );
+                    writeParamClassNames = new String[2];
+                    writeParamClassNames[0] = types[0].getName();
+                    writeParamClassNames[1] = types[1].getName();
+                }
+            }
+        }
+
+        private Method get()
+        {
+            if ( methodRef == null )
+            {
+                return null;
+            }
+            Method m = methodRef.get();
+            if ( m == null )
+            {
+                Class<?> clazz = classRef.get();
+                if ( clazz == null )
+                {
+                    clazz = reLoadClass();
+                    if ( clazz != null )
+                    {
+                        classRef = new WeakReference<Class<?>>( clazz );
+                    }
+                }
+                if ( clazz == null )
+                {
+                    throw new RuntimeException( "Method " + methodName + " for " + className
+                        + " could not be reconstructed - class reference has gone" );
+                }
+                Class<?>[] paramTypes = null;
+                if ( writeParamClassNames != null )
+                {
+                    paramTypes = new Class[2];
+                    paramTypes[0] = writeParamTypeRef0.get();
+                    if ( paramTypes[0] == null )
+                    {
+                        paramTypes[0] = reLoadClass( writeParamClassNames[0] );
+                        if ( paramTypes[0] != null )
+                        {
+                            writeParamTypeRef0 = new WeakReference<Class<?>>( paramTypes[0] );
+                        }
+                    }
+                    paramTypes[1] = writeParamTypeRef1.get();
+                    if ( paramTypes[1] == null )
+                    {
+                        paramTypes[1] = reLoadClass( writeParamClassNames[1] );
+                        if ( paramTypes[1] != null )
+                        {
+                            writeParamTypeRef1 = new WeakReference<Class<?>>( paramTypes[1] );
+                        }
+                    }
+                }
+                else
+                {
+                    paramTypes = STRING_CLASS_PARAMETER;
+                }
+                try
+                {
+                    m = clazz.getMethod( methodName, paramTypes );
+                    // Un-comment following line for testing
+                    // System.out.println("Recreated Method " + methodName + " for " + className);
+                }
+                catch ( NoSuchMethodException e )
+                {
+                    throw new RuntimeException( "Method " + methodName + " for " + className
+                        + " could not be reconstructed - method not found" );
+                }
+                methodRef = new SoftReference<Method>( m );
+            }
+            return m;
+        }
+
+        /**
+         * Try to re-load the class
+         */
+        private Class<?> reLoadClass()
+        {
+            return reLoadClass( className );
+        }
+
+        /**
+         * Try to re-load the class
+         */
+        private Class<?> reLoadClass( String name )
+        {
+
+            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+
+            // Try the context class loader
+            if ( classLoader != null )
+            {
+                try
+                {
+                    return classLoader.loadClass( name );
+                }
+                catch ( ClassNotFoundException e )
+                {
+                    // ignore
+                }
+            }
+
+            // Try this class's class loader
+            classLoader = MappedPropertyDescriptor.class.getClassLoader();
+            try
+            {
+                return classLoader.loadClass( name );
+            }
+            catch ( ClassNotFoundException e )
+            {
+                return null;
+            }
+        }
+    }
+}

Propchange: commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/MappedPropertyDescriptor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/MethodUtil.java
URL: http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/MethodUtil.java?rev=1441781&view=auto
==============================================================================
--- commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/MethodUtil.java (added)
+++ commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/MethodUtil.java Sat Feb  2 18:07:23 2013
@@ -0,0 +1,58 @@
+package org.apache.commons.beanutils2;
+
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Method;
+
+/**
+ * Utility for handling {@link Method Methods}
+ */
+final class MethodUtil
+{
+
+    private MethodUtil()
+    {
+        // this class cannot be instantiated
+    }
+
+    public static boolean isMappedSetter( Method method )
+    {
+        boolean startsWithSet = method.getName().startsWith( "set" );
+        boolean twoParameters = method.getParameterTypes().length == 2;
+        if ( startsWithSet && twoParameters )
+        {
+            return method.getParameterTypes()[0].equals( String.class );
+        }
+        return false;
+    }
+
+    public static boolean isMappedGetter( Method method )
+    {
+        boolean startsWithGet = method.getName().startsWith( "get" );
+        boolean onlyOneParameter = method.getParameterTypes().length == 1;
+        boolean returnsSomething = !method.getReturnType().equals( Void.TYPE );
+        if ( startsWithGet && onlyOneParameter && returnsSomething )
+        {
+            return method.getParameterTypes()[0].equals( String.class );
+        }
+        return false;
+    }
+
+}

Propchange: commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/MethodUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/PropertyDescriptorsRegistry.java
URL: http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/PropertyDescriptorsRegistry.java?rev=1441781&r1=1441780&r2=1441781&view=diff
==============================================================================
--- commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/PropertyDescriptorsRegistry.java (original)
+++ commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/PropertyDescriptorsRegistry.java Sat Feb  2 18:07:23 2013
@@ -26,7 +26,11 @@ import java.beans.IndexedPropertyDescrip
 import java.beans.IntrospectionException;
 import java.beans.PropertyDescriptor;
 import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.WeakHashMap;
 import java.util.concurrent.locks.Lock;
@@ -74,7 +78,10 @@ final class PropertyDescriptorsRegistry
             propertiesIndex = new HashMap<String, PropertyDescriptor>();
             BeanInfo beanInfo = getBeanInfo( beanType );
 
-            for ( PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors() )
+            List<PropertyDescriptor> propertyDescriptors =
+                new ArrayList<PropertyDescriptor>( Arrays.asList( beanInfo.getPropertyDescriptors() ) );
+            propertyDescriptors.addAll( getMappedPropertyDescriptors( beanType ) );
+            for ( PropertyDescriptor propertyDescriptor : propertyDescriptors )
             {
                 makeMethodsAccessible( beanType, propertyDescriptor );
                 propertiesIndex.put( propertyDescriptor.getName(), propertyDescriptor );
@@ -90,6 +97,24 @@ final class PropertyDescriptorsRegistry
         }
     }
 
+    private Collection<PropertyDescriptor> getMappedPropertyDescriptors( Class<?> beanType ) throws IntrospectionException
+    {
+        Map<String, PropertyDescriptor> mappedPropertyDescirptors = new HashMap<String, PropertyDescriptor>();
+        for ( Method method : beanType.getMethods() )
+        {
+            if ( MethodUtil.isMappedGetter( method ) || MethodUtil.isMappedSetter( method ) )
+            {
+                String propertyName = method.getName().substring( 3 );
+                propertyName = Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1);
+                if ( !mappedPropertyDescirptors.containsKey( propertyName ) )
+                {
+                    mappedPropertyDescirptors.put( propertyName, new MappedPropertyDescriptor( propertyName , beanType ));
+                }
+            }
+        }
+        return mappedPropertyDescirptors.values();
+    }
+
     private void makeMethodsAccessible( Class<?> beanType, PropertyDescriptor propertyDescriptor )
         throws IntrospectionException
     {

Added: commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/GetMappedPropertyTestCase.java
URL: http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/GetMappedPropertyTestCase.java?rev=1441781&view=auto
==============================================================================
--- commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/GetMappedPropertyTestCase.java (added)
+++ commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/GetMappedPropertyTestCase.java Sat Feb  2 18:07:23 2013
@@ -0,0 +1,124 @@
+package org.apache.commons.beanutils2;
+
+/*
+ * 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.
+ */
+
+import static org.apache.commons.beanutils2.BeanUtils.on;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.apache.commons.beanutils2.testbeans.ThrowingExceptionBean;
+import org.junit.Before;
+import org.junit.Test;
+
+public class GetMappedPropertyTestCase
+{
+
+    private TestBean testBean;
+    private ThrowingExceptionBean exceptionBean;
+
+    @Before
+    public void setUp()
+    {
+        testBean = new TestBean();
+        exceptionBean = new ThrowingExceptionBean();
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void getMappedNull()
+        throws Exception
+    {
+        on( testBean ).getMapped( null );
+    }
+
+    @Test( expected = NoSuchPropertyException.class )
+    public void getMappedUnknownProperty()
+        throws Exception
+    {
+        on( testBean ).getMapped( "unknown" );
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void getMappedNotMappedProperty()
+        throws Exception
+    {
+        on( testBean ).getMapped( "intProperty" );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void getMappedIntNullKey()
+        throws Exception
+    {
+        on( testBean ).getMapped( "mappedIntProperty" ).of( null );
+    }
+
+    @Test
+    public void getMappedUnknownKey()
+        throws Exception
+    {
+        BeanAccessor<?> result = on( testBean ).getMapped( "mappedProperty" ).of( "unknownKey" );
+        assertNull( result.get() );
+    }
+
+    @Test
+    public void getMappedMappedProperty()
+        throws Exception
+    {
+        BeanAccessor<?> result = on( testBean ).getMapped( "mappedProperty" ).of( "First Key" );
+        assertEquals( "First Value", result.get() );
+    }
+
+    @Test
+    public void getMappedMappedIntProperty()
+        throws Exception
+    {
+        BeanAccessor<?> result = on( testBean ).getMapped( "mappedIntProperty" ).of( "One" );
+        assertEquals( Integer.valueOf( 1 ), result.get() );
+    }
+
+    @Test
+    public void getMappedMappedObjects()
+        throws Exception
+    {
+        BeanAccessor<?> result = on( testBean ).getMapped( "mappedObjects" ).of( "First Key" );
+        assertEquals( "First Value", result.get() );
+    }
+
+    @Test( expected = PropertyGetterInvocationException.class )
+    public void getExceptionMapped()
+    {
+        on( exceptionBean ).getMapped( "exceptionMapped" ).of( "A Key" );
+    }
+
+    @Test( expected = NoSuchPropertyException.class )
+    public void getPrivateMapped()
+    {
+        on( exceptionBean ).getMapped( "privateMapped" );
+    }
+
+    @Test( expected = NoSuchPropertyException.class )
+    public void getProtectedMapped()
+    {
+        on( exceptionBean ).getMapped( "protecedMapped" );
+    }
+
+    @Test( expected = NoSuchPropertyException.class )
+    public void getDefaultMapped()
+    {
+        on( exceptionBean ).getMapped( "defaultMapped" );
+    }
+}

Propchange: commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/GetMappedPropertyTestCase.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/MappedPropertyTestCase.java
URL: http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/MappedPropertyTestCase.java?rev=1441781&view=auto
==============================================================================
--- commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/MappedPropertyTestCase.java (added)
+++ commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/MappedPropertyTestCase.java Sat Feb  2 18:07:23 2013
@@ -0,0 +1,392 @@
+package org.apache.commons.beanutils2;
+
+/*
+ * 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.
+ */
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import java.beans.IntrospectionException;
+
+import org.apache.commons.beanutils2.testbeans.MappedPropertyChildBean;
+import org.apache.commons.beanutils2.testbeans.MappedPropertyChildInterface;
+import org.apache.commons.beanutils2.testbeans.MappedPropertyTestBean;
+import org.apache.commons.beanutils2.testbeans.MappedPropertyTestInterface;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * <p>
+ * Test Case for the <code>MappedPropertyDescriptor</code>.
+ * </p>
+ * Extracted from BeanUtils1
+ */
+public class MappedPropertyTestCase
+{
+
+    @Test( expected = IntrospectionException.class )
+    public void testConstructorNullClass()
+        throws Exception
+    {
+        new MappedPropertyDescriptor( null, MappedPropertyTestBean.class );
+    }
+
+    @Test( expected = IntrospectionException.class )
+    public void testConstructorEmptyStringClass()
+        throws Exception
+    {
+        new MappedPropertyDescriptor( "", MappedPropertyTestBean.class );
+    }
+
+    @Test( expected = IntrospectionException.class )
+    public void testConstructorNullNullNull()
+        throws Exception
+    {
+        new MappedPropertyDescriptor( null, null, null );
+    }
+
+    @Test( expected = IntrospectionException.class )
+    public void testConstructorEmptyStringNullNull()
+        throws Exception
+    {
+        new MappedPropertyDescriptor( "", null, null );
+    }
+
+    /**
+     * Test valid method name
+     */
+    @Test
+    public void testFound()
+    {
+        String property = "mapproperty";
+        Class<MappedPropertyTestBean> clazz = MappedPropertyTestBean.class;
+        try
+        {
+            MappedPropertyDescriptor desc = new MappedPropertyDescriptor( property, clazz );
+            assertNotNull( "Getter is missing", desc.getMappedReadMethod() );
+            assertNotNull( "Setter is missing", desc.getMappedWriteMethod() );
+        }
+        catch ( Exception ex )
+        {
+            fail( "Property '" + property + "' Not Found in " + clazz.getName() + ": " + ex );
+        }
+    }
+
+    /**
+     * Test boolean "is" method name
+     */
+    @Test
+    public void testBooleanMapped()
+    {
+        String property = "mappedBoolean";
+        Class<MappedPropertyTestBean> clazz = MappedPropertyTestBean.class;
+        try
+        {
+            MappedPropertyDescriptor desc = new MappedPropertyDescriptor( property, clazz );
+            assertNotNull( "Getter is missing", desc.getMappedReadMethod() );
+            assertNotNull( "Setter is missing", desc.getMappedWriteMethod() );
+        }
+        catch ( Exception ex )
+        {
+            fail( "Property '" + property + "' Not Found in " + clazz.getName() + ": " + ex );
+        }
+    }
+
+    /**
+     * Test invalid method name
+     */
+    @Test
+    public void testNotFound()
+    {
+        String property = "xxxxxxx";
+        Class<MappedPropertyTestBean> clazz = MappedPropertyTestBean.class;
+        try
+        {
+            new MappedPropertyDescriptor( property, clazz );
+            fail( "Property '" + property + "' found in " + clazz.getName() );
+        }
+        catch ( Exception ex )
+        {
+            // expected result
+        }
+    }
+
+    /**
+     * Test Mapped Property - Getter only
+     */
+    @Test
+    public void testMappedGetterOnly()
+    {
+        String property = "mappedGetterOnly";
+        Class<MappedPropertyTestBean> clazz = MappedPropertyTestBean.class;
+        try
+        {
+            MappedPropertyDescriptor desc = new MappedPropertyDescriptor( property, clazz );
+            assertNotNull( "Getter is missing", desc.getMappedReadMethod() );
+            assertNull( "Setter is found", desc.getMappedWriteMethod() );
+        }
+        catch ( Exception ex )
+        {
+            fail( "Property '" + property + "' Not Found in " + clazz.getName() + ": " + ex );
+        }
+    }
+
+    /**
+     * Test Mapped Property - Setter Only
+     */
+    @Test
+    public void testMappedSetterOnly()
+    {
+        String property = "mappedSetterOnly";
+        Class<MappedPropertyTestBean> clazz = MappedPropertyTestBean.class;
+        try
+        {
+            MappedPropertyDescriptor desc = new MappedPropertyDescriptor( property, clazz );
+            assertNull( "Getter is found", desc.getMappedReadMethod() );
+            assertNotNull( "Setter is missing", desc.getMappedWriteMethod() );
+        }
+        catch ( Exception ex )
+        {
+            fail( "Property '" + property + "' Not Found in " + clazz.getName() + ": " + ex );
+        }
+    }
+
+    /**
+     * Test Mapped Property - Invalid Setter
+     */
+    @Test
+    public void testInvalidSetter()
+    {
+        String property = "invalidSetter";
+        Class<MappedPropertyTestBean> clazz = MappedPropertyTestBean.class;
+        try
+        {
+            MappedPropertyDescriptor desc = new MappedPropertyDescriptor( property, clazz );
+            assertNotNull( "Getter is missing", desc.getMappedReadMethod() );
+            assertNull( "Setter is found", desc.getMappedWriteMethod() );
+        }
+        catch ( Exception ex )
+        {
+            fail( "Property '" + property + "' Not Found in " + clazz.getName() + ": " + ex );
+        }
+    }
+
+    /**
+     * Test Mapped Property - Invalid Getter
+     */
+    @Test
+    public void testInvalidGetter()
+    {
+        String property = "invalidGetter";
+        Class<MappedPropertyTestBean> clazz = MappedPropertyTestBean.class;
+        try
+        {
+            MappedPropertyDescriptor desc = new MappedPropertyDescriptor( property, clazz );
+            assertNull( "Getter is found", desc.getMappedReadMethod() );
+            assertNotNull( "Setter is missing", desc.getMappedWriteMethod() );
+        }
+        catch ( Exception ex )
+        {
+            fail( "Property '" + property + "' Not Found in " + clazz.getName() + ": " + ex );
+        }
+    }
+
+    /**
+     * Test Mapped Property - Different Types Expect to find the getDifferentTypes() method, but not the
+     * setDifferentTypes() method because setDifferentTypes() sets and Integer, while getDifferentTypes() returns a
+     * Long.
+     */
+    @Test
+    public void testDifferentTypes()
+    {
+        String property = "differentTypes";
+        Class<MappedPropertyTestBean> clazz = MappedPropertyTestBean.class;
+        try
+        {
+            MappedPropertyDescriptor desc = new MappedPropertyDescriptor( property, clazz );
+            assertNotNull( "Getter is missing", desc.getMappedReadMethod() );
+            assertNull( "Setter is found", desc.getMappedWriteMethod() );
+        }
+        catch ( Exception ex )
+        {
+            fail( "Property '" + property + "' Not Found in " + clazz.getName() + ": " + ex );
+        }
+    }
+
+    /**
+     * Test Mpa getter
+     */
+    @Test
+    @Ignore // TODO review this test
+    public void testMapGetter()
+    {
+        MappedPropertyTestBean bean = new MappedPropertyTestBean();
+        Class<MappedPropertyTestBean> clazz = MappedPropertyTestBean.class;
+        String property = "myMap";
+        try
+        {
+            String testValue = "test value";
+            String testKey = "testKey";
+            // BeanUtils.setProperty( bean, "myMap(" + testKey + ")", "test value" );
+            assertEquals( "Map getter", testValue, bean.getMyMap().get( testKey ) );
+        }
+        catch ( Exception ex )
+        {
+            fail( "Test set mapped property failed: " + ex );
+        }
+    }
+
+    /**
+     * Test property with any two args
+     */
+    @Test
+    public void testAnyArgsProperty()
+    {
+        String property = "anyMapped";
+        Class<MappedPropertyTestBean> clazz = MappedPropertyTestBean.class;
+        try
+        {
+            MappedPropertyDescriptor desc = new MappedPropertyDescriptor( property, clazz );
+            assertNull( "Getter is found", desc.getMappedReadMethod() );
+            assertNotNull( "Setter is missing", desc.getMappedWriteMethod() );
+        }
+        catch ( Exception ex )
+        {
+            fail( "Property '" + property + "' Not Found in " + clazz.getName() + ": " + ex );
+        }
+    }
+
+    /**
+     * Test property with two primitve args
+     */
+    @Test
+    public void testPrimitiveArgsProperty()
+    {
+        String property = "mappedPrimitive";
+        Class<MappedPropertyTestBean> clazz = MappedPropertyTestBean.class;
+        try
+        {
+            MappedPropertyDescriptor desc = new MappedPropertyDescriptor( property, clazz );
+            assertNull( "Getter is found", desc.getMappedReadMethod() );
+            assertNotNull( "Setter is missing", desc.getMappedWriteMethod() );
+        }
+        catch ( Exception ex )
+        {
+            fail( "Property '" + property + "' Not Found in " + clazz.getName() + ": " + ex );
+        }
+    }
+
+    /**
+     * Test 'protected' mapped property
+     */
+    @Test( expected = IntrospectionException.class )
+    public void testProtected()
+        throws IntrospectionException
+    {
+        String property = "protectedProperty";
+        Class<MappedPropertyTestBean> clazz = MappedPropertyTestBean.class;
+        new MappedPropertyDescriptor( property, clazz );
+    }
+
+    /**
+     * Test 'public' method in parent
+     */
+    @Test
+    public void testPublicParentMethod()
+    {
+        String property = "mapproperty";
+        Class<MappedPropertyChildBean> clazz = MappedPropertyChildBean.class;
+        try
+        {
+            MappedPropertyDescriptor desc = new MappedPropertyDescriptor( property, clazz );
+            assertNotNull( "Getter is missing", desc.getMappedReadMethod() );
+            assertNotNull( "Setter is missing", desc.getMappedWriteMethod() );
+        }
+        catch ( Exception ex )
+        {
+            fail( "Property '" + property + "' Not Found in " + clazz.getName() + ": " + ex );
+        }
+    }
+
+    /**
+     * Test 'protected' method in parent
+     */
+    @Test( expected = IntrospectionException.class )
+    public void testProtectedParentMethod()
+        throws IntrospectionException
+    {
+        String property = "protectedMapped";
+        Class<MappedPropertyChildBean> clazz = MappedPropertyChildBean.class;
+        new MappedPropertyDescriptor( property, clazz );
+    }
+
+    /**
+     * Test Interface with mapped property
+     */
+    @Test
+    public void testInterfaceMapped()
+    {
+        String property = "mapproperty";
+        Class<MappedPropertyTestInterface> clazz = MappedPropertyTestInterface.class;
+        try
+        {
+            MappedPropertyDescriptor desc = new MappedPropertyDescriptor( property, clazz );
+            assertNotNull( "Getter is missing", desc.getMappedReadMethod() );
+            assertNotNull( "Setter is missing", desc.getMappedWriteMethod() );
+        }
+        catch ( Exception ex )
+        {
+            fail( "Property '" + property + "' Not Found in " + clazz.getName() + ": " + ex );
+        }
+    }
+
+    /**
+     * Test property not found in interface
+     */
+    @Test( expected = IntrospectionException.class )
+    public void testInterfaceNotFound()
+        throws IntrospectionException
+    {
+        String property = "XXXXXX";
+        Class<MappedPropertyTestInterface> clazz = MappedPropertyTestInterface.class;
+        new MappedPropertyDescriptor( property, clazz );
+    }
+
+    /**
+     * Test Interface Inherited mapped property
+     */
+    @Test
+    public void testChildInterfaceMapped()
+    {
+        String property = "mapproperty";
+        Class<MappedPropertyChildInterface> clazz = MappedPropertyChildInterface.class;
+        try
+        {
+            MappedPropertyDescriptor desc = new MappedPropertyDescriptor( property, clazz );
+            assertNotNull( "Getter is missing", desc.getMappedReadMethod() );
+            assertNotNull( "Setter is missing", desc.getMappedWriteMethod() );
+        }
+        catch ( Exception ex )
+        {
+            fail( "Property '" + property + "' Not Found in " + clazz.getName() + ": " + ex );
+        }
+    }
+}

Propchange: commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/MappedPropertyTestCase.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/MethodUtilTest.java
URL: http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/MethodUtilTest.java?rev=1441781&view=auto
==============================================================================
--- commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/MethodUtilTest.java (added)
+++ commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/MethodUtilTest.java Sat Feb  2 18:07:23 2013
@@ -0,0 +1,84 @@
+package org.apache.commons.beanutils2;
+
+/*
+ * 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.
+ */
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.Method;
+
+import org.junit.Test;
+
+public class MethodUtilTest
+{
+
+    @Test
+    public void isMappedSetterWithMappedPropertySetter()
+        throws Exception
+    {
+        Method mappedPropertySetter =
+            TestBean.class.getMethod( "setMappedProperty", new Class<?>[] { String.class, String.class } );
+        assertTrue( MethodUtil.isMappedSetter( mappedPropertySetter ) );
+    }
+
+    @Test
+    public void isMappedSetterWithIndexedPropertySetter()
+        throws Exception
+    {
+        Method indexedPropertySetter =
+            TestBean.class.getMethod( "setIntIndexed", new Class<?>[] { int.class, int.class } );
+        assertFalse( MethodUtil.isMappedSetter( indexedPropertySetter ) );
+    }
+
+    @Test
+    public void isMappedSetterWithStringPropertySetter()
+        throws Exception
+    {
+
+        Method stringPropertySetter = TestBean.class.getMethod( "setStringProperty", new Class<?>[] { String.class } );
+        assertFalse( MethodUtil.isMappedSetter( stringPropertySetter ) );
+    }
+
+    @Test
+    public void isMappedGetterWithMappedPropertyGetter()
+        throws Exception
+    {
+        Method mappedPropertyGetter = TestBean.class.getMethod( "getMappedProperty", new Class<?>[] { String.class } );
+        assertTrue( MethodUtil.isMappedGetter( mappedPropertyGetter ) );
+    }
+
+    @Test
+    public void isMappedGetterWithIndexedPropertyGetter()
+        throws Exception
+    {
+        Method indexedPropertyGetter = TestBean.class.getMethod( "getIntIndexed", new Class<?>[] { int.class } );
+        assertFalse( MethodUtil.isMappedGetter( indexedPropertyGetter ) );
+    }
+
+    @Test
+    public void isMappedGetterWithStringPropertyGetter()
+        throws Exception
+    {
+
+        Method stringPropertyGetter = TestBean.class.getMethod( "getStringProperty", new Class<?>[] {} );
+        assertFalse( MethodUtil.isMappedGetter( stringPropertyGetter ) );
+    }
+
+}

Propchange: commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/MethodUtilTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyChildBean.java
URL: http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyChildBean.java?rev=1441781&view=auto
==============================================================================
--- commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyChildBean.java (added)
+++ commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyChildBean.java Sat Feb  2 18:07:23 2013
@@ -0,0 +1,30 @@
+package org.apache.commons.beanutils2.testbeans;
+
+/*
+ * 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.
+ */
+
+/**
+ * Inherited Mapped property test bean.
+ * Extracted from BeanUtils1
+ *
+ * @version $Revision: 1088801 $ $Date: 2011-04-05 00:06:02 +0200 (Tue, 05 Apr 2011) $
+ */
+public class MappedPropertyChildBean
+    extends MappedPropertyTestBean
+{
+
+}

Propchange: commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyChildBean.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyChildInterface.java
URL: http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyChildInterface.java?rev=1441781&view=auto
==============================================================================
--- commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyChildInterface.java (added)
+++ commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyChildInterface.java Sat Feb  2 18:07:23 2013
@@ -0,0 +1,30 @@
+package org.apache.commons.beanutils2.testbeans;
+
+/*
+ * 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.
+ */
+
+/**
+ * Test Child Interface
+ * Extracted from BeanUtils1
+ *
+ * @version $Revision: 1088801 $ $Date: 2011-04-05 00:06:02 +0200 (Tue, 05 Apr 2011) $
+ */
+public interface MappedPropertyChildInterface
+    extends MappedPropertyTestInterface
+{
+
+}

Propchange: commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyChildInterface.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyTestBean.java
URL: http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyTestBean.java?rev=1441781&view=auto
==============================================================================
--- commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyTestBean.java (added)
+++ commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyTestBean.java Sat Feb  2 18:07:23 2013
@@ -0,0 +1,124 @@
+package org.apache.commons.beanutils2.testbeans;
+
+/*
+ * 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.
+ */
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Just a java bean (JAJB) to try to replicate a reported bug
+ * Extracted from BeanUtils1
+ *
+ * @author Robert Burrell Donkin
+ * @version $Revision: 658830 $ $Date: 2008-05-21 21:56:21 +0200 (Wed, 21 May 2008) $
+ */
+public class MappedPropertyTestBean
+{
+
+    // TODO generics
+    private final Map map = new HashMap();
+
+    private final Map myMap = new HashMap();
+
+    // -------------------------------------------------------------- Properties
+
+    public String getMapproperty( String key )
+    {
+        return (String) map.get( key );
+    }
+
+    public void setMapproperty( String key, String value )
+    {
+        map.put( key, value );
+    }
+
+    public boolean isMappedBoolean( String key )
+    {
+        return ( (Boolean) map.get( key ) ).booleanValue();
+    }
+
+    public void setMappedBoolean( String key, boolean value )
+    {
+        map.put( key, ( value ? Boolean.TRUE : Boolean.FALSE ) );
+    }
+
+    protected String getProtectedMapped( String key )
+    {
+        return (String) map.get( key );
+    }
+
+    protected void setProtectedMapped( String key, String value )
+    {
+        map.put( key, value );
+    }
+
+    public void setMappedPrimitive( int key, int value )
+    {
+        map.put( new Integer( key ), new Integer( value ) );
+    }
+
+    public void setAnyMapped( MappedPropertyTestBean key, MappedPropertyTestBean value )
+    {
+        map.put( key, value );
+    }
+
+    public void setMappedSetterOnly( String key, String value )
+    {
+        map.put( key, value );
+    }
+
+    public String getMappedGetterOnly( String key )
+    {
+        return (String) map.get( key );
+    }
+
+    public String getInvalidGetter( String key, String other )
+    {
+        return (String) map.get( key );
+    }
+
+    public Map getMyMap()
+    {
+        return myMap;
+    }
+
+    public void setInvalidGetter( String key, String value )
+    {
+        map.put( key, value );
+    }
+
+    public String getInvalidSetter( String key )
+    {
+        return (String) map.get( key );
+    }
+
+    public void setInvalidSetter( String key, String value, String other )
+    {
+    }
+
+    public Long getDifferentTypes( String key )
+    {
+        return new Long( ( (Number) map.get( key ) ).longValue() );
+    }
+
+    public void setDifferentTypes( String key, Integer value )
+    {
+        map.put( key, value );
+    }
+
+}

Propchange: commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyTestBean.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyTestInterface.java
URL: http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyTestInterface.java?rev=1441781&view=auto
==============================================================================
--- commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyTestInterface.java (added)
+++ commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyTestInterface.java Sat Feb  2 18:07:23 2013
@@ -0,0 +1,33 @@
+package org.apache.commons.beanutils2.testbeans;
+
+/*
+ * 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.
+ */
+
+/**
+ * Test Interface
+ * Extracted from BeanUtils1
+ *
+ * @version $Revision: 1088801 $ $Date: 2011-04-05 00:06:02 +0200 (Tue, 05 Apr 2011) $
+ */
+public interface MappedPropertyTestInterface
+{
+
+    public String getMapproperty( String key );
+
+    public void setMapproperty( String key, String value );
+
+}

Propchange: commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/MappedPropertyTestInterface.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/ThrowingExceptionBean.java
URL: http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/ThrowingExceptionBean.java?rev=1441781&r1=1441780&r2=1441781&view=diff
==============================================================================
--- commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/ThrowingExceptionBean.java (original)
+++ commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/testbeans/ThrowingExceptionBean.java Sat Feb  2 18:07:23 2013
@@ -140,6 +140,48 @@ public class ThrowingExceptionBean
         // do nothing
     }
 
+    public int getExceptionMapped( String key)
+    {
+        throw new RuntimeException( "Get indexed always throws an exception!" );
+    }
+
+    public void setExceptionMapped( String key, RuntimeException e )
+    {
+        throw e;
+    }
+
+    @SuppressWarnings( "unused" ) // used in IllegalAccessException test cases
+    private String getPrivateMapped( String key )
+    {
+        return "A Value";
+    }
+
+    @SuppressWarnings( "unused" ) // used in IllegalAccessException test cases
+    private void setPrivateMapped( String key, Object value )
+    {
+        // do nothing
+    }
+
+    protected String getProtectedMapped( String key )
+    {
+        return "A Value";
+    }
+
+    protected void setProtectedMapped( String key, String value)
+    {
+        // do nothing
+    }
+
+    String getDefaultMapped( String key )
+    {
+        return "A Value";
+    }
+
+    void setDefaultMapped( String key, String value )
+    {
+        // do nothing
+    }
+
     public static void staticException( String message )
     {
         throw new RuntimeException( message );