Revision: 617
http://stripes.svn.sourceforge.net/stripes/?rev=617&view=rev
Author: mongus
Date: 2007-11-06 11:00:11 -0800 (Tue, 06 Nov 2007)
Log Message:
-----------
patch provided by Alan Burlison to fix STS-426 along with updated unit tests
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
Added Paths:
-----------
trunk/tests/src/net/sourceforge/stripes/test/TestGenericBean.java
Modified:
trunk/stripes/src/net/sourceforge/stripes/util/bean/PropertyExpressionEvaluation.java
===================================================================
---
trunk/stripes/src/net/sourceforge/stripes/util/bean/PropertyExpressionEvaluation.java
2007-10-11 02:10:42 UTC (rev 616)
+++
trunk/stripes/src/net/sourceforge/stripes/util/bean/PropertyExpressionEvaluation.java
2007-11-06 19:00:11 UTC (rev 617)
@@ -25,7 +25,9 @@
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -136,8 +138,7 @@
// Else if it's parameterized and it's a List or Map, get the next
type
if (type instanceof ParameterizedType) {
ParameterizedType ptype = (ParameterizedType) type;
- // TODO: convert to using convertToClass() here
- Type rawType = ptype.getRawType();
+ Type rawType = convertToClass(type, current);
if (rawType instanceof Class) {
Class rawClass = (Class) rawType;
@@ -163,7 +164,7 @@
}
}
else {
- // Raw type is not a class? What on earth do we do now?
+ // XXX Raw type is not a class? What on earth do we do
now?
break;
}
}
@@ -175,6 +176,7 @@
String property = current.getNode().getStringValue();
type = getBeanPropertyType(clazz, property);
+ // XXX What do we do if type is a generic type?
if (type != null) {
current.setValueType(type);
current.setType(NodeType.BeanProperty);
@@ -182,9 +184,8 @@
else {
type = getTypeViaInstances(current);
if (type == null) {
-
+ // XXX What do we do now?
}
-
}
}
}
@@ -392,40 +393,78 @@
* @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
+ protected Type getTypeVariableValue(NodeEvaluation evaluation,
TypeVariable typeVar) {
+
+ // Type map from TypeVariables to the corresponding Type.
+ List<HashMap<TypeVariable, Type>> typemap = new
ArrayList<HashMap<TypeVariable, Type>>();
+
+ // Scan the evaluation chain for the first class or any parameterized
types.
Class lastBean = this.bean.getClass();
- for (NodeEvaluation n = evaluation.getPrevious(); n != null;
n=n.getPrevious()) {
- if (n.getValueType() instanceof Class) {
+ for (NodeEvaluation n = evaluation.getPrevious(); n != null; n =
n.getPrevious()) {
+ Type type = n.getValueType();
+
+ // Bean class found? Stop searching.
+ if (type instanceof Class) {
lastBean = (Class) n.getValueType();
break;
+
+ // Parameterized type? Add to the typemap and keep going.
+ } else if (type instanceof ParameterizedType) {
+ addTypeMappings(typemap, (ParameterizedType) 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();
+ // Add the bean class and all its superclasses to the typemap.
+ for (Class c = lastBean; c != null; c = c.getSuperclass()) {
+ Type t = c.getGenericSuperclass();
+ if (t instanceof ParameterizedType) {
+ addTypeMappings(typemap, (ParameterizedType) t);
+ }
+ }
- if (stype instanceof ParameterizedType) {
- ParameterizedType ptype = (ParameterizedType) stype;
- Type sclass = ptype.getRawType();
+ // Now traverse the typemap list, mapping the TypeVariable.
+ Type type = null;
+ for (int i = typemap.size() - 1; i >= 0; i--) {
- if (sclass instanceof Class) {
- Class parent = (Class) sclass;
- TypeVariable[] variables = parent.getTypeParameters();
- Type[] arguments = ptype.getActualTypeArguments();
+ // Map the type variable to a type.
+ if ((type = typemap.get(i).get(typeVar)) != null) {
- for (int i=0; i<variables.length && i<arguments.length;
++i) {
- if (variables[i] == type) {
- return arguments[i];
- }
- }
+ // Reached a real class? Done.
+ if (type instanceof Class) {
+ break;
+
+ // Another TypeVariable? Keep going.
+ } else if (type instanceof TypeVariable) {
+ typeVar = (TypeVariable) type;
}
}
}
+ return type;
+ }
- return null;
+ /**
+ * Build a map of TypeVariables to Types. We have to traverse the class
hierarchy
+ * from subclass to superclass, but the mapping of TypeVariables to Types
has to start
+ * from the place were the type variable was originally defined
(superclass) and map
+ * to the place where the type is bound to an actual class (subclass). We
therefore
+ * need to build up the mapings sub to super and then traverse super to sub.
+ *
+ * @param paramType parameterized type to add to the map.
+ */
+ private void addTypeMappings(List<HashMap<TypeVariable, Type>> typemap,
+ ParameterizedType paramType) {
+ Type rawType = paramType.getRawType();
+ if (rawType instanceof Class) {
+ Class rawClass = (Class) rawType;
+ TypeVariable[] vars = rawClass.getTypeParameters();
+ Type[] args = paramType.getActualTypeArguments();
+ HashMap<TypeVariable, Type> entry =
+ new HashMap<TypeVariable, Type>(vars.length);
+ for (int i = 0; i < vars.length && i < args.length; ++i) {
+ entry.put(vars[i], args[i]);
+ }
+ typemap.add(entry);
+ }
}
/**
@@ -630,5 +669,4 @@
}
}
}
-
}
Modified:
trunk/tests/src/net/sourceforge/stripes/controller/GenericsBindingTests.java
===================================================================
---
trunk/tests/src/net/sourceforge/stripes/controller/GenericsBindingTests.java
2007-10-11 02:10:42 UTC (rev 616)
+++
trunk/tests/src/net/sourceforge/stripes/controller/GenericsBindingTests.java
2007-11-06 19:00:11 UTC (rev 617)
@@ -19,7 +19,7 @@
* @author Tim Fennell
*/
public class GenericsBindingTests
- extends GenericsBindingTestsBaseClass<TestBean,Double,Boolean,Long, Date>
+ extends GenericsBindingTestsBaseClass<TestBean,Double,Boolean,Long,Date>
implements ActionBean {
// Stuff necessary to implement ActionBean!
@@ -49,6 +49,20 @@
}
@Test(groups="fast")
+ public void testGenericBean() throws Exception {
+ MockRoundtrip trip = getRoundtrip();
+ trip.addParameter("genericBean.genericA", "123.4");
+ trip.addParameter("genericBean.genericB", "true");
+ trip.execute();
+
+ GenericsBindingTests bean =
trip.getActionBean(GenericsBindingTests.class);
+ Assert.assertNotNull(bean.getGenericBean().getGenericA());
+ Assert.assertEquals(bean.getGenericBean().getGenericA(), new
Double(123.4));
+ Assert.assertNotNull(bean.getGenericBean().getGenericB());
+ Assert.assertEquals(bean.getGenericBean().getGenericB(), Boolean.TRUE);
+ }
+
+ @Test(groups="fast")
public void testTypeVariableLists() throws Exception {
MockRoundtrip trip = getRoundtrip();
trip.addParameter("list[0]", "true");
Modified:
trunk/tests/src/net/sourceforge/stripes/controller/GenericsBindingTests2.java
===================================================================
---
trunk/tests/src/net/sourceforge/stripes/controller/GenericsBindingTests2.java
2007-10-11 02:10:42 UTC (rev 616)
+++
trunk/tests/src/net/sourceforge/stripes/controller/GenericsBindingTests2.java
2007-11-06 19:00:11 UTC (rev 617)
@@ -1,37 +1,129 @@
package net.sourceforge.stripes.controller;
+import java.util.Calendar;
+import java.util.Date;
+import net.sourceforge.stripes.StripesTestFixture;
+import net.sourceforge.stripes.action.ActionBean;
+import net.sourceforge.stripes.action.ActionBeanContext;
+import net.sourceforge.stripes.action.DefaultHandler;
+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;
/**
* Basically a mirror of GenericsBindingTests except that in this case the
type variable/
- * parameter information is pushed further up the hierarchy by the fact that
we just extend
- * GenericsBindingTests. So this test ensures that our type inference using
type variables
- * works when the information is not directly in this class, but further up
the hierarchy.
+ * parameter information is pushed further up the hierarchy. So this test
ensures that
+ * our type inference using type variables works when the information is not
directly in
+ * this class, but further up the hierarchy.
*
* @author Tim Fennell
*/
-public class GenericsBindingTests2 extends GenericsBindingTests {
- @Override
+class Class1<A,B,C,D,E> extends GenericsBindingTestsBaseClass<A,B,C,D,E> { }
+
+class Class2<D,E,B,A,C> extends Class1<D,E,B,A,C> { }
+
+class Class3<Y,W,Z,V,X> extends Class2<Y,W,Z,V,X> { }
+
+class Class4<Z,Y,X,W,V> extends Class3<Z,Y,X,W,V> { }
+
+public class GenericsBindingTests2
+ extends Class4<TestBean,Double,Boolean,Long,Date>
+ implements ActionBean {
+
+ // Stuff necessary to implement ActionBean!
+ private ActionBeanContext context;
+ public ActionBeanContext getContext() { return context; }
+ public void setContext(ActionBeanContext context) { this.context =
context; }
+ @DefaultHandler public Resolution execute() { return new
RedirectResolution("/somewhere.jsp"); }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Test and Support Methods
+ ///////////////////////////////////////////////////////////////////////////
+
+ /** Makes a roundtrip using the current instances' type. */
+ protected MockRoundtrip getRoundtrip() {
+ return new MockRoundtrip(StripesTestFixture.getServletContext(),
GenericsBindingTests2.class);
+ }
+
@Test(groups="fast")
public void testSimpleTypeVariable() throws Exception {
- super.testSimpleTypeVariable();
+ MockRoundtrip trip = getRoundtrip();
+ trip.addParameter("number", "123.4");
+ trip.execute();
+
+ GenericsBindingTests2 bean =
trip.getActionBean(GenericsBindingTests2.class);
+ Assert.assertNotNull(bean.getNumber());
+ Assert.assertEquals(bean.getNumber(), new Double(123.4));
}
- @Override
@Test(groups="fast")
+ public void testGenericBean() throws Exception {
+ MockRoundtrip trip = getRoundtrip();
+ trip.addParameter("genericBean.genericA", "123.4");
+ trip.addParameter("genericBean.genericB", "true");
+ trip.execute();
+
+ GenericsBindingTests2 bean =
trip.getActionBean(GenericsBindingTests2.class);
+ Assert.assertNotNull(bean.getGenericBean().getGenericA());
+ Assert.assertEquals(bean.getGenericBean().getGenericA(), new
Double(123.4));
+ Assert.assertNotNull(bean.getGenericBean().getGenericB());
+ Assert.assertEquals(bean.getGenericBean().getGenericB(), Boolean.TRUE);
+ }
+
+ @Test(groups="fast")
public void testTypeVariableLists() throws Exception {
- super.testTypeVariableLists();
+ MockRoundtrip trip = getRoundtrip();
+ trip.addParameter("list[0]", "true");
+ trip.addParameter("list[1]", "false");
+ trip.addParameter("list[2]", "yes");
+ trip.execute();
+
+ GenericsBindingTests2 bean =
trip.getActionBean(GenericsBindingTests2.class);
+ Assert.assertNotNull(bean.getList());
+ Assert.assertEquals(bean.getList().get(0), Boolean.TRUE);
+ Assert.assertEquals(bean.getList().get(1), Boolean.FALSE);
+ Assert.assertEquals(bean.getList().get(2), Boolean.TRUE);
}
- @Override
@Test(groups="fast")
public void testTypeVariableMaps() throws Exception {
- super.testTypeVariableMaps();
+ MockRoundtrip trip = getRoundtrip();
+ trip.addParameter("map[10]", "1/1/2010");
+ trip.addParameter("map[20]", "1/1/2020");
+ trip.addParameter("map[30]", "1/1/2030");
+ trip.execute();
+
+ GenericsBindingTests2 bean =
trip.getActionBean(GenericsBindingTests2.class);
+ Assert.assertNotNull(bean.getMap());
+ Assert.assertEquals(bean.getMap().get(10l), makeDate(2010,1,1));
+ Assert.assertEquals(bean.getMap().get(20l), makeDate(2020,1,1));
+ Assert.assertEquals(bean.getMap().get(30l), makeDate(2030,1,1));
}
- @Override
@Test(groups="fast")
public void testTypeVariableNestedProperties() throws Exception {
- super.testTypeVariableNestedProperties();
+ MockRoundtrip trip = getRoundtrip();
+ trip.addParameter("bean.longProperty", "1234");
+ trip.addParameter("bean.stringProperty", "foobar");
+ trip.execute();
+
+ GenericsBindingTests2 bean =
trip.getActionBean(GenericsBindingTests2.class);
+ 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.
+ */
+ private Date makeDate(int year, int month, int day) {
+ Calendar cal = Calendar.getInstance();
+ cal.clear();
+ cal.set(year, month-1, day);
+ return cal.getTime();
+ }
}
Modified:
trunk/tests/src/net/sourceforge/stripes/controller/GenericsBindingTestsBaseClass.java
===================================================================
---
trunk/tests/src/net/sourceforge/stripes/controller/GenericsBindingTestsBaseClass.java
2007-10-11 02:10:42 UTC (rev 616)
+++
trunk/tests/src/net/sourceforge/stripes/controller/GenericsBindingTestsBaseClass.java
2007-11-06 19:00:11 UTC (rev 617)
@@ -2,16 +2,18 @@
import java.util.List;
import java.util.Map;
+import net.sourceforge.stripes.test.TestGenericBean;
/**
* A simple base class that is littered with Type parameters at the class
level. Contains
* no tests in and of itself, but it is necessary to be a public class in
order for
* [EMAIL PROTECTED] GenericsBindingTests} to extend it and have the methods
be accessible.
*
- * @author Tim Fennell
+ * @author Tim Fennell
*/
public class GenericsBindingTestsBaseClass<JB,N,E,K,V> {
JB bean;
+ TestGenericBean<N,E> genericBean;
N number;
List<? extends E> list;
Map<K,V> map;
@@ -19,6 +21,9 @@
public JB getBean() { return bean; }
public void setBean(JB bean) { this.bean = bean; }
+ public TestGenericBean<N,E> getGenericBean() { return genericBean; }
+ public void setGenericBean(TestGenericBean<N,E> genericBean) {
this.genericBean = genericBean; }
+
public N getNumber() { return number; }
public void setNumber(N number) { this.number = number; }
Added: trunk/tests/src/net/sourceforge/stripes/test/TestGenericBean.java
===================================================================
--- trunk/tests/src/net/sourceforge/stripes/test/TestGenericBean.java
(rev 0)
+++ trunk/tests/src/net/sourceforge/stripes/test/TestGenericBean.java
2007-11-06 19:00:11 UTC (rev 617)
@@ -0,0 +1,40 @@
+package net.sourceforge.stripes.test;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A JavaBean that is a generic type.
+ *
+ * @author Alan Burlison
+ *
+ * XXX If this class is not the top level in the inheritance hierarchy,
+ * Stripes cannot bind its properties. e.g. if instead of:
+ * public class TestGenericBean<A,B> { ... }
+ * we have
+ * class Class1<A,B> { ... }
+ * class Class2<B,A> extends Class1<B,A> {}
+ * class Class3<X,Y> extends Class2<X,Y> {}
+ * public class TestGenericBean<A,B> extends Class3<A,B> {}
+ * Stripes will fail to bind the properties of Class1 correctly. See STS-XXX
+ */
+public class TestGenericBean<A,B> {
+ private A genericA;
+ private B genericB;
+
+ public A getGenericA() {
+ return genericA;
+ }
+ public void setGenericA(A genericA) {
+ this.genericA = genericA;
+ }
+
+ public B getGenericB() {
+ return genericB;
+ }
+
+ public void setGenericB(B genericB) {
+ this.genericB = genericB;
+ }
+}
\ No newline at end of file
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems? Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >> http://get.splunk.com/
_______________________________________________
Stripes-development mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/stripes-development