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/25 20:00:38 UTC

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

Author: simonetripodi
Date: Sat Feb 25 19:00:37 2012
New Revision: 1293663

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

Added:
    commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/CloneTestCase.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

Modified: commons/sandbox/beanutils2/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/changes/changes.xml?rev=1293663&r1=1293662&r2=1293663&view=diff
==============================================================================
--- commons/sandbox/beanutils2/trunk/src/changes/changes.xml (original)
+++ commons/sandbox/beanutils2/trunk/src/changes/changes.xml Sat Feb 25 19:00:37 2012
@@ -23,9 +23,15 @@
   </properties>
   <body>
   <release version="0.1" date="201?-??-??" description="First release.">
+    <action dev="simonetripodi" type="add" issue="SANDBOX-396" due-to="Benedikt Ritter">
+      Implement clone() on DefaultBeanAccessor
+    </action>
     <action dev="simonetripodi" type="update" issue="SANDBOX-390" due-to="Benedikt Ritter">
       Make sure that the internal package does not get exported when packaging as OSGi bundle
     </action>
+    <action dev="simonetripodi" type="update" issue="SANDBOX-389" due-to="Benedikt Ritter">
+      implement populate() on DefaultBeanAccessor
+    </action>
     <action dev="simonetripodi" type="update" issue="SANDBOX-387" due-to="Benedikt Ritter">
       Implement possibility to find out if a property readable and/or wirtable
     </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=1293663&r1=1293662&r2=1293663&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 Sat Feb 25 19:00:37 2012
@@ -76,8 +76,26 @@ public interface BeanAccessor<B>
 
     // clone
 
+    /**
+     * <p>
+     * Clones a bean based on the available property getters and setters, even if the bean class itself does not
+     * implement Cloneable.
+     * </p>
+     * <p>
+     * <strong>Note:</strong> this method creates a <strong>shallow</strong> clone. In other words, any objects referred
+     * to by the bean are shared with the clone rather than being cloned in turn.
+     * </p>
+     *
+     * @return the cloned bean
+     * @throws IllegalAccessException if the caller does not have access to the property accessor method
+     * @throws InstantiationException if a new instance of the bean's class cannot be instantiated
+     * @throws InvocationTargetException if the property accessor method throws an exception
+     * @throws NoSuchMethodException if an accessor method for this property cannot be found
+     * @throws IntrospectionException
+     */
     B cloneBean()
-        throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException;
+        throws IllegalAccessException, InstantiationException, IntrospectionException,
+        InvocationTargetException, NoSuchMethodException;
 
     <T extends B> void copyPropertiesTo( T target )
         throws IllegalAccessException, InvocationTargetException, NoSuchMethodException;

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=1293663&r1=1293662&r2=1293663&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 25 19:00:37 2012
@@ -20,6 +20,7 @@ package org.apache.commons.beanutils2;
  */
 
 import static java.lang.String.format;
+import static org.apache.commons.beanutils2.BeanUtils.on;
 import static org.apache.commons.beanutils2.internal.Assertions.checkArgument;
 import static org.apache.commons.beanutils2.internal.Assertions.checkNotNull;
 
@@ -208,10 +209,19 @@ final class DefaultBeanAccessor<B>
      * {@inheritDoc}
      */
     public B cloneBean()
-        throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException
+        throws IllegalAccessException, InstantiationException, IntrospectionException, InvocationTargetException,
+        NoSuchMethodException
     {
-        // TODO
-        return null;
+        BeanAccessor<B> cloneAccessor = on( getBeanClass() ).newInstance();
+        cloneAccessor.populate( describe() );
+        return cloneAccessor.get();
+    }
+
+    private Class<B> getBeanClass()
+    {
+        @SuppressWarnings( "unchecked" ) // we can be sure, that bean is of Class<B>
+        Class<B> beanType = (Class<B>) bean.getClass();
+        return beanType;
     }
 
     /**
@@ -272,9 +282,8 @@ final class DefaultBeanAccessor<B>
     public ArgumentsAccessor invoke( String methodName )
         throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
     {
-        @SuppressWarnings( "unchecked" )
-        Class<B> beanClass = (Class<B>) bean.getClass();
-        checkNotNull(methodName, "Impossible to execute null method in %s", beanClass.getName());
+        Class<B> beanClass = getBeanClass();
+        checkNotNull( methodName, "Impossible to execute null method in %s", beanClass.getName() );
         return new DefaultArgumentsAccessor( beanClass, false, methodName, bean );
     }
 
@@ -284,9 +293,8 @@ final class DefaultBeanAccessor<B>
     public ArgumentsAccessor invokeExact( String methodName )
         throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
     {
-        @SuppressWarnings( "unchecked" )
-        Class<B> beanClass = (Class<B>) bean.getClass();
-        checkNotNull(methodName, "Impossible to execute null method in %s", beanClass.getName());
+        Class<B> beanClass = getBeanClass();
+        checkNotNull( methodName, "Impossible to execute null method in %s", beanClass.getName() );
         return new DefaultArgumentsAccessor( beanClass, true, methodName, bean );
     }
 

Added: commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/CloneTestCase.java
URL: http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/CloneTestCase.java?rev=1293663&view=auto
==============================================================================
--- commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/CloneTestCase.java (added)
+++ commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/CloneTestCase.java Sat Feb 25 19:00:37 2012
@@ -0,0 +1,178 @@
+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.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class CloneTestCase
+{
+
+    private TestBean original;
+
+    private TestBean clone;
+
+    @Before
+    public void setUp()
+    {
+        original = new TestBean();
+    }
+
+    @After
+    public void tearDown()
+    {
+        original = null;
+        clone = null;
+    }
+
+    @Test
+    public void cloneTestBean()
+        throws Exception
+    {
+        clone = on( new TestBean() ).cloneBean();
+        assertNotNull( "Clone is null!", clone );
+        assertReadWritePropertiesEquals( new TestBean(), clone );
+    }
+
+    @Test
+    public void cloneWithChangedScalarProperties()
+        throws Exception
+    {
+        changeScalarProperties();
+
+        clone = on( original ).cloneBean();
+        assertNotNull( "Clone is null!", clone );
+
+        assertReadWritePropertiesEquals( original, clone );
+    }
+
+    private void changeScalarProperties()
+    {
+        original.setBooleanProperty( false );
+        // booleanSecond is left at true
+        original.setByteProperty( (byte) 111 );
+        original.setDoubleProperty( 432.0 );
+        // floatProperty is left at 123.0
+        original.setIntProperty( 543 );
+        original.setLongProperty( 123456789l );
+        original.setNullProperty( "Non-null value" );
+        original.setShortProperty( (short) 654 );
+        // stringProperty is left at "This is a string"
+    }
+
+    @Test
+    public void cloneWithChangedArrayProperties()
+        throws Exception
+    {
+        changeArrayProperties();
+
+        clone = on( original ).cloneBean();
+        assertNotNull( clone );
+
+        assertReadWritePropertiesEquals( original, clone );
+    }
+
+    private void changeArrayProperties()
+    {
+        int intArray[] = new int[] { 123, 456, 789 };
+        String stringArray[] = new String[] { "New String 0", "New String 1" };
+        original.setIntArray( intArray );
+        original.setStringArray( stringArray );
+    }
+
+    @Test
+    public void cloneWithChangedWriteOnlyProperty()
+        throws Exception
+    {
+        original.setWriteOnlyProperty( "New writeOnlyProperty value" );
+
+        clone = on( original ).cloneBean();
+        assertNotNull( "Clone is null!", clone );
+        assertReadWritePropertiesEquals( original, clone );
+
+        // The value of writeOnlyProperty can not be read out from original, so it must have the
+        // original value
+        assertEquals( "writeOnlyProperty has been changed on clone!", new TestBean().getWriteOnlyPropertyValue(),
+                      clone.getWriteOnlyPropertyValue() );
+    }
+
+    /**
+     * Makes sure, that the general contract of creating a shallow clone is not violated.
+     */
+    @Test
+    public void cloneIsShallow()
+        throws Exception
+    {
+        clone = on( original ).cloneBean();
+
+        assertTrue(clone.getAnotherNested() == original.getAnotherNested());
+        assertTrue(clone.getDateArrayProperty() == original.getDateArrayProperty());
+        assertTrue(clone.getDateProperty() == original.getDateProperty());
+        assertTrue(clone.getDateProperty() == original.getDateProperty());
+        assertTrue(clone.getDupProperty() == original.getDupProperty());
+        assertTrue(clone.getIntArray() == original.getIntArray());
+        assertTrue(clone.getMapProperty() == original.getMapProperty());
+        assertTrue(clone.getStringArray() == original.getStringArray());
+        assertTrue(clone.getStringProperty() == original.getStringProperty());
+    }
+
+    /**
+     * Asserts that all properties that are readable and writable on {@code actual} are equal to those on
+     * {@code expected}.
+     *
+     * @param expected the {@code TestBean} with the expected properties.
+     * @param actual the {@code TestBean} with the actual values.
+     */
+    private static void assertReadWritePropertiesEquals( TestBean expected, TestBean actual )
+    {
+        assertEquals( "Property 'anotherNested' is not equal!", expected.getAnotherNested(), actual.getAnotherNested() );
+        assertEquals( "Property 'booleanProperty' is not equal!", expected.getBooleanProperty(),
+                      actual.getBooleanProperty() );
+        assertEquals( "Property 'booleanSecond' is not equal!", expected.isBooleanSecond(), actual.isBooleanSecond() );
+        assertEquals( "Property 'byteProperty' is not equal!", expected.getByteProperty(), actual.getByteProperty() );
+        assertArrayEquals( "Property 'DateArrayProperty' is not equal!", expected.getDateArrayProperty(),
+                           actual.getDateArrayProperty() );
+        assertEquals( "Property 'dateProperty' is not equal!", expected.getDateProperty(), actual.getDateProperty() );
+        assertEquals( "Property 'doubleProperty' is not equal!", expected.getDoubleProperty(),
+                      actual.getDoubleProperty(), 0.0 );
+        assertArrayEquals( "Property 'dupProperty' is not equal!", expected.getDupProperty(), actual.getDupProperty() );
+        assertEquals( "Property 'floatProperty' is not equal!", expected.getFloatProperty(), actual.getFloatProperty(),
+                      0.0 );
+        assertArrayEquals( "Property 'intArrayProperty' is not equal!", expected.getIntArray(), actual.getIntArray() );
+        assertEquals( "Property 'intProperty' is not equal!", expected.getIntProperty(), actual.getIntProperty() );
+        assertEquals( "Property 'invalidBoolean' is not equal!", expected.getInvalidBoolean(),
+                      actual.getInvalidBoolean() );
+        assertEquals( "Property 'listIndexed' is not equal!", expected.getListIndexed(), actual.getListIndexed() );
+        assertEquals( "Property 'longProperty' is not equal!", expected.getLongProperty(), actual.getLongProperty() );
+        assertEquals( "Property 'mapProperty' is not equal!", expected.getMapProperty(), actual.getMapProperty() );
+        assertEquals( "Property 'nested' is not equal!", expected.getNested(), actual.getNested() );
+        assertEquals( "Property 'nullProperty' is not equal!", expected.getNullProperty(), actual.getNullProperty() );
+        assertEquals( "Property 'shortProperty' is not equal!", expected.getShortProperty(), actual.getShortProperty() );
+        assertArrayEquals( "Property 'stringArray' is not equal!", expected.getStringArray(), actual.getStringArray() );
+        assertEquals( "Property 'stringProperty' is not equal!", expected.getStringProperty(),
+                      actual.getStringProperty() );
+    }
+
+}

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

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

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