Author: hlship
Date: Fri Jan 22 16:29:21 2010
New Revision: 902144
URL: http://svn.apache.org/viewvc?rev=902144&view=rev
Log:
Introduce a new and more efficient mechanism for injecting behavior into
components
Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentValueProvider.java
(with props)
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/AbstractIncludeAssetWorker.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ClassTransformation.java
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java?rev=902144&r1=902143&r2=902144&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java
Fri Jan 22 16:29:21 2010
@@ -4,7 +4,7 @@
// 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
+// 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,
@@ -41,7 +41,8 @@
import java.util.*;
/**
- * Implementation of the {...@link
org.apache.tapestry5.internal.services.InternalClassTransformation} interface.
+ * Implementation of the {...@link
org.apache.tapestry5.internal.services.InternalClassTransformation}
+ * interface.
*/
public final class InternalClassTransformationImpl implements
InternalClassTransformation
{
@@ -60,7 +61,8 @@
private final IdAllocator idAllocator;
/**
- * Map, keyed on InjectKey, of field name. Injections are always added as
protected (not private) fields to support
+ * Map, keyed on InjectKey, of field name. Injections are always added as
protected (not
+ * private) fields to support
* sharing of injections between a base class and a sub class.
*/
private final Map<InjectionKey, String> injectionCache =
CollectionFactory.newMap();
@@ -89,7 +91,8 @@
private Map<CtMethod, TransformMethodSignature> methodSignatures =
CollectionFactory.newMap();
- private Map<TransformMethodSignature, ComponentMethodInvocationBuilder>
methodToInvocationBuilder = CollectionFactory.newMap();
+ private Map<TransformMethodSignature, ComponentMethodInvocationBuilder>
methodToInvocationBuilder = CollectionFactory
+ .newMap();
// Key is field name, value is expression used to replace read access
@@ -124,17 +127,16 @@
/**
* Signature for newInstance() method of Instantiator.
*/
- private static final MethodSignature NEW_INSTANCE_SIGNATURE = new
MethodSignature(Component.class, "newInstance",
-
new Class[] {
-
InternalComponentResources.class },
-
null);
+ private static final MethodSignature NEW_INSTANCE_SIGNATURE = new
MethodSignature(
+ Component.class, "newInstance", new Class[]
+ { InternalComponentResources.class }, null);
/**
* This is a constructor for a base class.
*/
public InternalClassTransformationImpl(ClassFactory classFactory, CtClass
ctClass,
- ComponentClassCache
componentClassCache,
- ComponentModel componentModel,
CtClassSource classSource)
+ ComponentClassCache componentClassCache, ComponentModel
componentModel,
+ CtClassSource classSource)
{
this.ctClass = ctClass;
this.componentClassCache = componentClassCache;
@@ -155,24 +157,25 @@
addImplementedInterface(Component.class);
- resourcesFieldName =
addInjectedFieldUncached(InternalComponentResources.class, "resources", null);
+ resourcesFieldName =
addInjectedFieldUncached(InternalComponentResources.class,
+ "resources", null);
- TransformMethodSignature sig = new
TransformMethodSignature(Modifier.PUBLIC | Modifier.FINAL,
-
ComponentResources.class.getName(),
-
"getComponentResources", null, null);
+ TransformMethodSignature sig = new
TransformMethodSignature(Modifier.PUBLIC
+ | Modifier.FINAL, ComponentResources.class.getName(),
"getComponentResources",
+ null, null);
addMethod(sig, "return " + resourcesFieldName + ";");
- // The "}" will be added later, inside finish().
+ // The "}" will be added later, inside finish().
}
/**
* Constructor for a component sub-class.
*/
- private InternalClassTransformationImpl(CtClass ctClass,
InternalClassTransformation parentTransformation,
- ClassFactory classFactory,
CtClassSource classSource,
- ComponentClassCache
componentClassCache,
- ComponentModel componentModel)
+ private InternalClassTransformationImpl(CtClass ctClass,
+ InternalClassTransformation parentTransformation, ClassFactory
classFactory,
+ CtClassSource classSource, ComponentClassCache componentClassCache,
+ ComponentModel componentModel)
{
this.ctClass = ctClass;
this.componentClassCache = componentClassCache;
@@ -199,7 +202,8 @@
for (int i = 1; i <= count; i++)
{
- if (i > 1) constructor.append(", ");
+ if (i > 1)
+ constructor.append(", ");
// $0 is implicitly self, so the 0-index ConstructorArg will be
Javassisst
// pseudeo-variable $1, and so forth.
@@ -210,13 +214,14 @@
constructor.append(");\n");
- // The "}" will be added later, inside finish().
+ // The "}" will be added later, inside finish().
}
- public InternalClassTransformation createChildTransformation(CtClass
childClass, MutableComponentModel childModel)
+ public InternalClassTransformation createChildTransformation(CtClass
childClass,
+ MutableComponentModel childModel)
{
- return new InternalClassTransformationImpl(childClass, this,
classFactory, classSource, componentClassCache,
- childModel);
+ return new InternalClassTransformationImpl(childClass, this,
classFactory, classSource,
+ componentClassCache, childModel);
}
private void freeze()
@@ -259,7 +264,10 @@
}
/**
- * Invoked during instance construction to check that all fields are
either: <ul> <li>private</li> <li>static</li>
+ * Invoked during instance construction to check that all fields are
either:
+ * <ul>
+ * <li>private</li>
+ * <li>static</li>
* <li>groovy.lang.MetaClass (for Groovy compatiblility)</li> </li>
*/
void verifyFields()
@@ -274,9 +282,10 @@
// Fields must be either static or private.
- if (Modifier.isStatic(modifiers) || Modifier.isPrivate(modifiers))
continue;
+ if (Modifier.isStatic(modifiers) || Modifier.isPrivate(modifiers))
+ continue;
- // Groovy injects a public field named metaClass. We ignore it,
and add it as a claimed
+ // Groovy injects a public field named metaClass. We ignore it,
and add it as a claimed
// field to prevent any of the workers from seeing it.
if (name.equals("metaClass") &&
getFieldType(name).equals("groovy.lang.MetaClass"))
@@ -310,13 +319,16 @@
return findAnnotationInList(annotationClass, annotations);
}
- public <T extends Annotation> T
getMethodAnnotation(TransformMethodSignature signature, Class<T>
annotationClass)
+ public <T extends Annotation> T
getMethodAnnotation(TransformMethodSignature signature,
+ Class<T> annotationClass)
{
failIfFrozen();
CtMethod method = findMethod(signature);
- if (method == null) throw new
IllegalArgumentException(ServicesMessages.noDeclaredMethod(ctClass, signature));
+ if (method == null)
+ throw new IllegalArgumentException(ServicesMessages
+ .noDeclaredMethod(ctClass, signature));
List<Annotation> annotations = findMethodAnnotations(method);
@@ -324,19 +336,24 @@
}
/**
- * Searches an array of objects (that are really annotations instances) to
find one that is of the correct type,
+ * Searches an array of objects (that are really annotations instances) to
find one that is of
+ * the correct type,
* which is returned.
- *
+ *
* @param <T>
- * @param annotationClass the annotation to search for
- * @param annotations the available annotations
+ * @param annotationClass
+ * the annotation to search for
+ * @param annotations
+ * the available annotations
* @return the matching annotation instance, or null if not found
*/
- private <T extends Annotation> T findAnnotationInList(Class<T>
annotationClass, List<Annotation> annotations)
+ private <T extends Annotation> T findAnnotationInList(Class<T>
annotationClass,
+ List<Annotation> annotations)
{
for (Object annotation : annotations)
{
- if (annotationClass.isInstance(annotation)) return
annotationClass.cast(annotation);
+ if (annotationClass.isInstance(annotation))
+ return annotationClass.cast(annotation);
}
return null;
@@ -397,7 +414,8 @@
}
}
- private void addAnnotationsToList(List<Annotation> list, Object[]
annotations, boolean filterNonInherited)
+ private void addAnnotationsToList(List<Annotation> list, Object[]
annotations,
+ boolean filterNonInherited)
{
for (Object o : annotations)
{
@@ -412,7 +430,8 @@
Inherited inherited =
annotationType.getAnnotation(Inherited.class);
- if (inherited == null) continue;
+ if (inherited == null)
+ continue;
}
list.add(a);
@@ -427,7 +446,8 @@
}
catch (NotFoundException ex)
{
- throw new
RuntimeException(ServicesMessages.missingDeclaredField(ctClass, fieldName), ex);
+ throw new
RuntimeException(ServicesMessages.missingDeclaredField(ctClass, fieldName),
+ ex);
}
}
@@ -435,7 +455,8 @@
{
failIfFrozen();
- String memberName =
InternalUtils.createMemberName(Defense.notBlank(suggested, "suggested"));
+ String memberName = InternalUtils
+ .createMemberName(Defense.notBlank(suggested, "suggested"));
return idAllocator.allocateId(memberName);
}
@@ -449,13 +470,12 @@
{
failIfFrozen();
- String interfaceName = interfaceClass.getName();
-
try
{
- CtClass ctInterface = classPool.get(interfaceName);
+ CtClass ctInterface = toCtClass(interfaceClass);
- if (classImplementsInterface(ctInterface)) return;
+ if (classImplementsInterface(ctInterface))
+ return;
implementDefaultMethodsForInterface(ctInterface);
@@ -468,11 +488,14 @@
}
/**
- * Adds default implementations for the methods defined by the interface
(and all of its super-interfaces). The
- * implementations return null (or 0, or false, as appropriate to to the
method type). There are a number of
- * degenerate cases that are not covered properly: these are related to
base interfaces that may be implemented by
+ * Adds default implementations for the methods defined by the interface
(and all of its
+ * super-interfaces). The
+ * implementations return null (or 0, or false, as appropriate to to the
method type). There are
+ * a number of
+ * degenerate cases that are not covered properly: these are related to
base interfaces that may
+ * be implemented by
* base classes.
- *
+ *
* @param ctInterface
* @throws NotFoundException
*/
@@ -480,7 +503,8 @@
{
// java.lang.Object is the parent interface of interfaces
- if (ctInterface.getName().equals(Object.class.getName())) return;
+ if (ctInterface.getName().equals(Object.class.getName()))
+ return;
for (CtMethod method : ctInterface.getDeclaredMethods())
{
@@ -499,7 +523,8 @@
// up as methods of the interface. We skip those and only consider the
methods
// that are abstract.
- if (!Modifier.isAbstract(method.getModifiers())) return;
+ if (!Modifier.isAbstract(method.getModifiers()))
+ return;
try
{
@@ -524,14 +549,16 @@
}
catch (CannotCompileException ex)
{
- throw new
RuntimeException(ServicesMessages.errorAddingMethod(ctClass, method
- .getName(), ex), ex);
+ throw new
RuntimeException(ServicesMessages.errorAddingMethod(ctClass,
+ method.getName(), ex), ex);
}
}
/**
- * Check to see if the target class (or any of its super classes)
implements the provided interface. This is geared
- * for simple interfaces (that don't extend other interfaces), thus if the
class (or a base class) implement
+ * Check to see if the target class (or any of its super classes)
implements the provided
+ * interface. This is geared
+ * for simple interfaces (that don't extend other interfaces), thus if the
class (or a base
+ * class) implement
* interface Y that extends interface X, we may not return true for
interface X.
*/
@@ -542,7 +569,8 @@
{
for (CtClass anInterface : current.getInterfaces())
{
- if (anInterface == ctInterface) return true;
+ if (anInterface == ctInterface)
+ return true;
}
}
@@ -560,7 +588,8 @@
if (existing != null)
{
- String message = ServicesMessages.fieldAlreadyClaimed(fieldName,
ctClass, existing, tag);
+ String message = ServicesMessages
+ .fieldAlreadyClaimed(fieldName, ctClass, existing, tag);
throw new RuntimeException(message);
}
@@ -575,7 +604,8 @@
addOrReplaceMethod(signature, methodBody, true);
}
- private void addOrReplaceMethod(TransformMethodSignature signature, String
methodBody, boolean addAsNew)
+ private void addOrReplaceMethod(TransformMethodSignature signature, String
methodBody,
+ boolean addAsNew)
{
failIfFrozen();
@@ -609,7 +639,8 @@
try
{
- CtMethod method = new CtMethod(returnType,
signature.getMethodName(), parameters, ctClass);
+ CtMethod method = new CtMethod(returnType,
signature.getMethodName(), parameters,
+ ctClass);
// TODO: Check for duplicate method add
@@ -620,12 +651,13 @@
ctClass.addMethod(method);
- if (addAsNew) addedMethods.add(method);
+ if (addAsNew)
+ addedMethods.add(method);
}
catch (CannotCompileException ex)
{
- throw new
MethodCompileException(ServicesMessages.methodCompileError(signature,
methodBody, ex), methodBody,
- ex);
+ throw new
MethodCompileException(ServicesMessages.methodCompileError(signature,
+ methodBody, ex), methodBody, ex);
}
catch (NotFoundException ex)
{
@@ -643,7 +675,6 @@
CtClass[] parameters = buildCtClassList(signature.getParameterTypes());
CtClass[] exceptions = buildCtClassList(signature.getExceptionTypes());
-
try
{
CtMethod existing =
ctClass.getDeclaredMethod(signature.getMethodName(), parameters);
@@ -661,7 +692,8 @@
try
{
- CtMethod method = new CtMethod(returnType,
signature.getMethodName(), parameters, ctClass);
+ CtMethod method = new CtMethod(returnType,
signature.getMethodName(), parameters,
+ ctClass);
// TODO: Check for duplicate method add
@@ -674,8 +706,8 @@
}
catch (CannotCompileException ex)
{
- throw new
MethodCompileException(ServicesMessages.methodCompileError(signature,
methodBody, ex), methodBody,
- ex);
+ throw new
MethodCompileException(ServicesMessages.methodCompileError(signature,
+ methodBody, ex), methodBody, ex);
}
catch (NotFoundException ex)
{
@@ -719,8 +751,8 @@
}
catch (CannotCompileException ex)
{
- throw new
MethodCompileException(ServicesMessages.methodCompileError(methodSignature,
methodBody, ex),
- methodBody, ex);
+ throw new
MethodCompileException(ServicesMessages.methodCompileError(methodSignature,
+ methodBody, ex), methodBody, ex);
}
addMethodToDescription("extend", methodSignature, methodBody);
@@ -740,14 +772,15 @@
}
catch (CannotCompileException ex)
{
- throw new
MethodCompileException(ServicesMessages.methodCompileError(methodSignature,
methodBody, ex),
- methodBody, ex);
+ throw new
MethodCompileException(ServicesMessages.methodCompileError(methodSignature,
+ methodBody, ex), methodBody, ex);
}
addMethodToDescription("extend existing", methodSignature, methodBody);
}
- public void copyMethod(TransformMethodSignature sourceMethod, int
modifiers, String newMethodName)
+ public void copyMethod(TransformMethodSignature sourceMethod, int
modifiers,
+ String newMethodName)
{
failIfFrozen();
@@ -771,8 +804,7 @@
catch (CannotCompileException ex)
{
throw new RuntimeException(String.format("Error copying method %s
to new method %s().",
- sourceMethod,
- newMethodName), ex);
+ sourceMethod, newMethodName), ex);
}
catch (NotFoundException ex)
{
@@ -798,8 +830,8 @@
}
catch (CannotCompileException ex)
{
- throw new
MethodCompileException(ServicesMessages.methodCompileError(methodSignature,
body, ex),
- body, ex);
+ throw new
MethodCompileException(ServicesMessages.methodCompileError(methodSignature,
+ body, ex), body, ex);
}
addMethodToDescription(String.format("catch(%s) in", exceptionType),
methodSignature, body);
@@ -817,14 +849,15 @@
}
catch (CannotCompileException ex)
{
- throw new
MethodCompileException(ServicesMessages.methodCompileError(methodSignature,
methodBody, ex),
- methodBody, ex);
+ throw new
MethodCompileException(ServicesMessages.methodCompileError(methodSignature,
+ methodBody, ex), methodBody, ex);
}
addMethodToDescription("prefix", methodSignature, methodBody);
}
- private void addMethodToDescription(String operation,
TransformMethodSignature methodSignature, String methodBody)
+ private void addMethodToDescription(String operation,
TransformMethodSignature methodSignature,
+ String methodBody)
{
formatter.format("%s method: %s %s %s(", operation,
Modifier.toString(methodSignature
.getModifiers()), methodSignature.getReturnType(),
methodSignature.getMethodName());
@@ -832,7 +865,8 @@
String[] parameterTypes = methodSignature.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++)
{
- if (i > 0) description.append(", ");
+ if (i > 0)
+ description.append(", ");
formatter.format("%s $%d", parameterTypes[i], i + 1);
}
@@ -842,8 +876,10 @@
String[] exceptionTypes = methodSignature.getExceptionTypes();
for (int i = 0; i < exceptionTypes.length; i++)
{
- if (i == 0) description.append("\n throws ");
- else description.append(", ");
+ if (i == 0)
+ description.append("\n throws ");
+ else
+ description.append(", ");
description.append(exceptionTypes[i]);
}
@@ -855,20 +891,24 @@
{
CtMethod method = findDeclaredMethod(methodSignature);
- if (method != null) return method;
+ if (method != null)
+ return method;
CtMethod result = addOverrideOfSuperclassMethod(methodSignature);
- if (result != null) return result;
+ if (result != null)
+ return result;
- throw new
IllegalArgumentException(ServicesMessages.noDeclaredMethod(ctClass,
methodSignature));
+ throw new
IllegalArgumentException(ServicesMessages.noDeclaredMethod(ctClass,
+ methodSignature));
}
private CtMethod findDeclaredMethod(TransformMethodSignature
methodSignature)
{
for (CtMethod method : ctClass.getDeclaredMethods())
{
- if (match(method, methodSignature)) return method;
+ if (match(method, methodSignature))
+ return method;
}
return null;
@@ -911,7 +951,8 @@
private boolean match(CtMethod method, TransformMethodSignature sig)
{
- if (!sig.getMethodName().equals(method.getName())) return false;
+ if (!sig.getMethodName().equals(method.getName()))
+ return false;
CtClass[] paramTypes;
@@ -928,13 +969,15 @@
int count = sigTypes.length;
- if (paramTypes.length != count) return false;
+ if (paramTypes.length != count)
+ return false;
for (int i = 0; i < count; i++)
{
String paramType = paramTypes[i].getName();
- if (!paramType.equals(sigTypes[i])) return false;
+ if (!paramType.equals(sigTypes[i]))
+ return false;
}
// Ignore exceptions thrown and modifiers.
@@ -956,7 +999,6 @@
return findFields(filter);
}
-
public List<String> findFields(FieldFilter filter)
{
failIfFrozen();
@@ -967,11 +1009,13 @@
{
for (CtField field : ctClass.getDeclaredFields())
{
- if (!isInstanceField(field)) continue;
+ if (!isInstanceField(field))
+ continue;
String fieldName = field.getName();
- if (filter.accept(fieldName, field.getType().getName()))
result.add(fieldName);
+ if (filter.accept(fieldName, field.getType().getName()))
+ result.add(fieldName);
}
}
catch (NotFoundException ex)
@@ -984,7 +1028,8 @@
return result;
}
- public List<TransformMethodSignature> findMethodsWithAnnotation(final
Class<? extends Annotation> annotationClass)
+ public List<TransformMethodSignature> findMethodsWithAnnotation(
+ final Class<? extends Annotation> annotationClass)
{
failIfFrozen();
@@ -1016,7 +1061,8 @@
{
TransformMethodSignature sig = getMethodSignature(method);
- if (filter.accept(sig)) result.add(sig);
+ if (filter.accept(sig))
+ result.add(sig);
}
Collections.sort(result);
@@ -1035,8 +1081,8 @@
String[] parameters = toTypeNames(method.getParameterTypes());
String[] exceptions = toTypeNames(method.getExceptionTypes());
- result = new TransformMethodSignature(method.getModifiers(),
type, method.getName(), parameters,
- exceptions);
+ result = new TransformMethodSignature(method.getModifiers(),
type,
+ method.getName(), parameters, exceptions);
methodSignatures.put(method, result);
}
@@ -1070,15 +1116,18 @@
skipped.addAll(claimedFields.keySet());
skipped.addAll(addedFieldNames);
- if (removedFieldNames != null) skipped.addAll(removedFieldNames);
+ if (removedFieldNames != null)
+ skipped.addAll(removedFieldNames);
for (CtField field : ctClass.getDeclaredFields())
{
- if (!isInstanceField(field)) continue;
+ if (!isInstanceField(field))
+ continue;
String name = field.getName();
- if (skipped.contains(name)) continue;
+ if (skipped.contains(name))
+ continue;
names.add(name);
}
@@ -1172,8 +1221,7 @@
throw new RuntimeException(ex);
}
- formatter
- .format("add field: %s %s %s;\n\n",
Modifier.toString(modifiers), type, fieldName);
+ formatter.format("add field: %s %s %s;\n\n",
Modifier.toString(modifiers), type, fieldName);
addedFieldNames.add(fieldName);
@@ -1190,7 +1238,8 @@
String fieldName = searchForPreviousInjection(key);
- if (fieldName != null) return fieldName;
+ if (fieldName != null)
+ return fieldName;
// TODO: Probably doesn't handle arrays and primitives.
@@ -1203,25 +1252,57 @@
return fieldName;
}
- /**
- * This is split out from {...@link #addInjectedField(Class, String,
Object)} to handle a special case for the
- * InternalComponentResources, which is null when "injected" (during the
class transformation) and is only
- * determined when a component is actually instantiated.
- */
- private String addInjectedFieldUncached(Class type, String suggestedName,
Object value)
+ public <T> String addIndirectInjectedField(Class<T> type, String
suggestedName,
+ ComponentValueProvider<T> provider)
{
- CtClass ctType;
+ Defense.notNull(type, "type");
+ Defense.notNull(provider, "provider");
+
+ String fieldName = addField(Modifier.PRIVATE | Modifier.FINAL,
type.getName(),
+ suggestedName);
+
+ // TODO: This shouldn't have to be constantly recomputed
+
+ CtClass providerType = toCtClass(ComponentValueProvider.class);
+
+ constructorArgs.add(new ConstructorArg(providerType, provider));
+
+ // Inside the constructor,
+ // pass the resources to the provider's get() method, cast to the
+ // field type and assign. This will likely not work with
+ // primitives and arrays, but that's ok for now.
+
+ extendConstructor(String.format(" %s = (%s) $%d.get(%s);", fieldName,
type.getName(),
+ constructorArgs.size(), resourcesFieldName));
+ return fieldName;
+ }
+
+ private CtClass toCtClass(Class type)
+ {
try
{
- ctType = classPool.get(type.getName());
+ return classPool.get(type.getName());
}
catch (NotFoundException ex)
{
throw new RuntimeException(ex);
}
+ }
+
+ /**
+ * This is split out from {...@link #addInjectedField(Class, String,
Object)} to handle a special
+ * case for the
+ * InternalComponentResources, which is null when "injected" (during the
class transformation)
+ * and is only
+ * determined when a component is actually instantiated.
+ */
+ private String addInjectedFieldUncached(Class type, String suggestedName,
Object value)
+ {
+ CtClass ctType = toCtClass(type);
- String fieldName = addField(Modifier.PROTECTED | Modifier.FINAL,
type.getName(), suggestedName);
+ String fieldName = addField(Modifier.PROTECTED | Modifier.FINAL,
type.getName(),
+ suggestedName);
addInjectToConstructor(fieldName, ctType, value);
@@ -1234,9 +1315,11 @@
{
String result = injectionCache.get(key);
- if (result != null) return result;
+ if (result != null)
+ return result;
- if (parentTransformation != null) return
parentTransformation.searchForPreviousInjection(key);
+ if (parentTransformation != null)
+ return parentTransformation.searchForPreviousInjection(key);
return null;
}
@@ -1250,7 +1333,8 @@
if (builder == null)
{
- builder = new ComponentMethodInvocationBuilder(this,
componentClassCache, methodSignature, classSource);
+ builder = new ComponentMethodInvocationBuilder(this,
componentClassCache,
+ methodSignature, classSource);
methodToInvocationBuilder.put(methodSignature, builder);
}
@@ -1262,13 +1346,15 @@
Defense.notNull(methodSignature, "methodSignature");
if (!isMethod(methodSignature))
- throw new IllegalArgumentException(String.format("Method %s is not
implemented by transformed class %s.",
- methodSignature,
getClassName()));
+ throw new IllegalArgumentException(String.format(
+ "Method %s is not implemented by transformed class %s.",
methodSignature,
+ getClassName()));
InternalClassTransformation search = parentTransformation;
while (search != null)
{
- if (search.isMethod(methodSignature)) return true;
+ if (search.isMethod(methodSignature))
+ return true;
search = search.getParentTransformation();
}
@@ -1291,11 +1377,15 @@
}
/**
- * Adds a parameter to the constructor for the class; the parameter is
used to initialize the value for a field.
- *
- * @param fieldName name of field to inject
- * @param fieldType Javassist type of the field (and corresponding
parameter)
- * @param value the value to be injected (which will in unusual cases
be null)
+ * Adds a parameter to the constructor for the class; the parameter is
used to initialize the
+ * value for a field.
+ *
+ * @param fieldName
+ * name of field to inject
+ * @param fieldType
+ * Javassist type of the field (and corresponding parameter)
+ * @param value
+ * the value to be injected (which will in unusual cases be
null)
*/
private void addInjectToConstructor(String fieldName, CtClass fieldType,
Object value)
{
@@ -1380,7 +1470,8 @@
for (int i = 0; i < count; i++)
{
- if (i > 0) description.append(", ");
+ if (i > 0)
+ description.append(", ");
formatter.format("%s $%d", types[i].getName(), i + 1);
}
@@ -1400,11 +1491,11 @@
ctClass.addMethod(initializerMethod);
- // Replace the constructor body with one that fails. This leaves,
as an open question,
+ // Replace the constructor body with one that fails. This leaves,
as an open question,
// what to do about any other constructors.
- String body = String.format("throw new RuntimeException(\"%s\");",
-
ServicesMessages.forbidInstantiateComponentClass(getClassName()));
+ String body = String.format("throw new RuntimeException(\"%s\");",
ServicesMessages
+ .forbidInstantiateComponentClass(getClassName()));
defaultConstructor.setBody(body);
}
@@ -1420,23 +1511,20 @@
public Instantiator createInstantiator()
{
- if (Modifier.isAbstract(ctClass.getModifiers()))
+ if (Modifier.isAbstract(ctClass.getModifiers())) { return new
Instantiator()
{
- return new Instantiator()
+ public Component newInstance(InternalComponentResources resources)
{
- public Component newInstance(InternalComponentResources
resources)
- {
- throw new RuntimeException(
- String.format("Component class %s is abstract and
can not be instantiated.",
- ctClass.getName()));
- }
+ throw new RuntimeException(String.format(
+ "Component class %s is abstract and can not be
instantiated.", ctClass
+ .getName()));
+ }
- public ComponentModel getModel()
- {
- return componentModel;
- }
- };
- }
+ public ComponentModel getModel()
+ {
+ return componentModel;
+ }
+ }; }
String componentClassName = ctClass.getName();
@@ -1446,9 +1534,12 @@
BodyBuilder constructor = new BodyBuilder();
- // This is realy -1 + 2: The first value in constructorArgs is the
InternalComponentResources, which doesn't
- // count toward's the Instantiator's constructor ... then we add in
the Model and String description.
- // It's tricky because there's the constructor parameters for the
Instantiator, most of which are stored
+ // This is realy -1 + 2: The first value in constructorArgs is the
+ // InternalComponentResources, which doesn't
+ // count toward's the Instantiator's constructor ... then we add in
the Model and String
+ // description.
+ // It's tricky because there's the constructor parameters for the
Instantiator, most of
+ // which are stored
// in fields and then used as the constructor parameters for the
Component.
Class[] constructorParameterTypes = new Class[constructorArgs.size() +
1];
@@ -1498,9 +1589,8 @@
String parameterReference = "$" + (i + 2);
- constructor.addln("%s = %s;",
- fieldName,
- ClassFabUtils.castReference(parameterReference,
fieldType.getName()));
+ constructor.addln("%s = %s;", fieldName,
ClassFabUtils.castReference(
+ parameterReference, fieldType.getName()));
newInstance.add(", %s", fieldName);
}
@@ -1516,7 +1606,8 @@
try
{
- Object instance =
instantiatorClass.getConstructors()[0].newInstance(constructorParameterValues);
+ Object instance = instantiatorClass.getConstructors()[0]
+ .newInstance(constructorParameterValues);
return (Instantiator) instance;
}
@@ -1528,14 +1619,17 @@
private void failIfFrozen()
{
- if (frozen) throw new IllegalStateException(
- "The ClassTransformation instance (for " + ctClass.getName() +
") has completed all transformations and may not be further modified.");
+ if (frozen)
+ throw new IllegalStateException("The ClassTransformation instance
(for "
+ + ctClass.getName()
+ + ") has completed all transformations and may not be
further modified.");
}
private void failIfNotFrozen()
{
- if (!frozen) throw new IllegalStateException(
- "The ClassTransformation instance (for " + ctClass.getName() +
") has not yet completed all transformations.");
+ if (!frozen)
+ throw new IllegalStateException("The ClassTransformation instance
(for "
+ + ctClass.getName() + ") has not yet completed all
transformations.");
}
public IdAllocator getIdAllocator()
@@ -1556,7 +1650,8 @@
{
failIfFrozen();
- if (classAnnotations == null) assembleClassAnnotations();
+ if (classAnnotations == null)
+ assembleClassAnnotations();
return classAnnotations;
}
@@ -1597,15 +1692,17 @@
{
Formatter formatter = new Formatter(builder);
- formatter.format("%s %s extends %s",
Modifier.toString(ctClass.getModifiers()), ctClass.getName(),
- ctClass.getSuperclass().getName());
+ formatter.format("%s %s extends %s",
Modifier.toString(ctClass.getModifiers()), ctClass
+ .getName(), ctClass.getSuperclass().getName());
CtClass[] interfaces = ctClass.getInterfaces();
for (int i = 0; i < interfaces.length; i++)
{
- if (i == 0) builder.append("\n implements ");
- else builder.append(", ");
+ if (i == 0)
+ builder.append("\n implements ");
+ else
+ builder.append(", ");
builder.append(interfaces[i].getName());
}
@@ -1628,8 +1725,9 @@
String fieldType = getFieldType(fieldName);
- TransformMethodSignature sig = new
TransformMethodSignature(Modifier.PRIVATE, "void", methodName,
- new
String[] { fieldType }, null);
+ TransformMethodSignature sig = new
TransformMethodSignature(Modifier.PRIVATE, "void",
+ methodName, new String[]
+ { fieldType }, null);
String message = ServicesMessages.readOnlyField(ctClass.getName(),
fieldName);
@@ -1646,7 +1744,8 @@
// TODO: We could check that there's an existing field read and field
write transform ...
- if (removedFieldNames == null) removedFieldNames =
CollectionFactory.newSet();
+ if (removedFieldNames == null)
+ removedFieldNames = CollectionFactory.newSet();
removedFieldNames.add(fieldName);
}
@@ -1658,7 +1757,8 @@
String body = String.format("$_ = $0.%s();", methodName);
- if (fieldReadTransforms == null) fieldReadTransforms =
CollectionFactory.newMap();
+ if (fieldReadTransforms == null)
+ fieldReadTransforms = CollectionFactory.newMap();
// TODO: Collisions?
@@ -1674,7 +1774,8 @@
String body = String.format("$0.%s($1);", methodName);
- if (fieldWriteTransforms == null) fieldWriteTransforms =
CollectionFactory.newMap();
+ if (fieldWriteTransforms == null)
+ fieldWriteTransforms = CollectionFactory.newMap();
// TODO: Collisions?
@@ -1688,7 +1789,8 @@
// If no field transformations have been requested, then we can save
ourselves some
// trouble!
- if (fieldReadTransforms != null || fieldWriteTransforms != null)
replaceFieldAccess();
+ if (fieldReadTransforms != null || fieldWriteTransforms != null)
+ replaceFieldAccess();
if (removedFieldNames != null)
{
@@ -1714,9 +1816,11 @@
// Provide empty maps here, to make the code in the inner class a tad
// easier.
- if (fieldReadTransforms == null) fieldReadTransforms =
CollectionFactory.newMap();
+ if (fieldReadTransforms == null)
+ fieldReadTransforms = CollectionFactory.newMap();
- if (fieldWriteTransforms == null) fieldWriteTransforms =
CollectionFactory.newMap();
+ if (fieldWriteTransforms == null)
+ fieldWriteTransforms = CollectionFactory.newMap();
ExprEditor editor = new ExprEditor()
{
@@ -1725,14 +1829,15 @@
{
CtBehavior where = access.where();
- if (where instanceof CtConstructor) return;
+ if (where instanceof CtConstructor)
+ return;
boolean isRead = access.isReader();
String fieldName = access.getFieldName();
CtMethod method = (CtMethod) where;
- formatter.format("Checking field %s %s in method %s(): ",
isRead ? "read" : "write", fieldName,
- method.getName());
+ formatter.format("Checking field %s %s in method %s(): ",
+ isRead ? "read" : "write", fieldName,
method.getName());
// Ignore any methods to were added as part of the
transformation.
// If we reference the field there, we really mean the field.
@@ -1743,7 +1848,8 @@
return;
}
- Map<String, String> transformMap = isRead ?
fieldReadTransforms : fieldWriteTransforms;
+ Map<String, String> transformMap = isRead ? fieldReadTransforms
+ : fieldWriteTransforms;
String body = transformMap.get(fieldName);
if (body == null)
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/AbstractIncludeAssetWorker.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/AbstractIncludeAssetWorker.java?rev=902144&r1=902143&r2=902144&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/AbstractIncludeAssetWorker.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/AbstractIncludeAssetWorker.java
Fri Jan 22 16:29:21 2010
@@ -1,10 +1,10 @@
-// Copyright 2007, 2008, 2009 The Apache Software Foundation
+// Copyright 2007, 2008, 2009, 2010 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
+// 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,
@@ -14,10 +14,12 @@
package org.apache.tapestry5.internal.transform;
+import java.util.List;
+import java.util.Locale;
+
import org.apache.tapestry5.Asset;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.annotations.SetupRender;
-import org.apache.tapestry5.internal.services.ComponentResourcesOperation;
import org.apache.tapestry5.ioc.Resource;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.services.SymbolSource;
@@ -25,14 +27,12 @@
import org.apache.tapestry5.services.AssetSource;
import org.apache.tapestry5.services.ClassTransformation;
import org.apache.tapestry5.services.ComponentClassTransformWorker;
+import org.apache.tapestry5.services.ComponentValueProvider;
import org.apache.tapestry5.services.TransformConstants;
-import java.util.List;
-import java.util.Locale;
-
/**
- * Base class for workers that automatically inlcude assets in the page (via
methods on {...@link
- * org.apache.tapestry5.RenderSupport}).
+ * Base class for workers that automatically include assets in the page (via
methods on
+ * {...@link org.apache.tapestry5.RenderSupport}).
*/
public abstract class AbstractIncludeAssetWorker implements
ComponentClassTransformWorker
{
@@ -47,15 +47,19 @@
}
/**
- * Expands symbols in the path, then adds an operation into the setup
render phase of the component. Ultimately,
- * {...@link #handleAsset(org.apache.tapestry5.Asset)} will be invoked for
each asset (dervied from assetPaths).
- *
- * @param transformation transformation process for component
- * @param model component model for component
- * @param assetPaths raw paths to be converted to assets
+ * Expands symbols in the path, then adds an operation into the setup
render phase of the
+ * component. Ultimately, {...@link
#handleAsset(org.apache.tapestry5.Asset)} will be invoked for
+ * each asset (dervied from assetPaths).
+ *
+ * @param transformation
+ * transformation process for component
+ * @param model
+ * component model for component
+ * @param assetPaths
+ * raw paths to be converted to assets
*/
protected final void addOperationForAssetPaths(ClassTransformation
transformation,
- MutableComponentModel
model, String[] assetPaths)
+ MutableComponentModel model, String[] assetPaths)
{
final Resource baseResource = model.getBaseResource();
final List<String> paths = CollectionFactory.newList();
@@ -67,45 +71,57 @@
paths.add(expanded);
}
- ComponentResourcesOperation op = new ComponentResourcesOperation()
+ ComponentValueProvider<Runnable> provider = new
ComponentValueProvider<Runnable>()
{
- // Remember that ONE instances of this op will be injected into
EVERY instance
- // of the component ... that means that we can't do any aggresive
caching
- // inside the operation (the operation must be threadsafe).
-
- public void perform(ComponentResources resources)
+ @Override
+ public Runnable get(final ComponentResources resources)
{
- Locale locale = resources.getLocale();
+ // This code is re-executed for each new component instance.
We could
+ // possibly cache on resources.getCompleteId() + locale, but
that's
+ // probably not worth the effort.
+
+ final Locale locale = resources.getLocale();
+
+ final List<Asset> assets = CollectionFactory.newList();
for (String assetPath : paths)
{
Asset asset = assetSource.getAsset(baseResource,
assetPath, locale);
- handleAsset(asset);
+ assets.add(asset);
}
+
+ return new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ for (Asset asset : assets)
+ {
+ handleAsset(asset);
+ }
+ }
+ };
}
};
- String opFieldName =
transformation.addInjectedField(ComponentResourcesOperation.class, "operation",
op);
-
- String resourcesName = transformation.getResourcesFieldName();
-
- String body = String.format("%s.perform(%s);", opFieldName,
resourcesName);
-
- // This is what I like about this approach; the injected body is tiny.
The downside is that
- // the object that gets injected is hard to test, hard enough that
we'll just concentrate on
- // the integration test, thank you.
+ String runnableFieldName =
transformation.addIndirectInjectedField(Runnable.class,
+ "includeAssets", provider);
- transformation.extendMethod(TransformConstants.SETUP_RENDER_SIGNATURE,
body);
+ transformation.extendMethod(TransformConstants.SETUP_RENDER_SIGNATURE,
String.format(
+ "%s.run();", runnableFieldName));
model.addRenderPhase(SetupRender.class);
}
/**
- * Invoked, from the component's setup render phase, for each asset. This
method must be threadsafe. Most
- * implementation pass the asset to a particular method of {...@link
org.apache.tapestry5.RenderSupport}.
- *
- * @param asset to be processed
+ * Invoked, from the component's setup render phase, for each asset. This
method must be
+ * threadsafe. Most
+ * implementations pass the asset to a particular method of
+ * {...@link org.apache.tapestry5.RenderSupport}.
+ *
+ * @param asset
+ * to be processed
*/
protected abstract void handleAsset(Asset asset);
}
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ClassTransformation.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ClassTransformation.java?rev=902144&r1=902143&r2=902144&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ClassTransformation.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ClassTransformation.java
Fri Jan 22 16:29:21 2010
@@ -15,6 +15,8 @@
package org.apache.tapestry5.services;
import javassist.CtBehavior;
+
+import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.ioc.AnnotationProvider;
import org.slf4j.Logger;
@@ -230,6 +232,8 @@
* and value will return the same field name. Caching extends to the
parent transformation, so
* that a value injected
* into a parent class will be available (via the protected instance
variable) to subclasses.
+ * This is primarily used to inject service dependencies into components,
though it has a number
+ * of other uses as well.
*
* @param type
* the type of object to inject
@@ -242,7 +246,21 @@
String addInjectedField(Class type, String suggestedName, Object value);
/**
- * Converts the field into a read only field whose value is the provided
value. This is used
+ * Like {...@link #addInjectedField(Class, String, Object)}, but instead
of specifying the value,
+ * a provider for the value is specified. In the generated class'
constructor, the provider
+ * will be passed the {...@link ComponentResources} and will return the
final value; thus
+ * each component <em>instance</em> will receive a unique
+ * @param <T>
+ * @param type type of value to inject
+ * @param suggestedName suggested name for the new field
+ * @param provider injected into the component to provide the value
+ * @return the actual name of the injected field
+ * @since 5.2
+ */
+ <T> String addIndirectInjectedField(Class<T> type, String suggestedName,
ComponentValueProvider<T> provider);
+
+ /**
+ * Converts and <em>existing</em> field into a read only field whose value
is the provided value. This is used
* when converting an
* existing field into a read-only injected value.
*
Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentValueProvider.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentValueProvider.java?rev=902144&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentValueProvider.java
(added)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentValueProvider.java
Fri Jan 22 16:29:21 2010
@@ -0,0 +1,40 @@
+// Copyright 2010 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.services;
+
+import org.apache.tapestry5.ComponentResources;
+
+/**
+ * An object used to provide a value of a specific type to a component
(represented by an
+ * instance of {...@link ComponentResources}). The provider will create and
return the value
+ * (some providers may be smart enough to cache a value, but should be
implemented in
+ * a thread-safe manner). Often the provider is an inner class of a
+ * {...@link ComponentClassTransformWorker}.
+ *
+ * @param <T>
+ * type of object provided
+ * @since 5.2.0
+ */
+public interface ComponentValueProvider<T>
+{
+ /**
+ * Provide the object for the indicated component.
+ *
+ * @param resources
+ * Identifies the component
+ * @return the object
+ */
+ T get(ComponentResources resources);
+}
Propchange:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentValueProvider.java
------------------------------------------------------------------------------
svn:eol-style = native