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