Revision: 354
Author:   tfenne
Date:     2006-07-31 20:39:30 -0700 (Mon, 31 Jul 2006)
ViewCVS:  http://svn.sourceforge.net/stripes/?rev=354&view=rev

Log Message:
-----------
Changes to ensure that 1) read-through/write-through of type variables works 
and 2) type variables with wildcards and wildcards with type variables work!

Modified Paths:
--------------
    
trunk/stripes/src/net/sourceforge/stripes/util/bean/PropertyExpressionEvaluation.java
    trunk/tests/src/net/sourceforge/stripes/controller/GenericsBindingTests.java
    
trunk/tests/src/net/sourceforge/stripes/controller/GenericsBindingTests2.java
    
trunk/tests/src/net/sourceforge/stripes/controller/GenericsBindingTestsBaseClass.java
Modified: 
trunk/stripes/src/net/sourceforge/stripes/util/bean/PropertyExpressionEvaluation.java
===================================================================
--- 
trunk/stripes/src/net/sourceforge/stripes/util/bean/PropertyExpressionEvaluation.java
       2006-08-01 03:08:42 UTC (rev 353)
+++ 
trunk/stripes/src/net/sourceforge/stripes/util/bean/PropertyExpressionEvaluation.java
       2006-08-01 03:39:30 UTC (rev 354)
@@ -88,6 +88,17 @@
         Type type = this.bean.getClass();
 
         for (NodeEvaluation current = this.root; current != null; current = 
current.getNext()) {
+            // Firstly if the current type is a wildcard type of a type 
varible try and
+            // figure out what the real value to use is
+            while (type instanceof WildcardType || type instanceof 
TypeVariable) {
+                if (type instanceof WildcardType) {
+                    type = getWildcardTypeBound((WildcardType) type);
+                }
+                else {
+                    type = getTypeVariableValue(current, ((TypeVariable) 
type));
+                }
+            }
+
             // If it's an array, return the component type
             if (type instanceof GenericArrayType) {
                 type = ((GenericArrayType) type).getGenericComponentType();
@@ -295,61 +306,89 @@
         if (type instanceof ParameterizedType) {
             type = ((ParameterizedType) type).getRawType();
         }
-        else if (type instanceof WildcardType) {
-            WildcardType wtype = (WildcardType) type;
-            Type[] bounds = wtype.getLowerBounds();
-            if (bounds.length == 0) {
-                bounds = wtype.getUpperBounds();
+
+        while (type instanceof WildcardType || type instanceof TypeVariable) {
+            if (type instanceof WildcardType) {
+                type = getWildcardTypeBound((WildcardType) type);
             }
+            else if (type instanceof TypeVariable) {
+                type = getTypeVariableValue(evaluation, (TypeVariable) type);
+            }
+        }
 
-            if (bounds.length > 0) {
-                type = bounds[0];
+        // And now that we should have a single type, try and get a Class
+        if (type instanceof Class) {
+            return (Class) type;
+        }
+        else {
+            return null;
+        }
+    }
+
+    /**
+     * Scans backwards in the expression for the last node which contained a 
JavaBean type
+     * and attempts to use the type arguments to that class to find a match 
for the
+     * TypeParameter provided.
+     *
+     * @param evaluation the current NodeEvaluation
+     * @param type the TypeVariable to try and find a more concrete type for
+     * @return the actual type argument for the type variable if possible, or 
null
+     */
+    protected Type getTypeVariableValue(NodeEvaluation evaluation, 
TypeVariable type) {
+        // First try to locate the last node that is a bonafide Class and not 
some other Type
+        Class lastBean = this.bean.getClass();
+        for (NodeEvaluation n = evaluation.getPrevious(); n != null; 
n=n.getPrevious()) {
+            if (n.getValueType() instanceof Class) {
+                lastBean = (Class) n.getValueType();
+                break;
             }
         }
-        else if (type instanceof TypeVariable) {
-            // First try to locate the last node that is a bonafide Class and 
not some other Type
-            Class lastBean = this.bean.getClass();
-            for (NodeEvaluation n = evaluation.getPrevious(); n != null; 
n=n.getPrevious()) {
-                if (n.getValueType() instanceof Class) {
-                    lastBean = (Class) n.getValueType();
-                    break;
-                }
-            }
 
-            TypeVariable variable = (TypeVariable) type;
+        // Now if the super type is parameterized try and loop through the 
type variables
+        // and type arguments in tandem matching a concrete parameter to the 
type variable
+        for (Class beanClass=lastBean; beanClass != null; 
beanClass=beanClass.getSuperclass()) {
+            Type stype = beanClass.getGenericSuperclass();
 
-            // Now if the super type is parameterized try and loop through the 
type variables
-            // and type arguments in tandem matching a concrete parameter to 
the type variable
-            for (Class beanClass=lastBean; beanClass != null; 
beanClass=beanClass.getSuperclass()) {
-                Type stype = beanClass.getGenericSuperclass();
+            if (stype instanceof ParameterizedType) {
+                ParameterizedType ptype = (ParameterizedType) stype;
+                Type sclass = ptype.getRawType();
 
-                if (stype instanceof ParameterizedType) {
-                    ParameterizedType ptype = (ParameterizedType) stype;
-                    Type sclass = ptype.getRawType();
+                if (sclass instanceof Class) {
+                    Class parent = (Class) sclass;
+                    TypeVariable[] variables = parent.getTypeParameters();
+                    Type[] arguments = ptype.getActualTypeArguments();
 
-                    if (sclass instanceof Class) {
-                        Class parent = (Class) sclass;
-                        TypeVariable[] variables = parent.getTypeParameters();
-                        Type[] arguments = ptype.getActualTypeArguments();
-
-                        for (int i=0; i<variables.length && 
i<arguments.length; ++i) {
-                            if (variables[i] == variable) {
-                                type = arguments[i];
-                                break;
-                            }
+                    for (int i=0; i<variables.length && i<arguments.length; 
++i) {
+                        if (variables[i] == type) {
+                            return arguments[i];
                         }
                     }
                 }
             }
         }
 
-        // And now that we should have a single type, try and get a Class
-        if (type instanceof Class) {
-            return (Class) type;
+        return null;
+    }
+
+    /**
+     * Gets the preferred bound from the WildcardType provided. In the case of
+     * '? super SomeClass' then 'SomeClass' will be returned. In the case of
+     * '? extends AnotherClass' then 'AnotherClass' will be returned.
+     *
+     * @param wtype the WildcardType to fetch the bounds of
+     * @return the appropriate bound type
+     */
+    protected Type getWildcardTypeBound(WildcardType wtype) {
+        Type[] bounds = wtype.getLowerBounds();
+        if (bounds.length == 0) {
+            bounds = wtype.getUpperBounds();
         }
-        else {
-            return null;
+
+        if (bounds.length > 0) {
+            return bounds[0];
         }
+
+        return null;
     }
 
     /**

Modified: 
trunk/tests/src/net/sourceforge/stripes/controller/GenericsBindingTests.java
===================================================================
--- 
trunk/tests/src/net/sourceforge/stripes/controller/GenericsBindingTests.java    
    2006-08-01 03:08:42 UTC (rev 353)
+++ 
trunk/tests/src/net/sourceforge/stripes/controller/GenericsBindingTests.java    
    2006-08-01 03:39:30 UTC (rev 354)
@@ -7,19 +7,21 @@
 import net.sourceforge.stripes.action.RedirectResolution;
 import net.sourceforge.stripes.action.Resolution;
 import net.sourceforge.stripes.mock.MockRoundtrip;
+import net.sourceforge.stripes.test.TestBean;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
 import java.util.Calendar;
+import java.util.Date;
 
 /**
  *
  * @author Tim Fennell
  */
-public class GenericsBindingTests extends 
GenericsBindingTestsBaseClass<Double,Boolean,Long, Date> implements ActionBean {
+public class GenericsBindingTests
+     extends GenericsBindingTestsBaseClass<TestBean,Double,Boolean,Long, Date>
+  implements ActionBean {
+
     // Stuff necessary to implement ActionBean!
     private ActionBeanContext context;
     public ActionBeanContext getContext() { return context; }
@@ -76,6 +78,19 @@
         Assert.assertEquals(bean.getMap().get(30l), makeDate(2030,1,1));
     }
 
+    @Test(groups="fast")
+    public void testTypeVariableNestedProperties() throws Exception {
+        MockRoundtrip trip = getRoundtrip();
+        trip.addParameter("bean.longProperty", "1234");
+        trip.addParameter("bean.stringProperty", "foobar");
+        trip.execute();
+
+        GenericsBindingTests bean = trip.getActionBean(getClass());
+        Assert.assertNotNull(bean.getBean());
+        Assert.assertEquals(bean.getBean().getLongProperty(), new Long(1234));
+        Assert.assertEquals(bean.getBean().getStringProperty(), "foobar");
+    }
+
     /**
      * Helper method to manufacture dates without time components. Months are 
1 based unlike
      * the retarded Calendar API that uses 1 based everything else and 0 based 
months. Sigh.
@@ -86,20 +101,4 @@
         cal.set(year, month-1, day);
         return cal.getTime();
     }
-}
-
-/** Base class with lots of type variables. */
-class BaseClass<N,E,K,V> {
-    N number;
-    List<E> list;
-    Map<K,V> map;
-
-    public N getNumber() { return number; }
-    public void setNumber(N number) { this.number = number; }
-
-    public List<E> getList() { return list; }
-    public void setList(List<E> list) { this.list = list; }
-
-    public Map<K, V> getMap() { return map; }
-    public void setMap(Map<K, V> map) { this.map = map; }
 }
\ No newline at end of file

Modified: 
trunk/tests/src/net/sourceforge/stripes/controller/GenericsBindingTests2.java
===================================================================
--- 
trunk/tests/src/net/sourceforge/stripes/controller/GenericsBindingTests2.java   
    2006-08-01 03:08:42 UTC (rev 353)
+++ 
trunk/tests/src/net/sourceforge/stripes/controller/GenericsBindingTests2.java   
    2006-08-01 03:39:30 UTC (rev 354)
@@ -25,4 +25,9 @@
     public void testTypeVariableMaps() throws Exception {
         super.testTypeVariableMaps();
     }
+
+    @Test(groups="fast")
+    public void testTypeVariableNestedProperties() throws Exception {
+        super.testTypeVariableNestedProperties();
+    }
 }

Modified: 
trunk/tests/src/net/sourceforge/stripes/controller/GenericsBindingTestsBaseClass.java
===================================================================
--- 
trunk/tests/src/net/sourceforge/stripes/controller/GenericsBindingTestsBaseClass.java
       2006-08-01 03:08:42 UTC (rev 353)
+++ 
trunk/tests/src/net/sourceforge/stripes/controller/GenericsBindingTestsBaseClass.java
       2006-08-01 03:39:30 UTC (rev 354)
@@ -10,16 +10,20 @@
  *
  * @author Tim Fennell 
  */
-public class GenericsBindingTestsBaseClass<N,E,K,V> {
+public class GenericsBindingTestsBaseClass<JB,N,E,K,V> {
+    JB bean;
     N number;
-    List<E> list;
+    List<? extends E> list;
     Map<K,V> map;
 
+    public JB getBean() { return bean; }
+    public void setBean(JB bean) { this.bean = bean; }
+
     public N getNumber() { return number; }
     public void setNumber(N number) { this.number = number; }
 
-    public List<E> getList() { return list; }
-    public void setList(List<E> list) { this.list = list; }
+    public List<? extends E> getList() { return list; }
+    public void setList(List<? extends E> list) { this.list = list; }
 
     public Map<K, V> getMap() { return map; }
     public void setMap(Map<K, V> map) { this.map = map; }


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


-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Stripes-development mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/stripes-development

Reply via email to