You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2008/01/25 03:12:40 UTC
svn commit: r615099 - in /tapestry/tapestry5/trunk:
tapestry-core/src/main/java/org/apache/tapestry/internal/services/
tapestry-core/src/test/java/org/apache/tapestry/integration/
tapestry-core/src/test/java/org/apache/tapestry/integration/app1/base/ t...
Author: hlship
Date: Thu Jan 24 18:12:31 2008
New Revision: 615099
URL: http://svn.apache.org/viewvc?rev=615099&view=rev
Log:
TAPESTRY-1518: Add support for JDK 1.5 Generics when defining pages and accessing bean properties
Added:
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/base/GenericEditor.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/TrackEditor.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/GenericBean.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/StringHolder.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/StringHolderBean.java
tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app1/base/
tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app1/base/GenericEditor.tml
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/util/GenericsUtils.java
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/BaseGenericBean.java
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/GenericUtilsTest.java
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/NonGenericBean.java
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/Pair.java
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/StringBean.java
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/StringLongPair.java
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PropertyConduitSourceImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/Start.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PropertyConduitSourceImplTest.java
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/ClassPropertyAdapterImpl.java
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/PropertyAdapterImpl.java
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/services/PropertyAdapter.java
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/services/PropertyAccessImplTest.java
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PropertyConduitSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PropertyConduitSourceImpl.java?rev=615099&r1=615098&r2=615099&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PropertyConduitSourceImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PropertyConduitSourceImpl.java Thu Jan 24 18:12:31 2008
@@ -1,4 +1,4 @@
-// Copyright 2007 The Apache Software Foundation
+// Copyright 2007, 2008 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newConcurrentMap;
import static org.apache.tapestry.ioc.internal.util.Defense.notBlank;
import static org.apache.tapestry.ioc.internal.util.Defense.notNull;
+import org.apache.tapestry.ioc.internal.util.GenericsUtils;
import org.apache.tapestry.ioc.services.*;
import org.apache.tapestry.ioc.util.BodyBuilder;
import org.apache.tapestry.services.ComponentLayer;
@@ -30,9 +31,51 @@
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
+import java.util.regex.Pattern;
public class PropertyConduitSourceImpl implements PropertyConduitSource, InvalidationListener
{
+ private interface ReadInfo extends AnnotationProvider
+ {
+ /**
+ * The name of the method to invoke.
+ */
+ String getMethodName();
+
+ /**
+ * The return type of the method, or the type of the property.
+ */
+ Class getType();
+
+ /**
+ * True if an explicit cast to the return type is needed (typically because of generics).
+ */
+ boolean isCastRequired();
+ }
+
+
+ /**
+ * Result from writing the property navigation portion of the expression. For getter methods, the navigation is all
+ * terms in the expression; for setter methods, the navigation is all but the last term.
+ */
+ private interface PropertyNavigationResult
+ {
+ /**
+ * The name of the variable holding the final step in the expression.
+ */
+ String getFinalStepVariable();
+
+ /**
+ * The type of the final step variable.
+ */
+ Class getFinalStepType();
+
+ /**
+ * The method read information for the final term in the navigation portion of the expression.
+ */
+ ReadInfo getFinalReadInfo();
+ }
+
private static final String PARENS = "()";
private final PropertyAccess _access;
@@ -53,6 +96,8 @@
new Class[]{Object.class, Object.class},
null);
+ private final Pattern SPLIT_AT_DOTS = Pattern.compile("\\.");
+
public PropertyConduitSourceImpl(PropertyAccess access, @ComponentLayer ClassFactory classFactory)
{
_access = access;
@@ -95,9 +140,9 @@
}
/**
- * Clears its cache when the component class loader is invalidated; this is because it will be
- * common to generated conduits rooted in a component class (which will no longer be valid and
- * must be released to the garbage collector).
+ * Clears its caches when the component class loader is invalidated; this is because it will be common to generate
+ * conduits rooted in a component class (which will no longer be valid and must be released to the garbage
+ * collector).
*/
public void objectWasInvalidated()
{
@@ -105,11 +150,11 @@
_classToEffectiveClass.clear();
}
+
/**
- * Builds a subclass of {@link BasePropertyConduit} that implements the get() and set() methods
- * and overrides the constructor. In a worst-case race condition, we may build two (or more)
- * conduits for the same rootClass/expression, and it will get sorted out when the conduit is
- * stored into the cache.
+ * Builds a subclass of {@link BasePropertyConduit} that implements the get() and set() methods and overrides the
+ * constructor. In a worst-case race condition, we may build two (or more) conduits for the same
+ * rootClass/expression, and it will get sorted out when the conduit is stored into the cache.
*
* @param rootClass
* @param expression
@@ -123,15 +168,15 @@
classFab.addConstructor(new Class[]{Class.class, AnnotationProvider.class, String.class}, null, "super($$);");
- String[] terms = expression.split("\\.");
+ String[] terms = SPLIT_AT_DOTS.split(expression);
- final Method readMethod = buildGetter(rootClass, classFab, expression, terms);
+ final ReadInfo readInfo = buildGetter(rootClass, classFab, expression, terms);
final Method writeMethod = buildSetter(rootClass, classFab, expression, terms);
// A conduit is either readable or writable, otherwise there will already have been
// an error about unknown method name or property name.
- Class propertyType = readMethod != null ? readMethod.getReturnType() : writeMethod
+ Class propertyType = readInfo != null ? readInfo.getType() : writeMethod
.getParameterTypes()[0];
String description = String.format("PropertyConduit[%s %s]", rootClass.getName(), expression);
@@ -140,10 +185,9 @@
AnnotationProvider provider = new AnnotationProvider()
{
-
public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
{
- T result = readMethod == null ? null : readMethod.getAnnotation(annotationClass);
+ T result = readInfo == null ? null : readInfo.getAnnotation(annotationClass);
if (result == null && writeMethod != null) result = writeMethod.getAnnotation(annotationClass);
@@ -163,19 +207,60 @@
}
- private Method buildGetter(Class rootClass, ClassFab classFab, String expression, String[] terms)
+ private ReadInfo buildGetter(Class rootClass, ClassFab classFab, String expression, String[] terms)
{
BodyBuilder builder = new BodyBuilder();
+
builder.begin();
+ PropertyNavigationResult result = writePropertyNavigationCode(builder, rootClass, expression, terms, false);
+
+
+ if (result == null)
+ {
+ builder.clear();
+ builder
+ .addln("throw new RuntimeException(\"Expression %s for class %s is write-only.\");", expression,
+ rootClass.getName());
+ }
+ else
+ {
+ builder.addln("return %s;", result.getFinalStepVariable());
+
+ builder.end();
+ }
+
+ classFab.addMethod(Modifier.PUBLIC, GET_SIGNATURE, builder.toString());
+
+
+ return result == null ? null : result.getFinalReadInfo();
+ }
+
+ /**
+ * Writes the code for navigation
+ *
+ * @param builder
+ * @param rootClass
+ * @param expression
+ * @param terms
+ * @param forSetter if true, then the last term is not read since it will be updated
+ * @return
+ */
+ private PropertyNavigationResult writePropertyNavigationCode(BodyBuilder builder, Class rootClass,
+ String expression, String[] terms, boolean forSetter)
+ {
builder.addln("%s root = (%<s) $1;", ClassFabUtils.toJavaClassName(rootClass));
String previousStep = "root";
Class activeType = rootClass;
- Method result = null;
- boolean writeOnly = false;
+ ReadInfo readInfo = null;
+
+ // For a setter method, the navigation stops with the penultimate
+ // term in the expression (the final term is what gets updated).
- for (int i = 0; i < terms.length; i++)
+ int lastIndex = forSetter ? terms.length - 1 : terms.length;
+
+ for (int i = 0; i < lastIndex; i++)
{
String thisStep = "step" + (i + 1);
String term = terms[i];
@@ -183,45 +268,75 @@
boolean nullable = term.endsWith("?");
if (nullable) term = term.substring(0, term.length() - 1);
- Method readMethod = readMethodForTerm(activeType, expression, term, (i < terms.length - 1));
+ // All the navigation terms in the expression must be readable properties.
+ // The only exception is the final term in a reader method.
- if (readMethod == null)
- {
- writeOnly = true;
- break;
- }
+ boolean mustExist = forSetter || i < terms.length - 1;
+
+ readInfo = readInfoForTerm(activeType, expression, term, mustExist);
+
+ // Means the property for this step exists but is write only, which is a problem!
+ // This can only happen for getter methods, we return null to indicate that
+ // the expression is write-only.
+
+ if (readInfo == null) return null;
// If a primitive type, convert to wrapper type
- Class termType = ClassFabUtils.getWrapperType(readMethod.getReturnType());
+ Class termType = readInfo.getType();
+ Class wrappedType = ClassFabUtils.getWrapperType(termType);
+
+ String termJavaName = ClassFabUtils.toJavaClassName(wrappedType);
+ builder.add("%s %s = ", termJavaName, thisStep);
- // $w is harmless for non-wrapper types.
+ // Casts are needed for primitives, and for the case where
+ // generics are involved.
- builder.addln("%s %s = ($w) %s.%s();", ClassFabUtils.toJavaClassName(termType), thisStep, previousStep,
- readMethod.getName());
+ if (termType.isPrimitive())
+ {
+ builder.add(" ($w) ");
+ }
+ else if (readInfo.isCastRequired())
+ {
+ builder.add(" (%s) ", termJavaName);
+ }
+
+ builder.addln("%s.%s();", previousStep, readInfo.getMethodName());
+
+ if (nullable)
+ {
+ builder.add("if (%s == null) return", thisStep);
- if (nullable) builder.addln("if (%s == null) return null;", thisStep);
+ if (!forSetter) builder.add(" null");
- activeType = termType;
- result = readMethod;
+ builder.addln(";");
+ }
+
+ activeType = wrappedType;
previousStep = thisStep;
}
- builder.addln("return %s;", previousStep);
-
- builder.end();
+ final String finalStepVariable = previousStep;
+ final Class finalStepType = activeType;
+ final ReadInfo finalReadInfo = readInfo;
- if (writeOnly)
+ return new PropertyNavigationResult()
{
- builder.clear();
- builder
- .addln("throw new java.lang.RuntimeException(\"Expression %s for class %s is write-only.\");",
- expression, rootClass.getName());
- }
+ public String getFinalStepVariable()
+ {
+ return finalStepVariable;
+ }
- classFab.addMethod(Modifier.PUBLIC, GET_SIGNATURE, builder.toString());
+ public Class getFinalStepType()
+ {
+ return finalStepType;
+ }
- return result;
+ public ReadInfo getFinalReadInfo()
+ {
+ return finalReadInfo;
+ }
+ };
}
private Method buildSetter(Class rootClass, ClassFab classFab, String expression, String[] terms)
@@ -229,46 +344,19 @@
BodyBuilder builder = new BodyBuilder();
builder.begin();
- builder.addln("%s root = (%<s) $1;", ClassFabUtils.toJavaClassName(rootClass));
- String previousStep = "root";
+ PropertyNavigationResult result = writePropertyNavigationCode(builder, rootClass, expression, terms, true);
- Class activeType = rootClass;
-
- for (int i = 0; i < terms.length - 1; i++)
- {
- String thisStep = "step" + (i + 1);
- String term = terms[i];
-
- boolean nullable = term.endsWith("?");
- if (nullable) term = term.substring(0, term.length() - 1);
-
- Method readMethod = readMethodForTerm(activeType, expression, term, true);
-
- // If a primitive type, convert to wrapper type
-
- Class termType = ClassFabUtils.getWrapperType(readMethod.getReturnType());
-
- // $w is harmless for non-wrapper types.
-
- builder.addln("%s %s = ($w) %s.%s();", ClassFabUtils.toJavaClassName(termType), thisStep, previousStep,
- readMethod.getName());
-
- if (nullable) builder.addln("if (%s == null) return;", thisStep);
-
- activeType = termType;
- previousStep = thisStep;
- }
+ // Because we pass true for the forSetter parameter, we know that the expression for the leading
+ // terms is a chain of readable expressions. But is the final term writable?
- // When writing, the last step is different.
-
- Method writeMethod = writeMethodForTerm(activeType, expression, terms[terms.length - 1]);
+ Method writeMethod = writeMethodForTerm(result.getFinalStepType(), expression, terms[terms.length - 1]);
if (writeMethod == null)
{
builder.clear();
builder
- .addln("throw new java.lang.RuntimeException(\"Expression %s for class %s is read-only.\");",
- expression, rootClass.getName());
+ .addln("throw new RuntimeException(\"Expression %s for class %s is read-only.\");", expression,
+ rootClass.getName());
classFab.addMethod(Modifier.PUBLIC, SET_SIGNATURE, builder.toString());
return null;
@@ -278,9 +366,13 @@
Class wrapperType = ClassFabUtils.getWrapperType(parameterType);
+ // Cast the parameter from Object to the expected type for the method.
+
builder.addln("%s value = (%<s) $2;", ClassFabUtils.toJavaClassName(wrapperType));
- builder.add("%s.%s(value", previousStep, writeMethod.getName());
+ // Invoke the method, possibly converting a wrapper type to a primitive type along the way.
+
+ builder.add("%s.%s(value", result.getFinalStepVariable(), writeMethod.getName());
if (parameterType != wrapperType)
builder.add(".%s()", ClassFabUtils.getUnwrapMethodName(parameterType.getName()));
@@ -307,39 +399,89 @@
return adapter.getWriteMethod();
}
- private Method readMethodForTerm(Class activeType, String expression, String term, boolean mustExist)
+ private ReadInfo readInfoForTerm(Class activeType, String expression, String term, boolean mustExist)
{
if (term.endsWith(PARENS))
{
- Method method = null;
String methodName = term.substring(0, term.length() - PARENS.length());
try
{
- method = activeType.getMethod(methodName);
+ final Method method = activeType.getMethod(methodName);
+
+ if (method.getReturnType().equals(void.class))
+ throw new RuntimeException(ServicesMessages.methodIsVoid(term, activeType, expression));
+
+
+ final Class genericType = GenericsUtils.extractGenericReturnType(activeType, method);
+
+ return new ReadInfo()
+ {
+ public String getMethodName()
+ {
+ return method.getName();
+ }
+
+ public Class getType()
+ {
+ return genericType;
+ }
+
+ public boolean isCastRequired()
+ {
+ return genericType != method.getReturnType();
+ }
+
+ public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
+ {
+ return method.getAnnotation(annotationClass);
+ }
+ };
+
}
catch (NoSuchMethodException ex)
{
throw new RuntimeException(ServicesMessages.methodNotFound(term, activeType, expression), ex);
}
- if (method.getReturnType().equals(void.class))
- throw new RuntimeException(ServicesMessages.methodIsVoid(term, activeType, expression));
-
- return method;
}
+ // Otherwise, just a property name.
+
ClassPropertyAdapter classAdapter = _access.getAdapter(activeType);
- PropertyAdapter adapter = classAdapter.getPropertyAdapter(term);
+ final PropertyAdapter adapter = classAdapter.getPropertyAdapter(term);
if (adapter == null) throw new RuntimeException(
ServicesMessages.noSuchProperty(activeType, term, expression, classAdapter.getPropertyNames()));
- Method m = adapter.getReadMethod();
+ if (!adapter.isRead())
+ {
+ if (mustExist) throw new RuntimeException(ServicesMessages.writeOnlyProperty(term, activeType, expression));
+
+ return null;
+ }
+
+ return new ReadInfo()
+ {
+ public String getMethodName()
+ {
+ return adapter.getReadMethod().getName();
+ }
+
+ public Class getType()
+ {
+ return adapter.getType();
+ }
- if (m == null && mustExist)
- throw new RuntimeException(ServicesMessages.writeOnlyProperty(term, activeType, expression));
+ public boolean isCastRequired()
+ {
+ return adapter.isCastRequired();
+ }
- return m;
+ public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
+ {
+ return adapter.getAnnotation(annotationClass);
+ }
+ };
}
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java?rev=615099&r1=615098&r2=615099&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java Thu Jan 24 18:12:31 2008
@@ -1498,4 +1498,17 @@
// The rendered becomes just a blank string.
assertTextSeries("//tr[1]/td[%d]", 1, "7", "view", "1", "");
}
+
+ /**
+ * TAPESTRY-1518
+ */
+ @Test
+ public void generic_page_type()
+ {
+ start("Generic Page Class Demo");
+
+ assertTextPresent("Editor for org.apache.tapestry.integration.app1.data.Track");
+
+ assertSourcePresent("<label for=\"title\" id=\"title:label\">Title</label>");
+ }
}
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/base/GenericEditor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/base/GenericEditor.java?rev=615099&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/base/GenericEditor.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/base/GenericEditor.java Thu Jan 24 18:12:31 2008
@@ -0,0 +1,65 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// Licensed 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.tapestry.integration.app1.base;
+
+import org.apache.tapestry.PropertyConduit;
+import org.apache.tapestry.annotations.Component;
+import org.apache.tapestry.annotations.Persist;
+import org.apache.tapestry.annotations.Retain;
+import org.apache.tapestry.corelib.components.BeanEditForm;
+import org.apache.tapestry.ioc.annotations.Inject;
+import org.apache.tapestry.services.PropertyConduitSource;
+
+/**
+ * For testing TAPESTRY-1518.
+ */
+public class GenericEditor<T>
+{
+ @Persist
+ private T _bean;
+
+ @Component(parameters = {"object=bean"})
+ private BeanEditForm _form;
+
+ @Inject
+ private PropertyConduitSource _conduit;
+
+ @Retain
+ private String _beanType;
+
+ {
+ // Use getClass(), not GenericEditor.class, to determine the correct type for the bean.
+ // Otherwise, it would be Object.
+
+ PropertyConduit conduit = _conduit.create(getClass(), "bean");
+
+ _beanType = conduit.getPropertyType().getName();
+ }
+
+ public String getBeanType()
+ {
+ return _beanType;
+ }
+
+ public T getBean()
+ {
+ return _bean;
+ }
+
+ public void setBean(T bean)
+ {
+ _bean = bean;
+ }
+}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/Start.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/Start.java?rev=615099&r1=615098&r2=615099&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/Start.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/Start.java Thu Jan 24 18:12:31 2008
@@ -208,7 +208,10 @@
new Item("ExceptionEventDemo", "Exception Event Demo", "handling component event exceptions"),
- new Item("AddedGridColumnsDemo", "Added Grid Columns Demo", "programatically adding grid columns"));
+ new Item("AddedGridColumnsDemo", "Added Grid Columns Demo", "programatically adding grid columns"),
+
+ new Item("TrackEditor", "Generic Page Class Demo",
+ "demo use of generics with component classes and, particularily, with property types"));
static
{
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/TrackEditor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/TrackEditor.java?rev=615099&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/TrackEditor.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/TrackEditor.java Thu Jan 24 18:12:31 2008
@@ -0,0 +1,22 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// Licensed 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.tapestry.integration.app1.pages;
+
+import org.apache.tapestry.integration.app1.base.GenericEditor;
+import org.apache.tapestry.integration.app1.data.Track;
+
+public class TrackEditor extends GenericEditor<Track>
+{
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/GenericBean.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/GenericBean.java?rev=615099&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/GenericBean.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/GenericBean.java Thu Jan 24 18:12:31 2008
@@ -0,0 +1,30 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// Licensed 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.tapestry.internal.services;
+
+public class GenericBean<T>
+{
+ private T _value;
+
+ public T getValue()
+ {
+ return _value;
+ }
+
+ public void setValue(T value)
+ {
+ _value = value;
+ }
+}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PropertyConduitSourceImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PropertyConduitSourceImplTest.java?rev=615099&r1=615098&r2=615099&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PropertyConduitSourceImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PropertyConduitSourceImplTest.java Thu Jan 24 18:12:31 2008
@@ -83,8 +83,8 @@
}
/**
- * Or call this the "Hibernate" case; Hibernate creates sub-classes of entity classes in its own
- * class loader to do all sorts of proxying. This trips up Javassist.
+ * Or call this the "Hibernate" case; Hibernate creates sub-classes of entity classes in its own class loader to do
+ * all sorts of proxying. This trips up Javassist.
*/
@Test
public void handle_beans_from_unexpected_classloader() throws Exception
@@ -110,6 +110,22 @@
PropertyConduit conduit = _source.create(proxyClass, "firstName");
assertEquals(conduit.get(simple), "Howard");
+ }
+
+ @Test
+ public void generics()
+ {
+ String string = "surprise";
+ StringHolder stringHolder = new StringHolder();
+ stringHolder.put(string);
+ StringHolderBean bean = new StringHolderBean();
+ bean.setValue(stringHolder);
+
+ PropertyConduit conduit = _source.create(StringHolderBean.class, "value.get()");
+
+ assertSame(conduit.get(bean), string);
+
+ assertSame(conduit.getPropertyType(), String.class);
}
}
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/StringHolder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/StringHolder.java?rev=615099&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/StringHolder.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/StringHolder.java Thu Jan 24 18:12:31 2008
@@ -0,0 +1,21 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// Licensed 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.tapestry.internal.services;
+
+import org.apache.tapestry.internal.util.Holder;
+
+public class StringHolder extends Holder<String>
+{
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/StringHolderBean.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/StringHolderBean.java?rev=615099&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/StringHolderBean.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/StringHolderBean.java Thu Jan 24 18:12:31 2008
@@ -0,0 +1,19 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// Licensed 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.tapestry.internal.services;
+
+public class StringHolderBean extends GenericBean<StringHolder>
+{
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app1/base/GenericEditor.tml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app1/base/GenericEditor.tml?rev=615099&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app1/base/GenericEditor.tml (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app1/base/GenericEditor.tml Thu Jan 24 18:12:31 2008
@@ -0,0 +1,7 @@
+<t:border xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+
+ <h1>Editor for ${beanType}</h1>
+
+ <form t:id="form"/>
+
+</t:border>
Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/ClassPropertyAdapterImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/ClassPropertyAdapterImpl.java?rev=615099&r1=615098&r2=615099&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/ClassPropertyAdapterImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/ClassPropertyAdapterImpl.java Thu Jan 24 18:12:31 2008
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007 The Apache Software Foundation
+// Copyright 2006, 2007, 2008 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,11 +15,13 @@
package org.apache.tapestry.ioc.internal.services;
import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newCaseInsensitiveMap;
+import org.apache.tapestry.ioc.internal.util.GenericsUtils;
import org.apache.tapestry.ioc.internal.util.InternalUtils;
import org.apache.tapestry.ioc.services.ClassPropertyAdapter;
import org.apache.tapestry.ioc.services.PropertyAdapter;
import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
@@ -40,7 +42,12 @@
if (pd.getPropertyType() == null) continue;
- PropertyAdapter pa = new PropertyAdapterImpl(pd);
+ Method readMethod = pd.getReadMethod();
+
+ Class propertyType = readMethod == null ? pd.getPropertyType() : GenericsUtils.extractGenericReturnType(
+ beanType, readMethod);
+
+ PropertyAdapter pa = new PropertyAdapterImpl(pd.getName(), propertyType, readMethod, pd.getWriteMethod());
_adapters.put(pa.getName(), pa);
}
@@ -83,8 +90,7 @@
{
PropertyAdapter pa = _adapters.get(name);
- if (pa == null)
- throw new IllegalArgumentException(ServiceMessages.noSuchProperty(_beanType, name));
+ if (pa == null) throw new IllegalArgumentException(ServiceMessages.noSuchProperty(_beanType, name));
return pa;
}
Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/PropertyAdapterImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/PropertyAdapterImpl.java?rev=615099&r1=615098&r2=615099&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/PropertyAdapterImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/PropertyAdapterImpl.java Thu Jan 24 18:12:31 2008
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2008 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@
import static org.apache.tapestry.ioc.internal.util.Defense.notNull;
import org.apache.tapestry.ioc.services.PropertyAdapter;
-import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -33,6 +32,8 @@
private final Class _type;
+ private final boolean _castRequired;
+
public PropertyAdapterImpl(String name, Class type, Method readMethod, Method writeMethod)
{
_name = notBlank(name, "name");
@@ -40,12 +41,8 @@
_readMethod = readMethod;
_writeMethod = writeMethod;
- }
- public PropertyAdapterImpl(PropertyDescriptor descriptor)
- {
- this(descriptor.getName(), descriptor.getPropertyType(), descriptor.getReadMethod(),
- descriptor.getWriteMethod());
+ _castRequired = readMethod != null && readMethod.getReturnType() != type;
}
public String getName()
@@ -133,5 +130,10 @@
if (result == null && _writeMethod != null) result = _writeMethod.getAnnotation(annotationClass);
return result;
+ }
+
+ public boolean isCastRequired()
+ {
+ return _castRequired;
}
}
Added: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/util/GenericsUtils.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/util/GenericsUtils.java?rev=615099&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/util/GenericsUtils.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/util/GenericsUtils.java Thu Jan 24 18:12:31 2008
@@ -0,0 +1,91 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// Licensed 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.tapestry.ioc.internal.util;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+
+/**
+ * Static methods related to the use of JDK 1.5 generics.
+ */
+public class GenericsUtils
+{
+ /**
+ * Analyzes the method (often defined in a base class) in the context of a particular concrete implementation of the
+ * class to establish the generic type of a property. This works when the property type is defined as a class
+ * generic parameter.
+ *
+ * @param type base type for evaluation
+ * @param method method (possibly from a base class of type) to extract
+ * @return the generic type if it may be determined, or the raw type (that is, with type erasure, most often
+ * Object)
+ */
+ public static Class extractGenericReturnType(Class type, Method method)
+ {
+ Class defaultType = method.getReturnType();
+
+ Type genericType = method.getGenericReturnType();
+
+ // We can only handle the case where you "lock down" a generic type to a specific type.
+
+ if (genericType instanceof TypeVariable)
+ {
+
+ // An odd name for the method that gives you access to the type parameters
+ // used when implementing this class. When you say Bean<String>, the first
+ // type variable of the generic superclass is class String.
+
+ Type superType = type.getGenericSuperclass();
+
+ if (superType instanceof ParameterizedType)
+ {
+ ParameterizedType superPType = (ParameterizedType) superType;
+
+ TypeVariable tv = (TypeVariable) genericType;
+
+ String name = tv.getName();
+
+ TypeVariable[] typeVariables = tv.getGenericDeclaration().getTypeParameters();
+
+ for (int i = 0; i < typeVariables.length; i++)
+ {
+ TypeVariable stv = typeVariables[i];
+
+ // We're trying to match the name of the type variable that is used as the return type
+ // of the method. With that name, we find the corresponding index in the
+ // type declarations. With the index, we check superPType for the Class instance
+ // that defines it. Generics has lots of other options that we simply can't handle.
+
+ if (stv.getName().equals(name))
+ {
+ Type actualType = superPType.getActualTypeArguments()[i];
+
+ if (actualType instanceof Class) return (Class) actualType;
+
+ break;
+ }
+ }
+
+ }
+ }
+
+
+ return defaultType;
+
+ // P.S. I wrote this and I barely understand it. Fortunately, I have tests ...
+ }
+}
Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/services/PropertyAdapter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/services/PropertyAdapter.java?rev=615099&r1=615098&r2=615099&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/services/PropertyAdapter.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/services/PropertyAdapter.java Thu Jan 24 18:12:31 2008
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2008 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -19,8 +19,8 @@
import java.lang.reflect.Method;
/**
- * Provides access to a single property within a class. Acts as an {@link AnnotationProvider}; when searching
- * for annotations, the read method (if present) is checked first, followed by the write method.
+ * Provides access to a single property within a class. Acts as an {@link AnnotationProvider}; when searching for
+ * annotations, the read method (if present) is checked first, followed by the write method.
*
* @see org.apache.tapestry.ioc.services.ClassPropertyAdapter
*/
@@ -60,8 +60,8 @@
Object get(Object instance);
/**
- * Updates the property value. The provided value must not be null if the property type is
- * primitive, and must otherwise be of the proper type.
+ * Updates the property value. The provided value must not be null if the property type is primitive, and must
+ * otherwise be of the proper type.
*
* @param instance to update
* @param value new value for the property
@@ -73,4 +73,11 @@
* Returns the type of the property.
*/
Class getType();
+
+ /**
+ * Returns true if the return type of the read method is not the same as the property type. This can occur when the
+ * property has been defined using generics, in which case, the method's type may be Object when the property type
+ * is something more specific. This method is primarily used when generating runtime code related to the property.
+ */
+ boolean isCastRequired();
}
Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/services/PropertyAccessImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/services/PropertyAccessImplTest.java?rev=615099&r1=615098&r2=615099&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/services/PropertyAccessImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/services/PropertyAccessImplTest.java Thu Jan 24 18:12:31 2008
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007 The Apache Software Foundation
+// Copyright 2006, 2007, 2008 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -17,6 +17,8 @@
import org.apache.tapestry.ioc.Registry;
import org.apache.tapestry.ioc.annotations.Scope;
import org.apache.tapestry.ioc.internal.IOCInternalTestCase;
+import org.apache.tapestry.ioc.internal.util.Pair;
+import org.apache.tapestry.ioc.internal.util.StringLongPair;
import org.apache.tapestry.ioc.services.ClassPropertyAdapter;
import org.apache.tapestry.ioc.services.PropertyAccess;
import org.apache.tapestry.ioc.services.PropertyAdapter;
@@ -332,6 +334,7 @@
assertTrue(pa.isRead());
assertFalse(pa.isUpdate());
+ assertFalse(pa.isCastRequired());
assertNull(pa.getWriteMethod());
assertEquals(pa.getReadMethod(), findMethod(Bean.class, "getReadOnly"));
@@ -447,5 +450,33 @@
PropertyAdapter pa = _access.getAdapter(AnnotatedBean.class).getPropertyAdapter("readOnly");
assertNull(pa.getAnnotation(Scope.class));
+ }
+
+ @Test
+ public void using_generics()
+ {
+ ClassPropertyAdapter cpa1 = _access.getAdapter(StringLongPair.class);
+
+ PropertyAdapter pa1 = cpa1.getPropertyAdapter("key");
+ assertSame(pa1.getType(), String.class);
+ assertTrue(pa1.isCastRequired());
+
+ PropertyAdapter pa2 = cpa1.getPropertyAdapter("value");
+ assertSame(pa2.getType(), Long.class);
+ assertTrue(pa2.isCastRequired());
+
+ // On the base class, which defines the generic parameter type variables,
+ // the properties just look like Object.
+
+ ClassPropertyAdapter cpa2 = _access.getAdapter(Pair.class);
+
+ pa1 = cpa2.getPropertyAdapter("key");
+ assertSame(pa1.getType(), Object.class);
+ assertFalse(pa1.isCastRequired());
+
+ pa2 = cpa2.getPropertyAdapter("value");
+ assertSame(pa2.getType(), Object.class);
+ assertFalse(pa2.isCastRequired());
+
}
}
Added: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/BaseGenericBean.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/BaseGenericBean.java?rev=615099&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/BaseGenericBean.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/BaseGenericBean.java Thu Jan 24 18:12:31 2008
@@ -0,0 +1,30 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// Licensed 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.tapestry.ioc.internal.util;
+
+public class BaseGenericBean<T>
+{
+ private T _value;
+
+ public T getValue()
+ {
+ return _value;
+ }
+
+ public void setValue(T value)
+ {
+ _value = value;
+ }
+}
Added: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/GenericUtilsTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/GenericUtilsTest.java?rev=615099&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/GenericUtilsTest.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/GenericUtilsTest.java Thu Jan 24 18:12:31 2008
@@ -0,0 +1,61 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// Licensed 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.tapestry.ioc.internal.util;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import java.lang.reflect.Method;
+
+public class GenericUtilsTest extends Assert
+{
+ protected Method find(Class clazz, String name)
+ {
+ for (Method m : clazz.getMethods())
+ {
+ if (m.getName().equalsIgnoreCase(name)) return m;
+ }
+
+ throw new IllegalArgumentException(
+ String.format("Could not locate a public method named '%s' in %s.", name, clazz));
+
+ }
+
+ @Test
+ public void generic_return_type_of_non_generic_type()
+ {
+ Method m = find(NonGenericBean.class, "getvalue");
+
+ assertSame(GenericsUtils.extractGenericReturnType(NonGenericBean.class, m), String.class);
+ }
+
+ @Test
+ public void generic_return_type_of_parameterized_bean()
+ {
+ Method m = find(StringBean.class, "getvalue");
+
+ assertSame(GenericsUtils.extractGenericReturnType(StringBean.class, m), String.class);
+ }
+
+ @Test
+ public void generic_bean_with_multiple_parameters()
+ {
+ Method getKey = find(StringLongPair.class, "getkey");
+ Method getValue = find(StringLongPair.class, "getvalue");
+
+ assertSame(GenericsUtils.extractGenericReturnType(StringLongPair.class, getKey), String.class);
+ assertSame(GenericsUtils.extractGenericReturnType(StringLongPair.class, getValue), Long.class);
+ }
+}
Added: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/NonGenericBean.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/NonGenericBean.java?rev=615099&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/NonGenericBean.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/NonGenericBean.java Thu Jan 24 18:12:31 2008
@@ -0,0 +1,30 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// Licensed 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.tapestry.ioc.internal.util;
+
+public class NonGenericBean
+{
+ private String _value;
+
+ public String getValue()
+ {
+ return _value;
+ }
+
+ public void setValue(String value)
+ {
+ _value = value;
+ }
+}
Added: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/Pair.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/Pair.java?rev=615099&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/Pair.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/Pair.java Thu Jan 24 18:12:31 2008
@@ -0,0 +1,42 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// Licensed 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.tapestry.ioc.internal.util;
+
+public class Pair<K, V>
+{
+ private K _key;
+
+ private V _value;
+
+ public K getKey()
+ {
+ return _key;
+ }
+
+ public void setKey(K key)
+ {
+ _key = key;
+ }
+
+ public V getValue()
+ {
+ return _value;
+ }
+
+ public void setValue(V value)
+ {
+ _value = value;
+ }
+}
Added: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/StringBean.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/StringBean.java?rev=615099&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/StringBean.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/StringBean.java Thu Jan 24 18:12:31 2008
@@ -0,0 +1,20 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// Licensed 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.tapestry.ioc.internal.util;
+
+public class StringBean extends BaseGenericBean<String>
+{
+
+}
Added: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/StringLongPair.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/StringLongPair.java?rev=615099&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/StringLongPair.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/util/StringLongPair.java Thu Jan 24 18:12:31 2008
@@ -0,0 +1,19 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// Licensed 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.tapestry.ioc.internal.util;
+
+public class StringLongPair extends Pair<String, Long>
+{
+}