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/06/19 03:55:08 UTC
svn commit: r669357 - in /tapestry/tapestry5/trunk:
tapestry-core/src/main/java/org/apache/tapestry5/internal/services/
tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/
tapestry-core/src/test/java/org/apache/tapestry5/interna...
Author: hlship
Date: Wed Jun 18 18:55:08 2008
New Revision: 669357
URL: http://svn.apache.org/viewvc?rev=669357&view=rev
Log:
TAPESTRY-2450: Unlike reflective access (via PropertyAdapter), PropertyConduit does not make field annotations visible
TAPESTRY-2404: PropertyConduitSource could build a shared method to "navigate" to the final property
Added:
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/AccessableObjectAnnotationProvider.java
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/AnnotationProviderChain.java
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImplTest.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SimpleBean.java
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAdapterImpl.java
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java?rev=669357&r1=669356&r2=669357&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java Wed Jun 18 18:55:08 2008
@@ -18,9 +18,8 @@
import org.apache.tapestry5.internal.events.InvalidationListener;
import org.apache.tapestry5.internal.util.MultiKey;
import org.apache.tapestry5.ioc.AnnotationProvider;
-import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newConcurrentMap;
-import static org.apache.tapestry5.ioc.internal.util.Defense.notBlank;
-import static org.apache.tapestry5.ioc.internal.util.Defense.notNull;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.ioc.internal.util.Defense;
import org.apache.tapestry5.ioc.internal.util.GenericsUtils;
import org.apache.tapestry5.ioc.services.*;
import org.apache.tapestry5.ioc.util.BodyBuilder;
@@ -35,12 +34,17 @@
public class PropertyConduitSourceImpl implements PropertyConduitSource, InvalidationListener
{
- private interface ReadInfo extends AnnotationProvider
+ private interface ExpressionTermInfo extends AnnotationProvider
{
/**
- * The name of the method to invoke.
+ * The name of the method to invoke to read the property value, or null.
*/
- String getMethodName();
+ String getReadMethodName();
+
+ /**
+ * The name of the method to invoke to write the property value, or null.
+ */
+ String getWriteMethodName();
/**
* The return type of the method, or the type of the property.
@@ -54,40 +58,18 @@
}
- /**
- * 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;
private final ClassFactory classFactory;
- private final Map<Class, Class> classToEffectiveClass = newConcurrentMap();
+ private final Map<Class, Class> classToEffectiveClass = CollectionFactory.newConcurrentMap();
/**
* Keyed on combination of root class and expression.
*/
- private final Map<MultiKey, PropertyConduit> cache = newConcurrentMap();
+ private final Map<MultiKey, PropertyConduit> cache = CollectionFactory.newConcurrentMap();
private static final MethodSignature GET_SIGNATURE = new MethodSignature(Object.class, "get",
new Class[] { Object.class }, null);
@@ -106,8 +88,8 @@
public PropertyConduit create(Class rootClass, String expression)
{
- notNull(rootClass, "rootClass");
- notBlank(expression, "expression");
+ Defense.notNull(rootClass, "rootClass");
+ Defense.notBlank(expression, "expression");
Class effectiveClass = toEffectiveClass(rootClass);
@@ -171,85 +153,123 @@
String[] terms = SPLIT_AT_DOTS.split(expression);
- final ReadInfo readInfo = buildGetter(rootClass, classFab, expression, terms);
- final Method writeMethod = buildSetter(rootClass, classFab, expression, terms);
+ MethodSignature navigate = createNavigationMethod(rootClass, classFab, expression, terms);
+
+ String lastTerm = terms[terms.length - 1];
- // A conduit is either readable or writable, otherwise there will already have been
- // an error about unknown method name or property name.
+ ExpressionTermInfo termInfo = infoForTerm(navigate.getReturnType(), expression, lastTerm);
- Class propertyType = readInfo != null ? readInfo.getType() : writeMethod
- .getParameterTypes()[0];
+ createAccessors(rootClass, expression, classFab, navigate, termInfo);
String description = String.format("PropertyConduit[%s %s]", rootClass.getName(), expression);
Class conduitClass = classFab.createClass();
- AnnotationProvider provider = new AnnotationProvider()
- {
- public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
- {
- T result = readInfo == null ? null : readInfo.getAnnotation(annotationClass);
-
- if (result == null && writeMethod != null) result = writeMethod.getAnnotation(annotationClass);
-
- return result;
- }
-
- };
-
try
{
- return (PropertyConduit) conduitClass.getConstructors()[0].newInstance(propertyType, provider, description);
+ return (PropertyConduit) conduitClass.getConstructors()[0].newInstance(termInfo.getType(), termInfo,
+ description);
}
catch (Exception ex)
{
throw new RuntimeException(ex);
}
+ }
+ private void createAccessors(Class rootClass, String expression, ClassFab classFab, MethodSignature navigateMethod,
+ ExpressionTermInfo termInfo)
+ {
+ createGetter(rootClass, expression, classFab, navigateMethod, termInfo);
+ createSetter(rootClass, expression, classFab, navigateMethod, termInfo);
}
- private ReadInfo buildGetter(Class rootClass, ClassFab classFab, String expression, String[] terms)
+ private void createSetter(Class rootClass, String expression, ClassFab classFab, MethodSignature navigateMethod,
+ ExpressionTermInfo termInfo)
{
- BodyBuilder builder = new BodyBuilder();
+ String methodName = termInfo.getWriteMethodName();
- builder.begin();
+ if (methodName == null)
+ {
+ createNoOp(classFab, SET_SIGNATURE, "Expression %s for class %s is read-only.", expression,
+ rootClass.getName());
+ return;
+ }
- PropertyNavigationResult result = writePropertyNavigationCode(builder, rootClass, expression, terms, false);
+ BodyBuilder builder = new BodyBuilder().begin();
+ builder.addln("%s target = %s($1);",
+ ClassFabUtils.toJavaClassName(navigateMethod.getReturnType()),
+ navigateMethod.getName());
- if (result == null)
+ // I.e. due to ?. operator
+
+ builder.addln("if (target == null) return;");
+
+ String propertyTypeName = ClassFabUtils.toJavaClassName(termInfo.getType());
+
+ builder.addln("target.%s(%s);", methodName, ClassFabUtils.castReference("$2", propertyTypeName));
+
+ builder.end();
+
+ classFab.addMethod(Modifier.PUBLIC, SET_SIGNATURE, builder.toString());
+ }
+
+ private void createGetter(Class rootClass, String expression, ClassFab classFab, MethodSignature navigateMethod,
+ ExpressionTermInfo termInfo)
+ {
+ String methodName = termInfo.getReadMethodName();
+
+ if (methodName == null)
{
- builder.clear();
- builder
- .addln("throw new RuntimeException(\"Expression %s for class %s is write-only.\");", expression,
- rootClass.getName());
+ createNoOp(classFab, GET_SIGNATURE, "Expression %s for class %s is write-only.", expression,
+ rootClass.getName());
+ return;
}
- else
- {
- builder.addln("return %s;", result.getFinalStepVariable());
- builder.end();
- }
+ BodyBuilder builder = new BodyBuilder().begin();
+
+ builder.addln("%s target = %s($1);", ClassFabUtils.toJavaClassName(navigateMethod.getReturnType()),
+ navigateMethod.getName());
+
+ // I.e. due to ?. operator
+
+ builder.addln("if (target == null) return null;");
+
+ builder.addln("return ($w) target.%s();", methodName);
+
+ builder.end();
classFab.addMethod(Modifier.PUBLIC, GET_SIGNATURE, builder.toString());
+ }
+
+
+ private void createNoOp(ClassFab classFab, MethodSignature signature, String format, Object... values)
+ {
+ String message = String.format(format, values);
+ String body = String.format("throw new RuntimeException(\"%s\");", message);
- return result == null ? null : result.getFinalReadInfo();
+ classFab.addMethod(Modifier.PUBLIC, signature, body);
}
+
/**
- * Writes the code for navigation
+ * Builds a method that navigates from the root object upto, but not including, the final property. For simple
+ * properties, the generated method is effectively a big cast. Otherwise, the generated method returns the object
+ * that contains the final property (the final term). The generated method may return null if an intermediate
+ * term is null (and evaluated using the "?." safe dereferencing operator).
*
- * @param builder
* @param rootClass
+ * @param classFab
* @param expression
- * @param terms
- * @param forSetter if true, then the last term is not read since it will be updated
- * @return
+ * @param terms the expression divided into individual terms
+ * @return signature of the added method
*/
- private PropertyNavigationResult writePropertyNavigationCode(BodyBuilder builder, Class rootClass,
- String expression, String[] terms, boolean forSetter)
+ private MethodSignature createNavigationMethod(Class rootClass, ClassFab classFab, String expression,
+ String[] terms)
{
+ BodyBuilder builder = new BodyBuilder().begin();
+
builder.addln("%s root = (%<s) $1;", ClassFabUtils.toJavaClassName(rootClass));
String previousStep = "root";
@@ -258,37 +278,27 @@
expression);
Class activeType = rootClass;
- ReadInfo readInfo = null;
-
- // For a setter method, the navigation stops with the penultimate
- // term in the expression (the final term is what gets updated).
+ ExpressionTermInfo expressionTermInfo = null;
- int lastIndex = forSetter ? terms.length - 1 : terms.length;
-
- for (int i = 0; i < lastIndex; i++)
+ 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);
- // All the navigation terms in the expression must be readable properties.
- // The only exception is the final term in a reader method.
-
- boolean mustExist = forSetter || i < terms.length - 1;
+ if (nullable) term = term.substring(0, term.length() - 1);
- readInfo = readInfoForTerm(activeType, expression, term, mustExist);
+ expressionTermInfo = infoForTerm(activeType, expression, term);
- // 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.
+ String methodName = expressionTermInfo.getReadMethodName();
- if (readInfo == null) return null;
+ if (methodName == null)
+ throw new RuntimeException(ServicesMessages.writeOnlyProperty(term, activeType, expression));
// If a primitive type, convert to wrapper type
- Class termType = readInfo.getType();
+ Class termType = expressionTermInfo.getType();
Class wrappedType = ClassFabUtils.getWrapperType(termType);
String termJavaName = ClassFabUtils.toJavaClassName(wrappedType);
@@ -301,113 +311,41 @@
{
builder.add(" ($w) ");
}
- else if (readInfo.isCastRequired())
+ else if (expressionTermInfo.isCastRequired())
{
builder.add(" (%s) ", termJavaName);
}
- builder.addln("%s.%s();", previousStep, readInfo.getMethodName());
+ builder.addln("%s.%s();", previousStep, expressionTermInfo.getReadMethodName());
if (nullable)
{
- builder.add("if (%s == null) return", thisStep);
-
- if (!forSetter) builder.add(" null");
-
- builder.addln(";");
+ builder.add("if (%s == null) return null;", thisStep);
}
else
{
// Perform a null check on intermediate terms.
- if (i < lastIndex - 1)
- {
- builder.addln("if (%s == null) throw new NullPointerException(%s.nullTerm(\"%s\", \"%s\", root));",
- thisStep, getClass().getName(), term, expression);
- }
+ builder.addln("if (%s == null) %s.nullTerm(\"%s\", \"%s\", root);",
+ thisStep, getClass().getName(), term, expression);
}
activeType = wrappedType;
previousStep = thisStep;
}
- final String finalStepVariable = previousStep;
- final Class finalStepType = activeType;
- final ReadInfo finalReadInfo = readInfo;
-
- return new PropertyNavigationResult()
- {
- public String getFinalStepVariable()
- {
- return finalStepVariable;
- }
-
- public Class getFinalStepType()
- {
- return finalStepType;
- }
-
- public ReadInfo getFinalReadInfo()
- {
- return finalReadInfo;
- }
- };
- }
-
- private Method buildSetter(Class rootClass, ClassFab classFab, String expression, String[] terms)
- {
- BodyBuilder builder = new BodyBuilder();
- builder.begin();
-
- PropertyNavigationResult result = writePropertyNavigationCode(builder, rootClass, expression, terms, true);
-
- // 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?
-
- Method writeMethod = writeMethodForTerm(result.getFinalStepType(), expression, terms[terms.length - 1]);
-
- if (writeMethod == null)
- {
- builder.clear();
- builder
- .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;
- }
-
- Class propertyType = writeMethod.getParameterTypes()[0];
- String propertyTypeName = ClassFabUtils.toJavaClassName(propertyType);
-
- // Cast the parameter from Object to the expected type for the method.
-
- builder.addln("%s value = %s;", propertyTypeName, ClassFabUtils.castReference("$2", propertyTypeName));
-
- // Invoke the method.
-
- builder.addln("%s.%s(value);", result.getFinalStepVariable(), writeMethod.getName());
+ builder.addln("return %s;", previousStep);
builder.end();
- classFab.addMethod(Modifier.PUBLIC, SET_SIGNATURE, builder.toString());
-
- return writeMethod;
- }
-
- private Method writeMethodForTerm(Class activeType, String expression, String term)
- {
- if (term.endsWith(PARENS)) return null;
+ MethodSignature sig = new MethodSignature(activeType, "navigate", new Class[] { Object.class }, null);
- ClassPropertyAdapter classAdapter = access.getAdapter(activeType);
- PropertyAdapter adapter = classAdapter.getPropertyAdapter(term);
-
- if (adapter == null) throw new RuntimeException(
- ServicesMessages.noSuchProperty(activeType, term, expression, classAdapter.getPropertyNames()));
+ classFab.addMethod(Modifier.PRIVATE, sig, builder.toString());
- return adapter.getWriteMethod();
+ return sig;
}
- private ReadInfo readInfoForTerm(Class activeType, String expression, String term, boolean mustExist)
+
+ private ExpressionTermInfo infoForTerm(Class activeType, String expression, String term)
{
if (term.endsWith(PARENS))
{
@@ -420,16 +358,20 @@
if (method.getReturnType().equals(void.class))
throw new RuntimeException(ServicesMessages.methodIsVoid(term, activeType, expression));
-
final Class genericType = GenericsUtils.extractGenericReturnType(activeType, method);
- return new ReadInfo()
+ return new ExpressionTermInfo()
{
- public String getMethodName()
+ public String getReadMethodName()
{
return method.getName();
}
+ public String getWriteMethodName()
+ {
+ return null;
+ }
+
public Class getType()
{
return genericType;
@@ -462,18 +404,21 @@
if (adapter == null) throw new RuntimeException(
ServicesMessages.noSuchProperty(activeType, term, expression, classAdapter.getPropertyNames()));
- if (!adapter.isRead())
+ return new ExpressionTermInfo()
{
- if (mustExist) throw new RuntimeException(ServicesMessages.writeOnlyProperty(term, activeType, expression));
+ public String getReadMethodName()
+ {
+ return name(adapter.getReadMethod());
+ }
- return null;
- }
+ public String getWriteMethodName()
+ {
+ return name(adapter.getWriteMethod());
+ }
- return new ReadInfo()
- {
- public String getMethodName()
+ private String name(Method m)
{
- return adapter.getReadMethod().getName();
+ return m == null ? null : m.getName();
}
public Class getType()
@@ -505,9 +450,14 @@
throw new NoSuchMethodException(ServicesMessages.noSuchMethod(activeType, methodName));
}
- public static String nullTerm(String term, String expression, Object root)
+ /**
+ * May be invoked from the fabricated PropertyConduit instances.
+ */
+ public static void nullTerm(String term, String expression, Object root)
{
- return String.format("Property '%s' (within property expression '%s', of %s) is null.",
- term, expression, root);
+ String message = String.format("Property '%s' (within property expression '%s', of %s) is null.",
+ term, expression, root);
+
+ throw new NullPointerException(message);
}
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java?rev=669357&r1=669356&r2=669357&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java Wed Jun 18 18:55:08 2008
@@ -92,7 +92,7 @@
{
long elapsed = System.nanoTime() - startTime;
- log.info(String.format("Request time: %5.2f s (%s)", elapsed * 10E-9d, request.getPath()));
+ log.info(String.format("Request time: %5.2f s -- %s", elapsed * 10E-9d, request.getPath()));
}
}
};
Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImplTest.java?rev=669357&r1=669356&r2=669357&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImplTest.java Wed Jun 18 18:55:08 2008
@@ -15,6 +15,7 @@
package org.apache.tapestry5.internal.services;
import org.apache.tapestry5.PropertyConduit;
+import org.apache.tapestry5.beaneditor.Validate;
import org.apache.tapestry5.internal.bindings.PropBindingFactoryTest;
import org.apache.tapestry5.internal.test.InternalBaseTestCase;
import org.apache.tapestry5.ioc.internal.services.ClassFactoryImpl;
@@ -190,4 +191,16 @@
assertNull(conduit.get(bean));
}
+ @Test
+ public void field_annotations_are_visible()
+ {
+ PropertyConduit conduit = source.create(CompositeBean.class, "simple.firstName");
+
+ Validate annotation = conduit.getAnnotation(Validate.class);
+
+ assertNotNull(annotation);
+
+ assertEquals(annotation.value(), "required");
+ }
+
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SimpleBean.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SimpleBean.java?rev=669357&r1=669356&r2=669357&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SimpleBean.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SimpleBean.java Wed Jun 18 18:55:08 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.
@@ -14,10 +14,12 @@
package org.apache.tapestry5.internal.services;
+import org.apache.tapestry5.beaneditor.Validate;
import org.apache.tapestry5.beaneditor.Width;
public class SimpleBean
{
+ @Validate("required")
private String firstName;
private String lastName;
Added: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/AccessableObjectAnnotationProvider.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/AccessableObjectAnnotationProvider.java?rev=669357&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/AccessableObjectAnnotationProvider.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/AccessableObjectAnnotationProvider.java Wed Jun 18 18:55:08 2008
@@ -0,0 +1,45 @@
+// 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.tapestry5.ioc.internal.services;
+
+import org.apache.tapestry5.ioc.AnnotationProvider;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AccessibleObject;
+
+/**
+ * Provides access to annotations of an accessable object such as a {@link java.lang.reflect.Method} or {@link
+ * java.lang.reflect.Field}.
+ */
+public class AccessableObjectAnnotationProvider implements AnnotationProvider
+{
+ private final AccessibleObject object;
+
+ public AccessableObjectAnnotationProvider(AccessibleObject object)
+ {
+ this.object = object;
+ }
+
+ public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
+ {
+ return object.getAnnotation(annotationClass);
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("AnnotationProvider[%s]", object);
+ }
+}
Added: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/AnnotationProviderChain.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/AnnotationProviderChain.java?rev=669357&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/AnnotationProviderChain.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/AnnotationProviderChain.java Wed Jun 18 18:55:08 2008
@@ -0,0 +1,58 @@
+// 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.tapestry5.ioc.internal.services;
+
+import org.apache.tapestry5.ioc.AnnotationProvider;
+
+import java.lang.annotation.Annotation;
+import java.util.List;
+
+/**
+ * Chain of command for {@link org.apache.tapestry5.ioc.AnnotationProvider}.
+ */
+public class AnnotationProviderChain implements AnnotationProvider
+{
+ private final AnnotationProvider[] providers;
+
+ public AnnotationProviderChain(AnnotationProvider[] providers)
+ {
+ this.providers = providers;
+ }
+
+ /**
+ * Creates an AnnotationProvider from the list of providers. Returns either an {@link AnnotationProviderChain} or
+ * the sole element in the list.
+ */
+ public static AnnotationProvider create(List<AnnotationProvider> providers)
+ {
+ int size = providers.size();
+
+ if (size == 1) return providers.get(0);
+
+ return new AnnotationProviderChain(providers.toArray(new AnnotationProvider[providers.size()]));
+ }
+
+ public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
+ {
+ for (AnnotationProvider p : providers)
+ {
+ T result = p.getAnnotation(annotationClass);
+
+ if (result != null) return result;
+ }
+
+ return null;
+ }
+}
Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAdapterImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAdapterImpl.java?rev=669357&r1=669356&r2=669357&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAdapterImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAdapterImpl.java Wed Jun 18 18:55:08 2008
@@ -14,6 +14,8 @@
package org.apache.tapestry5.ioc.internal.services;
+import org.apache.tapestry5.ioc.AnnotationProvider;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.services.ClassPropertyAdapter;
import org.apache.tapestry5.ioc.services.PropertyAdapter;
@@ -21,6 +23,7 @@
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.util.List;
public class PropertyAdapterImpl implements PropertyAdapter
{
@@ -36,15 +39,7 @@
private final boolean castRequired;
- /**
- * Have we tried to resolve from the property name to the field yet?
- */
- private boolean fieldCheckedFor;
- /**
- * The field from the containing type that matches this property name (may be null if not found, or not checked for
- * yet).
- */
- private Field field;
+ private AnnotationProvider annotationProvider;
PropertyAdapterImpl(ClassPropertyAdapter classAdapter, String name, Class type, Method readMethod,
Method writeMethod)
@@ -139,27 +134,24 @@
public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
{
- T result = readMethod != null ? readMethod.getAnnotation(annotationClass) : null;
-
- if (result == null && writeMethod != null) result = writeMethod.getAnnotation(annotationClass);
-
- if (result == null) result = getAnnotationFromField(annotationClass);
-
- return result;
+ return getAnnnotationProvider().getAnnotation(annotationClass);
}
- private <T extends Annotation> T getAnnotationFromField(Class<T> annotationClass)
+ /**
+ * Creates (as needed) the annotation provider for this property.
+ */
+ private synchronized AnnotationProvider getAnnnotationProvider()
{
- Field field = getField();
+ if (annotationProvider == null)
+ {
+ List<AnnotationProvider> providers = CollectionFactory.newList();
- return field == null ? null : field.getAnnotation(annotationClass);
- }
+ if (readMethod != null)
+ providers.add(new AccessableObjectAnnotationProvider(readMethod));
+ if (writeMethod != null)
+ providers.add(new AccessableObjectAnnotationProvider(writeMethod));
- private synchronized Field getField()
- {
- if (!fieldCheckedFor)
- {
// There's an assumption here, that the fields match the property name (we ignore case
// which leads to a manageable ambiguity) and that the field and the getter/setter
// are in the same class (i.e., that we don't have a getter exposing a protected field inherted
@@ -174,7 +166,8 @@
{
if (f.getName().equalsIgnoreCase(name))
{
- field = f;
+ providers.add(new AccessableObjectAnnotationProvider(f));
+
break out;
}
}
@@ -182,11 +175,10 @@
cursor = cursor.getSuperclass();
}
-
- fieldCheckedFor = true;
+ annotationProvider = AnnotationProviderChain.create(providers);
}
- return field;
+ return annotationProvider;
}
public boolean isCastRequired()