You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by si...@apache.org on 2012/02/03 22:05:58 UTC

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

Author: simonetripodi
Date: Fri Feb  3 21:05:57 2012
New Revision: 1240336

URL: http://svn.apache.org/viewvc?rev=1240336&view=rev
Log:
[SANDBOX-379] Implement describe() on DefaultBeanAccessor - patch provided by Benedikt Ritter

Added:
    commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/DescribeTestCase.java   (with props)
Modified:
    commons/sandbox/beanutils2/trunk/src/changes/changes.xml
    commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/BeanAccessor.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/PropertyDescriptorsRegistry.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=1240336&r1=1240335&r2=1240336&view=diff
==============================================================================
--- commons/sandbox/beanutils2/trunk/src/changes/changes.xml (original)
+++ commons/sandbox/beanutils2/trunk/src/changes/changes.xml Fri Feb  3 21:05:57 2012
@@ -23,6 +23,9 @@
   </properties>
   <body>
   <release version="0.1" date="201?-??-??" description="First release.">
+    <action dev="simonetripodi" type="update" issue="SANDBOX-379" due-to="Benedikt Ritter">
+      Implement describe() on DefaultBeanAccessor
+    </action>
     <action dev="simonetripodi" type="update" issue="SANDBOX-378" due-to="Benedikt Ritter">
       Extend StaticMethodsTestCase
     </action>

Modified: commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/BeanAccessor.java
URL: http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/BeanAccessor.java?rev=1240336&r1=1240335&r2=1240336&view=diff
==============================================================================
--- commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/BeanAccessor.java (original)
+++ commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/BeanAccessor.java Fri Feb  3 21:05:57 2012
@@ -56,8 +56,46 @@ public interface BeanAccessor<B>
 
     // description
 
+    /**
+     * <p>Return the entire set of properties for which the specified bean
+     * provides a read method. This map contains the property names as keys
+     * and the property values as values, for all properties the bean provides
+     * a read method for (i.e. where the getReadMethod() returns non-null).</p>
+     *
+     * <p>This map can be fed back to a call to
+     * <code>BeanAccessor.populate()</code> to reconstitute the same set of
+     * properties, modulo differences for read-only and write-only
+     * properties, but only if there are no indexed properties.</p>
+     *
+     * <p><strong>Warning:</strong> if any of the bean property implementations
+     * contain (directly or indirectly) a call to this method then
+     * a stack overflow may result. For example:
+     * <code><pre>
+     * class MyBean
+     * {
+     *    public Map&lt;String, Object&gt; getParameterMap()
+     *    {
+     *         on( this ).describe;
+     *    }
+     * }
+     * </pre></code>
+     * will result in an infinite regression when <code>getParametersMap</code>
+     * is called. It is recommended that such methods are given alternative
+     * names (for example, <code>parametersMap</code>).
+     * </p>
+     * @return Map that contains the property names as keys and property values as values.
+     *
+     * @exception IllegalAccessException if the caller does not have
+     *  access to the property accessor method
+     * @exception InvocationTargetException if one of the property accessor methods
+     *  throws an exception
+     *  TODO what is meant with "this property" in the context of describe()?
+     * @exception NoSuchMethodException if an accessor method for this
+     *  property cannot be found
+     * @throws IntrospectionException TODO
+     */
     Map<String, Object> describe()
-        throws IllegalAccessException, InvocationTargetException, NoSuchMethodException;
+        throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, IntrospectionException;
 
     void populate( Map<String, Object> properties );
 
@@ -65,7 +103,7 @@ public interface BeanAccessor<B>
 
     /**
      * Invokes the method with name {@code methodName}. Arguments are casted to fit the methods signature, if possible.
-     * 
+     *
      * @param methodName the name of the method to invoke. Must not be {@code null}!
      * @return a {@link ArgumentsAccessor} to specify any arguments
      * @throws NoSuchMethodException if there no method with the name {@code methodName} can be found or the arguments
@@ -78,7 +116,7 @@ public interface BeanAccessor<B>
 
     /**
      * Invoke the method with name {@code methodName} and the exact arguments.
-     * 
+     *
      * @param methodName the name of the method to invoke. Must not be {@code null}!
      * @return a {@link ArgumentsAccessor} to specify any arguments
      * @throws NoSuchMethodException if no method with the name {@code methodName} cand be found that accepts the exact

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=1240336&r1=1240335&r2=1240336&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 Fri Feb  3 21:05:57 2012
@@ -25,6 +25,7 @@ import static org.apache.commons.beanuti
 import java.beans.IntrospectionException;
 import java.beans.PropertyDescriptor;
 import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
 import java.util.Map;
 
 final class DefaultBeanAccessor<B>
@@ -115,17 +116,29 @@ final class DefaultBeanAccessor<B>
 
     }
 
+    /**
+     * {@inheritDoc}
+     */
     public Map<String, Object> describe()
-        throws IllegalAccessException, InvocationTargetException, NoSuchMethodException
+        throws IllegalAccessException, IntrospectionException, InvocationTargetException, NoSuchMethodException
     {
-        // TODO Auto-generated method stub
-        return null;
+        Map<String, PropertyDescriptor> propertiesIndex = registry.getPropertiesIndex( bean.getClass() );
+        Map<String, Object> result = new HashMap<String, Object>( propertiesIndex.size() );
+        for ( String key : propertiesIndex.keySet() )
+        {
+            PropertyDescriptor propertyDescriptor = propertiesIndex.get( key );
+            if ( propertyDescriptor.getReadMethod() != null )
+            {
+                Object value = propertyDescriptor.getReadMethod().invoke( bean );
+                result.put( key, value );
+            }
+        }
+        return result;
     }
 
     public void populate( Map<String, Object> properties )
     {
         // TODO Auto-generated method stub
-
     }
 
     /**

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=1240336&r1=1240335&r2=1240336&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 Fri Feb  3 21:05:57 2012
@@ -26,6 +26,7 @@ import java.beans.IntrospectionException
 import java.beans.PropertyDescriptor;
 import java.lang.ref.Reference;
 import java.lang.ref.WeakReference;
+import java.lang.reflect.Method;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.WeakHashMap;
@@ -39,6 +40,8 @@ import java.util.concurrent.locks.Reentr
 final class PropertyDescriptorsRegistry
 {
 
+    private static final AccessibleObjectsRegistry<Method> METHODS_REGISTRY = AccessibleObjectsRegistry.getMethodsRegistry();
+
     private static final PropertyDescriptorsRegistry INSTANCE = new PropertyDescriptorsRegistry();
 
     public static PropertyDescriptorsRegistry getInstance()
@@ -52,14 +55,14 @@ final class PropertyDescriptorsRegistry
                     new WeakHashMap<Class<?>, WeakReference<Map<String, PropertyDescriptor>>>();
 
     /**
-     * This class cann not be instantiated.
+     * This class can not be instantiated.
      */
     private PropertyDescriptorsRegistry()
     {
         // do nothing
     }
 
-    public PropertyDescriptor getPropertyDescriptor( Class<?> beanType, String propertyName )
+    public Map<String, PropertyDescriptor> getPropertiesIndex( Class<?> beanType )
         throws IntrospectionException
     {
         Lock readLock = lock.readLock();
@@ -69,7 +72,7 @@ final class PropertyDescriptorsRegistry
             Reference<Map<String, PropertyDescriptor>> methodReference = cache.get( beanType );
             if ( methodReference != null )
             {
-                return methodReference.get().get( propertyName );
+                return methodReference.get();
             }
         }
         finally
@@ -86,12 +89,13 @@ final class PropertyDescriptorsRegistry
 
             for ( PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors() )
             {
+                makeMethodsAccessible( beanType, propertyDescriptor );
                 propertiesIndex.put( propertyDescriptor.getName(), propertyDescriptor );
             }
 
             cache.put( beanType, new WeakReference<Map<String, PropertyDescriptor>>( propertiesIndex ) );
 
-            return propertiesIndex.get( propertyName );
+            return propertiesIndex;
         }
         finally
         {
@@ -99,4 +103,29 @@ final class PropertyDescriptorsRegistry
         }
     }
 
+    private static void makeMethodsAccessible( Class<?> beanType, PropertyDescriptor propertyDescriptor )
+        throws IntrospectionException
+    {
+        // we need to make sure that methods are accessible for anonymous types
+        // see https://issues.apache.org/jira/browse/BEANUTILS-157
+        if ( propertyDescriptor.getReadMethod() != null )
+        {
+            Method readMethod = propertyDescriptor.getReadMethod();
+            readMethod = METHODS_REGISTRY.get( true, beanType, readMethod.getName() );
+            propertyDescriptor.setReadMethod( readMethod );
+        }
+        if ( propertyDescriptor.getWriteMethod() != null )
+        {
+            Method writeMethod = propertyDescriptor.getWriteMethod();
+            writeMethod = METHODS_REGISTRY.get( true, beanType, writeMethod.getName(), writeMethod.getParameterTypes() );
+            propertyDescriptor.setWriteMethod( writeMethod );
+        }
+    }
+
+    public PropertyDescriptor getPropertyDescriptor( Class<?> beanType, String propertyName )
+        throws IntrospectionException
+    {
+        return getPropertiesIndex( beanType ).get( propertyName );
+    }
+
 }

Added: commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/DescribeTestCase.java
URL: http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/DescribeTestCase.java?rev=1240336&view=auto
==============================================================================
--- commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/DescribeTestCase.java (added)
+++ commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/DescribeTestCase.java Fri Feb  3 21:05:57 2012
@@ -0,0 +1,212 @@
+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.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DescribeTestCase
+{
+
+    private TestBean testBean;
+
+    @Before
+    public void setUp()
+    {
+        testBean = new TestBean();
+    }
+
+    @After
+    public void tearDown()
+    {
+        testBean = null;
+    }
+
+    /**
+     * The set of properties that should be described.
+     */
+    private final String describes[] =
+    { "booleanProperty",
+      "booleanSecond",
+      "byteProperty",
+      "doubleProperty",
+      "dupProperty",
+      "floatProperty",
+      "intArray",
+//            "intIndexed",
+      "longProperty",
+      "listIndexed",
+      "longProperty",
+//            "mappedProperty",
+//            "mappedIntProperty",
+      "nested",
+      "nullProperty",
+      "readOnlyProperty",
+      "shortProperty",
+      "stringArray",
+//            "stringIndexed",
+      "stringProperty"
+    };
+
+    @Test
+    public void describe()
+        throws Exception
+    {
+        Map<String, Object> properties = on( testBean ).describe();
+        assertNotNull( properties );
+
+        // Verify existence of all the properties that should be present
+        for ( int i = 0; i < describes.length; i++ )
+        {
+            assertTrue( "Property '" + describes[i] + "' is not present", properties.containsKey( describes[i] ) );
+        }
+
+        assertTrue( properties.containsKey( "class" ) );
+
+        assertTrue( "Property 'writeOnlyProperty' is not present", !properties.containsKey( "writeOnlyProperty" ) );
+
+        // Verify the values of scalar properties
+        assertEquals( "Value of 'booleanProperty'", true, properties.get( "booleanProperty" ) );
+        assertEquals( "Value of 'byteProperty'", (byte) 121, properties.get( "byteProperty" ) );
+        assertEquals( "Value of 'doubleProperty'", 321.0d, properties.get( "doubleProperty" ) );
+        assertEquals( "Value of 'floatProperty'", 123.0f, properties.get( "floatProperty" ) );
+        assertEquals( "Value of 'intProperty'", 123, properties.get( "intProperty" ) );
+        assertEquals( "Value of 'longProperty'", 321l, properties.get( "longProperty" ) );
+        assertEquals( "Value of 'shortProperty'", (short) 987, properties.get( "shortProperty" ) );
+        assertEquals( "Value of 'stringProperty'", "This is a string", properties.get( "stringProperty" ) );
+    }
+
+    /**
+     * Test with an private class that overrides a public method of a "grand parent" public class.
+     * <p />
+     * See Jira issue# BEANUTILS-157.
+     */
+    @Test
+    public void describeSerializable()
+        throws Exception
+    {
+        Object bean = new Serializable()
+        {
+            private static final long serialVersionUID = 1L;
+
+            @SuppressWarnings( "unused" )
+            public String getX()
+            {
+                return "x-value";
+            }
+
+            @SuppressWarnings( "unused" )
+            public String getY()
+            {
+                return "y-value";
+            }
+        };
+        Map<String, Object> result = on( bean ).describe();
+        // 2 properties + getClass()
+        assertEquals( "Check Size", 3, result.size() );
+        assertTrue( "Class", result.containsKey( "class" ) );
+    }
+
+    /**
+     * Test with an private class that overrides a public method of a "grand parent" public class.
+     * <p />
+     * See Jira issue# BEANUTILS-157.
+     */
+    @Test
+    public void describeInterface()
+        throws Exception
+    {
+        Object bean = new XY()
+        {
+            public String getX()
+            {
+                return "x-value";
+            }
+
+            public String getY()
+            {
+                return "y-value";
+            }
+        };
+        Map<String, Object> result = on( bean ).describe();
+        assertEquals( "Check Size", 3, result.size() );
+        assertTrue( "Class", result.containsKey( "class" ) );
+        assertTrue( "X Key", result.containsKey( "x" ) );
+        assertTrue( "Y Key", result.containsKey( "y" ) );
+        assertEquals( "X Value", "x-value", result.get( "x" ) );
+        assertEquals( "Y Value", "y-value", result.get( "y" ) );
+    }
+
+    /**
+     * Test with an private class that overrides a public method of a "grand parent" public class.
+     * <p />
+     * See Jira issue# BEANUTILS-157.
+     */
+    @Test
+    public void describeBean()
+        throws Exception
+    {
+        Object bean = new FooBar();
+        Map<String, Object> result = on( bean ).describe();
+        assertEquals( "Check Size", 2, result.size() );
+        assertTrue( "Class", result.containsKey( "class" ) );
+        assertTrue( "publicFoo Key", result.containsKey( "publicFoo" ) );
+        assertEquals( "publicFoo Value", "PublicFoo Value", result.get( "publicFoo" ) );
+    }
+
+    public static interface XY
+    {
+        String getX();
+
+        String getY();
+    }
+
+    public static class FooBar
+    {
+        String getPackageFoo()
+        {
+            return "Package Value";
+        }
+
+        @SuppressWarnings( "unused" )
+        private String getPrivateFoo()
+        {
+            return "PrivateFoo Value";
+        }
+
+        protected String getProtectedFoo()
+        {
+            return "ProtectedFoo Value";
+        }
+
+        public String getPublicFoo()
+        {
+            return "PublicFoo Value";
+        }
+    }
+
+}

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

Propchange: commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/DescribeTestCase.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/DescribeTestCase.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain