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<String, Object> 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