You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by oh...@apache.org on 2014/01/03 20:37:03 UTC

svn commit: r1555229 - in /commons/proper/beanutils/trunk/src: main/java/org/apache/commons/beanutils/BeanIntrospectionData.java test/java/org/apache/commons/beanutils/BeanIntrospectionDataTestCase.java

Author: oheger
Date: Fri Jan  3 19:37:03 2014
New Revision: 1555229

URL: http://svn.apache.org/r1555229
Log:
Added a getWriteMethod() method to BeanIntrospectionData.

BeanIntrospectionData now remembers the names of write methods. If a write
method of a property is no longer stored in its PropertyDescriptor, the method
can now be obtained anew.

Added:
    commons/proper/beanutils/trunk/src/test/java/org/apache/commons/beanutils/BeanIntrospectionDataTestCase.java
Modified:
    commons/proper/beanutils/trunk/src/main/java/org/apache/commons/beanutils/BeanIntrospectionData.java

Modified: commons/proper/beanutils/trunk/src/main/java/org/apache/commons/beanutils/BeanIntrospectionData.java
URL: http://svn.apache.org/viewvc/commons/proper/beanutils/trunk/src/main/java/org/apache/commons/beanutils/BeanIntrospectionData.java?rev=1555229&r1=1555228&r2=1555229&view=diff
==============================================================================
--- commons/proper/beanutils/trunk/src/main/java/org/apache/commons/beanutils/BeanIntrospectionData.java (original)
+++ commons/proper/beanutils/trunk/src/main/java/org/apache/commons/beanutils/BeanIntrospectionData.java Fri Jan  3 19:37:03 2014
@@ -16,7 +16,11 @@
  */
 package org.apache.commons.beanutils;
 
+import java.beans.IntrospectionException;
 import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * <p>
@@ -42,6 +46,9 @@ class BeanIntrospectionData {
     /** An array with property descriptors for the managed bean class. */
     private final PropertyDescriptor[] descriptors;
 
+    /** A map for remembering the write method names for properties. */
+    private final Map<String, String> writeMethodNames;
+
     /**
      * Creates a new instance of {@code BeanIntrospectionData} and initializes its
      * completely.
@@ -49,7 +56,19 @@ class BeanIntrospectionData {
      * @param descs the array with the descriptors of the available properties
      */
     public BeanIntrospectionData(PropertyDescriptor[] descs) {
+        this(descs, setUpWriteMethodNames(descs));
+    }
+
+    /**
+     * Creates a new instance of {@code BeanIntrospectionData} and allows setting the map
+     * with write method names. This constructor is mainly used for testing purposes.
+     *
+     * @param descs the array with the descriptors of the available properties
+     * @param writeMethNames the map with the names of write methods
+     */
+    BeanIntrospectionData(PropertyDescriptor[] descs, Map<String, String> writeMethNames) {
         descriptors = descs;
+        writeMethodNames = writeMethNames;
     }
 
     /**
@@ -76,4 +95,57 @@ class BeanIntrospectionData {
         }
         return null;
     }
+
+    /**
+     * Returns the write method for the property determined by the given
+     * {@code PropertyDescriptor}. This information is normally available in the
+     * descriptor object itself. However, at least by the ORACLE implementation, the
+     * method is stored as a {@code SoftReference}. If this reference has been freed by
+     * the GC, it may be the case that the method cannot be obtained again. Then,
+     * additional information stored in this object is necessary to obtain the method
+     * again.
+     *
+     * @param beanCls the class of the affected bean
+     * @param desc the {@code PropertyDescriptor} of the desired property
+     * @return the write method for this property or <b>null</b> if there is none
+     */
+    public Method getWriteMethod(Class<?> beanCls, PropertyDescriptor desc) {
+        Method method = desc.getWriteMethod();
+        if (method == null) {
+            String methodName = writeMethodNames.get(desc.getName());
+            if (methodName != null) {
+                method = MethodUtils.getAccessibleMethod(beanCls, methodName,
+                        desc.getPropertyType());
+                if (method != null) {
+                    try {
+                        desc.setWriteMethod(method);
+                    } catch (IntrospectionException e) {
+                        // ignore, in this case the method is not cached
+                    }
+                }
+            }
+        }
+
+        return method;
+    }
+
+    /**
+     * Initializes the map with the names of the write methods for the supported
+     * properties. The method names - if defined - need to be stored separately because
+     * they may get lost when the GC claims soft references used by the
+     * {@code PropertyDescriptor} objects.
+     *
+     * @param descs the array with the descriptors of the available properties
+     * @return the map with the names of write methods for properties
+     */
+    private static Map<String, String> setUpWriteMethodNames(PropertyDescriptor[] descs) {
+        Map<String, String> methods = new HashMap<String, String>();
+        for (PropertyDescriptor pd : descs) {
+            Method method = pd.getWriteMethod();
+            if (method != null) {
+                methods.put(pd.getName(), method.getName());
+            }
+        }
+        return methods;
+    }
 }

Added: commons/proper/beanutils/trunk/src/test/java/org/apache/commons/beanutils/BeanIntrospectionDataTestCase.java
URL: http://svn.apache.org/viewvc/commons/proper/beanutils/trunk/src/test/java/org/apache/commons/beanutils/BeanIntrospectionDataTestCase.java?rev=1555229&view=auto
==============================================================================
--- commons/proper/beanutils/trunk/src/test/java/org/apache/commons/beanutils/BeanIntrospectionDataTestCase.java (added)
+++ commons/proper/beanutils/trunk/src/test/java/org/apache/commons/beanutils/BeanIntrospectionDataTestCase.java Fri Jan  3 19:37:03 2014
@@ -0,0 +1,117 @@
+/*
+ * 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.beanutils;
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+/**
+ * Test class for {@code BeanIntrospectionData}.
+ *
+ * @version $Id: $
+ */
+public class BeanIntrospectionDataTestCase extends TestCase {
+    /** Constant for the test bean class. */
+    private static final Class<?> BEAN_CLASS = FluentIntrospectionTestBean.class;
+
+    /** Constant for the name of the test property. */
+    private static final String TEST_PROP = "fluentGetProperty";
+
+    /**
+     * Creates an array with property descriptors for the test bean class.
+     *
+     * @return the array with property descriptors
+     */
+    private static PropertyDescriptor[] fetchDescriptors() {
+        PropertyUtilsBean pub = new PropertyUtilsBean();
+        pub.addBeanIntrospector(new FluentPropertyBeanIntrospector());
+        return pub.getPropertyDescriptors(BEAN_CLASS);
+    }
+
+    /**
+     * Creates a test instance which is initialized with default property descriptors.
+     *
+     * @return the test instance
+     */
+    private static BeanIntrospectionData setUpData() {
+        return new BeanIntrospectionData(fetchDescriptors());
+    }
+
+    /**
+     * Returns the property descriptor for the test property.
+     *
+     * @param bid the data object
+     * @return the test property descriptor
+     */
+    private static PropertyDescriptor fetchTestDescriptor(BeanIntrospectionData bid) {
+        return bid.getDescriptor(TEST_PROP);
+    }
+
+    /**
+     * Tests whether a write method can be queried if it is defined in the descriptor.
+     */
+    public void testGetWriteMethodDefined() {
+        BeanIntrospectionData data = setUpData();
+        PropertyDescriptor pd = fetchTestDescriptor(data);
+        assertNotNull("No write method", pd.getWriteMethod());
+        assertEquals("Wrong write method", pd.getWriteMethod(),
+                data.getWriteMethod(BEAN_CLASS, pd));
+    }
+
+    /**
+     * Tests whether a write method can be queried that is currently not available in the
+     * property descriptor.
+     */
+    public void testGetWriteMethodUndefined() throws Exception {
+        BeanIntrospectionData data = setUpData();
+        PropertyDescriptor pd = fetchTestDescriptor(data);
+        Method writeMethod = pd.getWriteMethod();
+        pd.setWriteMethod(null);
+        assertEquals("Wrong write method", writeMethod,
+                data.getWriteMethod(BEAN_CLASS, pd));
+        assertEquals("Method not set in descriptor", writeMethod, pd.getWriteMethod());
+    }
+
+    /**
+     * Tests getWriteMethod() if the method cannot be resolved. (This is a corner case
+     * which should normally not happen in practice.)
+     */
+    public void testGetWriteMethodNonExisting() throws Exception {
+        PropertyDescriptor pd = new PropertyDescriptor(TEST_PROP,
+                BEAN_CLASS.getMethod("getFluentGetProperty"), BEAN_CLASS.getMethod(
+                        "setFluentGetProperty", String.class));
+        Map<String, String> methods = new HashMap<String, String>();
+        methods.put(TEST_PROP, "hashCode");
+        BeanIntrospectionData data = new BeanIntrospectionData(
+                new PropertyDescriptor[] { pd }, methods);
+        pd.setWriteMethod(null);
+        assertNull("Got a write method", data.getWriteMethod(BEAN_CLASS, pd));
+    }
+
+    /**
+     * Tests getWriteMethod() for a property for which no write method is known.
+     */
+    public void testGetWriteMethodUnknown() {
+        BeanIntrospectionData data = setUpData();
+        PropertyDescriptor pd = data.getDescriptor("class");
+        assertNull("Got a write method", data.getWriteMethod(BEAN_CLASS, pd));
+    }
+}