[Stripes-dev] SF.net SVN: stripes: [437] trunk

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[Stripes-dev] SF.net SVN: stripes: [437] trunk

tfenne
Revision: 437
          http://svn.sourceforge.net/stripes/?rev=437&view=rev
Author:   tfenne
Date:     2006-10-13 19:42:10 -0700 (Fri, 13 Oct 2006)

Log Message:
-----------
Fix for STS-285: properties specified with type parameters in interfaces don't work correctly.

Modified Paths:
--------------
    trunk/stripes/src/net/sourceforge/stripes/util/bean/PropertyExpressionEvaluation.java

Added Paths:
-----------
    trunk/tests/src/net/sourceforge/stripes/util/bean/GenericInterfaceImplTest.java

Modified: trunk/stripes/src/net/sourceforge/stripes/util/bean/PropertyExpressionEvaluation.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/util/bean/PropertyExpressionEvaluation.java 2006-10-14 02:40:54 UTC (rev 436)
+++ trunk/stripes/src/net/sourceforge/stripes/util/bean/PropertyExpressionEvaluation.java 2006-10-14 02:42:10 UTC (rev 437)
@@ -16,17 +16,18 @@
 
 import net.sourceforge.stripes.util.ReflectUtil;
 
-import java.lang.reflect.Type;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
 import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
 import java.lang.reflect.WildcardType;
-import java.lang.reflect.Array;
-import java.lang.reflect.TypeVariable;
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
-import java.util.Collection;
-import java.beans.PropertyDescriptor;
 
 /**
  * The dynamic partner to a PropertyExpression that represents the evaluation of the expression
@@ -202,10 +203,10 @@
         PropertyDescriptor pd = ReflectUtil.getPropertyDescriptor(beanClass, property);
         if (pd != null) {
             if (pd.getReadMethod() != null) {
-                return pd.getReadMethod().getGenericReturnType();
+                return untangleBridgeMethod(pd.getReadMethod()).getGenericReturnType();
             }
             else {
-                return pd.getWriteMethod().getGenericParameterTypes()[0];
+                return untangleBridgeMethod(pd.getWriteMethod()).getGenericParameterTypes()[0];
             }
         }
         else {
@@ -220,6 +221,45 @@
     }
 
     /**
+     * <p>Locates and returns a non-bridge method for the method supplied. In certain cases the
+     * Introspector will return PropertyDescriptors that contain bridge methods for read
+     * and write methods. This usually results from classes implementing generic interfaces
+     * that contain accessor method specifications with type parameters. Since the bridge
+     * methods have inappropriate/unhelpful return and parameter types it is necessary to
+     * locate the non-bridge method and use that instead.</p>
+     *
+     * <p>When supplied with a non-bridge method, the method parameter passed in is returned
+     * immediately and no other work is performed.</p>
+     *
+     * @param m a Method instance, potentially a bridge method
+     * @return a non-bridge method instance if one is locatable, otherwise the method passed in
+     */
+    protected Method untangleBridgeMethod(Method m) {
+        if (!m.isBridge()) return m;
+
+        try {
+            // If it's a setter method the only way to really find the right method
+            // is to hope that there's only one setter with the same name and a single
+            // parameter!!
+            if (m.getParameterTypes().length == 1) { // deal with set methods
+                String name = m.getName();
+                for (Method m2 : m.getDeclaringClass().getMethods()) {
+                    if (name.equals(m2.getName()) && m2 != m
+                            && m2.getParameterTypes().length == m.getParameterTypes().length) {
+                        return m2;
+                    }
+                }
+            }
+            else { // deal with get methods
+                return m.getDeclaringClass().getMethod(m.getName());
+            }
+        }
+        catch (Exception e) { /* Supress. */ }
+
+        return m;
+    }
+
+    /**
      * <p>Determines the type of the supplied node and sets appropriate information on the node.
      * The type is discovered by fetching (and instantiating if necessary) all prior values
      * in the expression to determine the actual type of the prior node.  The prior node is

Added: trunk/tests/src/net/sourceforge/stripes/util/bean/GenericInterfaceImplTest.java
===================================================================
--- trunk/tests/src/net/sourceforge/stripes/util/bean/GenericInterfaceImplTest.java                        (rev 0)
+++ trunk/tests/src/net/sourceforge/stripes/util/bean/GenericInterfaceImplTest.java 2006-10-14 02:42:10 UTC (rev 437)
@@ -0,0 +1,57 @@
+package net.sourceforge.stripes.util.bean;
+
+import org.testng.annotations.Test;
+import org.testng.Assert;
+
+/**
+ * Tests a corner cases where a property's getter and/or setter method(s) are specified
+ * in an interface using a type parameter. In this case the compiler generates bridge
+ * method(s) that wrap the actual implementations of of the interface methods, and have
+ * erased types!  This test ensures that BeanUtil does the right thing and finds the
+ * non-bridge method(s) and determines their types.
+ *
+ * @author Tim Fennell
+ */
+public class GenericInterfaceImplTest  {
+    /** An interface that has a type parameter for a property type. */
+    public static interface GenericInterface<T> {
+        T getProp();
+        void setProp(T prop);
+    }
+
+    /** A simple implementation of a parameterized interface with a type argument. */
+    public static class GenericImpl implements GenericInterface<GenericImpl> {
+        GenericImpl prop;
+        String stringProperty;
+
+        public GenericImpl getProp() { return prop; }
+        public void setProp(GenericImpl prop) { this.prop = prop; }
+
+        public String getStringProperty() { return stringProperty; }
+        public void setStringProperty(String stringProperty) { this.stringProperty = stringProperty; }
+    }
+
+    /** An interface with a type parameter and only a write method. */
+    public static interface WriteOnlyGenericInterface<T> {
+        void setProp(T prop);
+    }
+
+    /** An implementation of a parameterized interface with only a write method. */
+    public static class WriteOnlyGenericImpl implements WriteOnlyGenericInterface<WriteOnlyGenericImpl> {
+        public void setProp(WriteOnlyGenericImpl prop) {  }
+    }
+
+    @Test(groups="fast")
+    public void testInheritFromGenericInterface() throws Exception {
+        GenericImpl bean = new GenericImpl();
+        BeanUtil.setPropertyValue("prop.stringProperty", bean, "whee");
+        Assert.assertEquals(bean.getProp().getStringProperty(), "whee");
+    }
+
+    @Test(groups="fast")
+    public void testInheritFromWriteOnlyGenericInterface() throws Exception {
+        WriteOnlyGenericImpl bean = new WriteOnlyGenericImpl();
+        Assert.assertEquals(BeanUtil.getPropertyType("prop", bean), WriteOnlyGenericImpl.class);
+    }
+
+}


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.

-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Stripes-development mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/stripes-development