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 [EMAIL
PROTECTED] java.lang.reflect.Method} or [EMAIL PROTECTED]
+ * 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 [EMAIL PROTECTED]
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 [EMAIL PROTECTED] 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()