Author: hlship Date: Fri Apr 21 11:59:10 2006 New Revision: 395987 URL: http://svn.apache.org/viewcvs?rev=395987&view=rev Log: Work on the injectField() method of ClassTransformation
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ResourceAware.java tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/worker/ImplementResourceAwareWorker.java tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/ClassTransformWorker.java - copied, changed from r395912, tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ClassTransformWorker.java tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/TransformConstants.java Removed: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ClassTransformWorker.java tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/TransformConstants.java Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ClassTransformerImpl.java tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalClassTransformation.java tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalClassTransformationImpl.java tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/TransformMessages.java tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/worker/RetainWorker.java tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/worker/UnclaimedFieldWorker.java tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/ClassTransformation.java tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/MethodSignature.java tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/transform/TransformStrings.properties tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/InternalClassTransformationImplTest.java tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/worker/UnclaimedFieldWorkerTest.java Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ResourceAware.java URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ResourceAware.java?rev=395987&view=auto ============================================================================== --- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ResourceAware.java (added) +++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ResourceAware.java Fri Apr 21 11:59:10 2006 @@ -0,0 +1,13 @@ +package org.apache.tapestry; + +/** + * Interface implemented by components (after they have been transformed at load time). Component + * classes should not implement this interface directly. + * + * @author Howard M. Lewis Ship + */ +public interface ResourceAware +{ + /** Returns the resources associated with this component class. */ + ComponentResources getResources(); +} Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ClassTransformerImpl.java URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ClassTransformerImpl.java?rev=395987&r1=395986&r2=395987&view=diff ============================================================================== --- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ClassTransformerImpl.java (original) +++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ClassTransformerImpl.java Fri Apr 21 11:59:10 2006 @@ -4,8 +4,10 @@ import javassist.CtClass; +import org.apache.tapestry.annotations.ComponentClass; import org.apache.tapestry.internal.model.MutableComponentModelImpl; import org.apache.tapestry.model.MutableComponentModel; +import org.apache.tapestry.transform.ClassTransformWorker; import static org.apache.tapestry.util.CollectionFactory.newMap; @@ -19,6 +21,8 @@ /** Map from class name to class transformation. */ private final Map<String, InternalClassTransformation> _nameToClassTransformation = newMap(); + private ClassTransformWorker _workers; + public Instantiator findInstantiator(String classname) { // TODO Auto-generated method stub @@ -33,11 +37,29 @@ InternalClassTransformation transformation = new InternalClassTransformationImpl(ctClass); + // Not all classes in the packages are components. + + if (transformation.getAnnotation(ComponentClass.class) == null) + return; + // Eventually these will also be cached or published or something. MutableComponentModel model = new MutableComponentModelImpl(); + _workers.transform(transformation, model); + + transformation.finish(); + _nameToClassTransformation.put(classname, transformation); + } + + /** + * For injection. This will usually be an ordered series of [EMAIL PROTECTED] ClassTransformWorker}s, as a + * chain-of-command. + */ + public final void setWorkers(ClassTransformWorker workers) + { + _workers = workers; } } Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalClassTransformation.java URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalClassTransformation.java?rev=395987&r1=395986&r2=395987&view=diff ============================================================================== --- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalClassTransformation.java (original) +++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalClassTransformation.java Fri Apr 21 11:59:10 2006 @@ -14,15 +14,20 @@ package org.apache.tapestry.internal.transform; +import org.apache.tapestry.transform.ClassTransformWorker; import org.apache.tapestry.transform.ClassTransformation; /** - * Extends [EMAIL PROTECTED] org.apache.tapestry.transform.ClassTransformation} with additional methods that may only be - * used internally by Tapestry. + * Extends [EMAIL PROTECTED] org.apache.tapestry.transform.ClassTransformation} with additional methods that + * may only be used internally by Tapestry. * * @author Howard M. Lewis Ship */ public interface InternalClassTransformation extends ClassTransformation { - + /** + * Invoked after all [EMAIL PROTECTED] ClassTransformWorker}s have had their chance to work over the + * class. This performs any final operations for the class transformation. + */ + void finish(); } Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalClassTransformationImpl.java URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalClassTransformationImpl.java?rev=395987&r1=395986&r2=395987&view=diff ============================================================================== --- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalClassTransformationImpl.java (original) +++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalClassTransformationImpl.java Fri Apr 21 11:59:10 2006 @@ -24,14 +24,17 @@ import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; +import javassist.CtConstructor; import javassist.CtField; import javassist.CtMember; import javassist.CtMethod; +import javassist.CtNewConstructor; import javassist.CtNewMethod; import javassist.NotFoundException; import javassist.expr.ExprEditor; import javassist.expr.FieldAccess; +import org.apache.hivemind.service.BodyBuilder; import org.apache.tapestry.transform.MethodSignature; import org.apache.tapestry.util.IdAllocator; @@ -67,26 +70,47 @@ private final Set<String> _addedFieldNames = newSet(); + private final Map<String, String> _meta = newMap(); + // Cache of class annotations private Object[] _classAnnotations; + private final List<ConstructorArg> _constructorArgs = newList(); + // All names will have this value as a prefix private final String NAME_PREFIX = "_$"; - private final static Map<CtClass, String> _defaultMethodBody = newMap(); - - static + private static class ConstructorArg { - _defaultMethodBody.put(CtClass.booleanType, "return false;"); - _defaultMethodBody.put(CtClass.byteType, "return 0;"); - _defaultMethodBody.put(CtClass.shortType, "return 0;"); - _defaultMethodBody.put(CtClass.intType, "return 0;"); - _defaultMethodBody.put(CtClass.longType, "return 0l;"); - _defaultMethodBody.put(CtClass.floatType, "return 0.0f;"); - _defaultMethodBody.put(CtClass.doubleType, "return 0.0d;"); - _defaultMethodBody.put(CtClass.voidType, "return;"); + private final String _fieldName; + + private final Class _type; + + private final Object _value; + + ConstructorArg(Class type, String fieldName, Object value) + { + _type = type; + _fieldName = fieldName; + _value = value; + } + + String getFieldName() + { + return _fieldName; + } + + Class getType() + { + return _type; + } + + Object getValue() + { + return _value; + } } public InternalClassTransformationImpl(CtClass ctClass) @@ -366,16 +390,12 @@ newMethod.setModifiers(Modifier.PUBLIC); - _ctClass.addMethod(newMethod); - - String body = _defaultMethodBody.get(method.getReturnType()); + // Javassist will provide a minimal implementation for us (return null, false, 0, + // whatever). - // If null, then its an object (or array) return type + newMethod.setBody(null); - if (body == null) - body = "return null;"; - - newMethod.setBody(body); + _ctClass.addMethod(newMethod); } catch (CannotCompileException ex) { @@ -433,8 +453,66 @@ public void extendMethod(MethodSignature methodSignature, String methodBody) { - // TODO Auto-generated method stub + CtMethod method = findMethod(methodSignature); + + try + { + method.insertAfter(methodBody); + } + catch (CannotCompileException ex) + { + throw new RuntimeException(ex); + } + } + + private CtMethod findMethod(MethodSignature methodSignature) + { + for (CtMethod method : _ctClass.getDeclaredMethods()) + { + if (match(method, methodSignature)) + return method; + } + throw new IllegalArgumentException(TransformMessages.noDeclaredMethod( + methodSignature, + _ctClass)); + } + + private boolean match(CtMethod method, MethodSignature sig) + { + if (!sig.getMethodName().equals(method.getName())) + return false; + + CtClass[] paramTypes; + + try + { + paramTypes = method.getParameterTypes(); + } + catch (NotFoundException ex) + { + throw new RuntimeException(ex); + } + + String[] sigTypes = sig.getParameterTypes(); + + int count = sigTypes.length; + + 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; + } + + // Ignore exceptions thrown and modifiers. + // TODO: Validate a match on return type? + + return true; } public List<String> findFieldsWithAnnotation(Class<? extends Annotation> annotationClass) @@ -529,9 +607,108 @@ return fieldName; } + public String addInjectedField(Class type, String suggestedName, Object value) + { + notNull(type, "type"); + + // TODO: Probably doesn't handle arrays and primitives. + + String fieldName = newField(Modifier.PROTECTED, type.getName(), suggestedName); + + _constructorArgs.add(new ConstructorArg(type, fieldName, value)); + + return fieldName; + } + private CtClass convertNameToCtType(String type) throws NotFoundException { return _classPool.get(type); + } + + public void finish() + { + BodyBuilder builder = new BodyBuilder(); + + builder.begin(); + + int count = _constructorArgs.size(); + + CtClass[] types = new CtClass[count]; + + for (int i = 0; i < count; i++) + { + ConstructorArg arg = _constructorArgs.get(i); + + builder.addln("{0} = ${1};", arg.getFieldName(), i + 1); + + try + { + types[i] = _classPool.get(arg.getType().getName()); + } + catch (NotFoundException ex) + { + throw new RuntimeException(ex); + } + } + + builder.end(); + + try + { + CtConstructor cons = CtNewConstructor.make(types, null, builder.toString(), _ctClass); + _ctClass.addConstructor(cons); + } + catch (CannotCompileException ex) + { + throw new RuntimeException(ex); + } + + } + + public String readMeta(String key) + { + return _meta.get(notBlank(key, "key")); + } + + public void storeMeta(String key, String value) + { + notBlank(key, "key"); + + if (_meta.containsKey(key)) + throw new IllegalArgumentException(TransformMessages.metaKeyExists( + key, + _meta.get(key), + value)); + + _meta.put(key, notBlank(value, "value")); + } + + public void addGetterMethod(String type, String methodName, String fieldName) + { + try + { + CtClass ctType = _classPool.get(type); + + CtMethod method = CtNewMethod.make( + ctType, + notBlank(methodName, "methodName"), + null, + null, + "return " + fieldName + ";", + _ctClass); + _ctClass.addMethod(method); + + _addedMethods.add(method); + } + catch (NotFoundException ex) + { + throw new RuntimeException(ex); + } + catch (CannotCompileException ex) + { + throw new RuntimeException(ex); + } + } } Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/TransformMessages.java URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/TransformMessages.java?rev=395987&r1=395986&r2=395987&view=diff ============================================================================== --- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/TransformMessages.java (original) +++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/TransformMessages.java Fri Apr 21 11:59:10 2006 @@ -18,6 +18,7 @@ import org.apache.hivemind.Messages; import org.apache.hivemind.impl.MessageFormatter; +import org.apache.tapestry.transform.MethodSignature; /** * @author Howard M. Lewis Ship @@ -51,5 +52,15 @@ { return MESSAGES.format("field-already-claimed", new Object[] { fieldName, ctClass.getName(), existingTag, newTag }); + } + + static String metaKeyExists(String key, String existing, String value) + { + return MESSAGES.format("meta-key-exists", key, existing, value); + } + + static String noDeclaredMethod(MethodSignature methodSignature, CtClass ctClass) + { + return MESSAGES.format("no-declared-method", methodSignature, ctClass.getName()); } } Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/worker/ImplementResourceAwareWorker.java URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/worker/ImplementResourceAwareWorker.java?rev=395987&view=auto ============================================================================== --- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/worker/ImplementResourceAwareWorker.java (added) +++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/worker/ImplementResourceAwareWorker.java Fri Apr 21 11:59:10 2006 @@ -0,0 +1,45 @@ +package org.apache.tapestry.internal.transform.worker; + +import java.lang.reflect.Modifier; + +import org.apache.tapestry.ComponentResources; +import org.apache.tapestry.ResourceAware; +import org.apache.tapestry.internal.InternalComponentResources; +import org.apache.tapestry.model.MutableComponentModel; +import org.apache.tapestry.transform.ClassTransformWorker; +import org.apache.tapestry.transform.ClassTransformation; +import org.apache.tapestry.transform.MethodSignature; +import org.apache.tapestry.transform.TransformConstants; + +/** + * Transforms classes so that they implement the [EMAIL PROTECTED] org.apache.tapestry.ResourceAware} + * interface. In addition, sets things up so that the first constructor argument is an + * [EMAIL PROTECTED] org.apache.tapestry.internal.InternalComponentResources} that is exposed through a + * protected instance variable. + * <p> + * This worker should be "scheduled" to operate absolutely first. + * + * @author Howard M. Lewis Ship + */ +public class ImplementResourceAwareWorker implements ClassTransformWorker +{ + + public void transform(ClassTransformation transformation, MutableComponentModel model) + { + transformation.addImplementedInterface(ResourceAware.class); + + String fieldName = transformation.addInjectedField( + InternalComponentResources.class, + "resources", + null); + + MethodSignature sig = new MethodSignature(Modifier.PUBLIC, ComponentResources.class + .getName(), "getResources", null, null); + + // Override the default, empty implementation to simply return the field. + + transformation.extendMethod(sig, "return " + fieldName + ";"); + + transformation.storeMeta(TransformConstants.RESOURCES_FIELD_NAME_KEY, fieldName); + } +} Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/worker/RetainWorker.java URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/worker/RetainWorker.java?rev=395987&r1=395986&r2=395987&view=diff ============================================================================== --- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/worker/RetainWorker.java (original) +++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/worker/RetainWorker.java Fri Apr 21 11:59:10 2006 @@ -17,8 +17,8 @@ import java.util.List; import org.apache.tapestry.annotations.Retain; -import org.apache.tapestry.internal.transform.ClassTransformWorker; import org.apache.tapestry.model.MutableComponentModel; +import org.apache.tapestry.transform.ClassTransformWorker; import org.apache.tapestry.transform.ClassTransformation; /** Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/worker/UnclaimedFieldWorker.java URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/worker/UnclaimedFieldWorker.java?rev=395987&r1=395986&r2=395987&view=diff ============================================================================== --- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/worker/UnclaimedFieldWorker.java (original) +++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/worker/UnclaimedFieldWorker.java Fri Apr 21 11:59:10 2006 @@ -18,10 +18,10 @@ import org.apache.hivemind.service.BodyBuilder; import org.apache.tapestry.events.ComponentLifecycle; -import org.apache.tapestry.internal.transform.ClassTransformWorker; -import org.apache.tapestry.internal.transform.TransformConstants; import org.apache.tapestry.model.MutableComponentModel; +import org.apache.tapestry.transform.ClassTransformWorker; import org.apache.tapestry.transform.ClassTransformation; +import org.apache.tapestry.transform.TransformConstants; /** * Designed to be just about the last worker in the pipeline. Its job is to add cleanup code that Copied: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/ClassTransformWorker.java (from r395912, tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ClassTransformWorker.java) URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/ClassTransformWorker.java?p2=tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/ClassTransformWorker.java&p1=tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ClassTransformWorker.java&r1=395912&r2=395987&rev=395987&view=diff ============================================================================== --- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ClassTransformWorker.java (original) +++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/ClassTransformWorker.java Fri Apr 21 11:59:10 2006 @@ -12,10 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -package org.apache.tapestry.internal.transform; +package org.apache.tapestry.transform; import org.apache.tapestry.model.MutableComponentModel; -import org.apache.tapestry.transform.ClassTransformation; /** * Interface for a set of objects that can perform component class transformations. Implementations Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/ClassTransformation.java URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/ClassTransformation.java?rev=395987&r1=395986&r2=395987&view=diff ============================================================================== --- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/ClassTransformation.java (original) +++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/ClassTransformation.java Fri Apr 21 11:59:10 2006 @@ -131,6 +131,19 @@ String newField(int modifiers, String type, String suggestedName); /** + * Defines a new protected instance variable whose initial value is provided statically. + * + * @param type + * the type of object to inject + * @param suggestedName + * the suggested name for the new field + * @param value + * to be injected. This value is retained. + * @return the actual name of the injected field + */ + String addInjectedField(Class type, String suggestedName, Object value); + + /** * Transforms the class to implement the indicated interface. If the class (or its super class) * does not already implement the interface, then the interface is added, and default * implementations of any methods of the interface are added. @@ -146,8 +159,9 @@ void addImplementedInterface(Class interfaceClass); /** - * Extends an existing method. The body of the method is replaced with the provided body. - * However, a call to the Javassist pseudo-method $proceed() is required. + * Extends an existing method. The provided method body is inserted at the end of the existing + * method (i.e. [EMAIL PROTECTED] javassist.CtBehavior#insertAfter(java.lang.String)}). To access or + * change the return value, use the <code>$_</code> pseudo variable. * * @param signature * the signature of the method to extend @@ -158,4 +172,32 @@ * Javassist method body can not be compiled */ void extendMethod(MethodSignature methodSignature, String methodBody); + + /** + * Stores meta data about the transformation. This is how workers can communicate; earlier + * workers may record meta data needed by later workers. + * + * @throws IllegalArgumentException + * if the provided key has already been stored + */ + void storeMeta(String key, String value); + + /* + * Retrieves previously stored meta data. @return value for key, or null if not found. + */ + + String readMeta(String key); + + /** + * Adds a getter method, a public instance method that returns the value of a field. This is + * often paired with [EMAIL PROTECTED] #addInjectedField(Class, String, Object)}. + * + * @param the + * return type of the method + * @param methodName + * the name of the method (i.e., "getFoo") + * @param fieldName + * the field to return + */ + void addGetterMethod(String type, String methodName, String fieldName); } Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/MethodSignature.java URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/MethodSignature.java?rev=395987&r1=395986&r2=395987&view=diff ============================================================================== --- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/MethodSignature.java (original) +++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/MethodSignature.java Fri Apr 21 11:59:10 2006 @@ -14,11 +14,10 @@ package org.apache.tapestry.transform; -import static org.apache.tapestry.util.Defense.notBlank; -import static org.apache.tapestry.util.Defense.notNull; - import java.lang.reflect.Modifier; +import static org.apache.tapestry.util.Defense.notBlank; + /** * A representation of a method signature, which consists of its name, modifiers (primarily, * visibility), return type, parameter types, and declared exception types. @@ -63,8 +62,13 @@ // TODO: Checks that no element within the two arrays // is null or blank. - _parameterTypes = notNull(parameterTypes, "parameterTypes"); - _exceptionTypes = notNull(exceptionTypes, "exceptionTypes"); + _parameterTypes = typeNamesOrEmpty(parameterTypes); + _exceptionTypes = typeNamesOrEmpty(exceptionTypes); + } + + private String[] typeNamesOrEmpty(String[] types) + { + return types == null ? EMPTY_STRINGS : types; } /** Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/TransformConstants.java URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/TransformConstants.java?rev=395987&view=auto ============================================================================== --- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/TransformConstants.java (added) +++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/TransformConstants.java Fri Apr 21 11:59:10 2006 @@ -0,0 +1,31 @@ +package org.apache.tapestry.transform; + +/** + * Constants used by implementations of [EMAIL PROTECTED] org.apache.tapestry.transform.ClassTransformWorker}. + * + * @author Howard M. Lewis Ship + */ +public final class TransformConstants +{ + /** + * Meta key used to get the name of the field which will contain the + * [EMAIL PROTECTED] org.apache.tapestry.ComponentResources} instance for the component (which is passed + * into the component instance via its constructor). + */ + public static final String RESOURCES_FIELD_NAME_KEY = "org.apache.tapestry.resources-field-name-key"; + + /** Signature for [EMAIL PROTECTED] org.apache.tapestry.events.ComponentLifecycle#containingPageDidLoad()}. */ + public static final MethodSignature CONTAINING_PAGE_DID_LOAD_SIGNATURE = new MethodSignature( + "containingPageDidLoad"); + + /** + * Signature for [EMAIL PROTECTED] org.apache.tapestry.events.ComponentLifecycle#containingPageDidDetach()}. + */ + public static final MethodSignature CONTAINING_PAGE_DID_DETACH = new MethodSignature( + "containingPageDidDetach"); + + /** Prevent instantiation. */ + private TransformConstants() + { + } +} Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/transform/TransformStrings.properties URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/transform/TransformStrings.properties?rev=395987&r1=395986&r2=395987&view=diff ============================================================================== --- tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/transform/TransformStrings.properties (original) +++ tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/transform/TransformStrings.properties Fri Apr 21 11:59:10 2006 @@ -15,4 +15,6 @@ no-constructor-found=Unable to find an applicable constructor for class {0}. missing-declared-field=Class {0} does not contain a field named ''{1}''. error-adding-method=Error adding method {1} to class {0}: {2} -field-already-claimed=Field {0} of class {1} is already claimed by {2} and can not be claimed by {3}. \ No newline at end of file +field-already-claimed=Field {0} of class {1} is already claimed by {2} and can not be claimed by {3}. +meta-key-exists=Class transformation meta key ''{0}'' already contains value ''{1}'' and can not be set to ''{2}''. +no-declared-method=Class {1} does not declare method ''{0}''. \ No newline at end of file Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/InternalClassTransformationImplTest.java URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/InternalClassTransformationImplTest.java?rev=395987&r1=395986&r2=395987&view=diff ============================================================================== --- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/InternalClassTransformationImplTest.java (original) +++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/InternalClassTransformationImplTest.java Fri Apr 21 11:59:10 2006 @@ -16,7 +16,7 @@ import java.lang.annotation.Documented; import java.lang.annotation.Target; -import java.lang.reflect.Method; +import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.List; @@ -27,15 +27,20 @@ import javassist.Loader; import javassist.NotFoundException; +import org.apache.tapestry.ResourceAware; import org.apache.tapestry.annotations.ComponentClass; import org.apache.tapestry.annotations.Retain; +import org.apache.tapestry.internal.InternalComponentResources; import org.apache.tapestry.internal.transform.pages.AbstractFoo; import org.apache.tapestry.internal.transform.pages.BarImpl; import org.apache.tapestry.internal.transform.pages.ChildClassInheritsAnnotation; import org.apache.tapestry.internal.transform.pages.ClaimedFields; import org.apache.tapestry.internal.transform.pages.ParentClass; import org.apache.tapestry.internal.transform.pages.TargetObject; +import org.apache.tapestry.internal.transform.worker.ImplementResourceAwareWorker; +import org.apache.tapestry.model.MutableComponentModel; import org.apache.tapestry.test.TestBase; +import org.apache.tapestry.transform.ClassTransformWorker; import org.apache.tapestry.transform.ClassTransformation; import org.testng.annotations.Configuration; import org.testng.annotations.Test; @@ -45,6 +50,7 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertSame; import static org.testng.Assert.assertTrue; /** @@ -95,7 +101,7 @@ assertEquals(ct.newMemberName("conflictMethod"), "_$conflictMethod_0"); } - private ClassTransformation createClassTransformation(Class targetClass) + private InternalClassTransformation createClassTransformation(Class targetClass) throws NotFoundException { CtClass ctClass = findCtClass(targetClass); @@ -103,6 +109,30 @@ } @Test + public void meta() throws Exception + { + ClassTransformation ct = createClassTransformation(ParentClass.class); + + assertNull(ct.readMeta("foo.bar")); + + ct.storeMeta("foo.bar", "baz"); + + assertEquals(ct.readMeta("foo.bar"), "baz"); + + try + { + ct.storeMeta("foo.bar", "Biff"); + unreachable(); + } + catch (IllegalArgumentException ex) + { + assertEquals( + ex.getMessage(), + "Class transformation meta key 'foo.bar' already contains value 'baz' and can not be set to 'Biff'."); + } + } + + @Test public void findAnnotationOnUnknownField() throws Exception { ClassTransformation ct = createClassTransformation(ParentClass.class); @@ -303,9 +333,53 @@ } @Test + public void injectedField() throws Exception + { + MutableComponentModel model = newMock(MutableComponentModel.class); + InternalComponentResources resources = newMock(InternalComponentResources.class); + + ClassLoader childLoader = newLoader(); + + CtClass targetObjectCtClass = findCtClass(TargetObject.class); + + InternalClassTransformation ct = new InternalClassTransformationImpl(targetObjectCtClass); + + replay(); + + ClassTransformWorker worker = new ImplementResourceAwareWorker(); + + worker.transform(ct, model); + + verify(); + + ct.finish(); + + Class modified = _classPool.toClass(targetObjectCtClass, childLoader); + + Constructor cons = modified.getConstructors()[0]; + + ResourceAware instance = (ResourceAware) cons.newInstance(resources); + + assertSame(instance.getResources(), resources); + } + + private Loader newLoader() + { + Loader loader = new Loader(_contextClassLoader, _classPool); + + // This ensures that only the classes we explicitly access and modify + // are loaded by the new loader; everthing else comes out of the common + // context class loader, which prevents a lot of nasty class cast exceptions. + + loader.delegateLoadingOf("org.apache.tapestry."); + + return loader; + } + + @Test public void addImplementedInterface() throws Exception { - ClassLoader childLoader = new Loader(_contextClassLoader, _classPool); + ClassLoader childLoader = newLoader(); CtClass targetObjectCtClass = findCtClass(TargetObject.class); @@ -330,28 +404,21 @@ Object target = modified.newInstance(); - invoke(target, "foo"); + FooInterface asFoo = (FooInterface) target; - assertEquals(invoke(target, "getBoolean"), false); - assertEquals(invoke(target, "getByte"), (byte) 0); - assertEquals(invoke(target, "getShort"), (short) 0); - assertEquals(invoke(target, "getInt"), 0); - assertEquals(invoke(target, "getLong"), 0l); - assertEquals(invoke(target, "getFloat"), 0.0f); - assertEquals(invoke(target, "getDouble"), 0.0d); - assertNull(invoke(target, "getString")); - assertNull(invoke(target, "getObjectArray")); - assertNull(invoke(target, "getIntArray")); - } + asFoo.foo(); - @SuppressWarnings("unchecked") - private <T> T invoke(Object target, String methodName) throws Exception - { - Class targetClass = target.getClass(); + GetterMethodsInterface getters = (GetterMethodsInterface) target; - Method method = targetClass.getMethod(methodName); - - return (T) method.invoke(target); + assertEquals(getters.getBoolean(), false); + assertEquals(getters.getByte(), (byte) 0); + assertEquals(getters.getShort(), (short) 0); + assertEquals(getters.getInt(), 0); + assertEquals(getters.getLong(), 0l); + assertEquals(getters.getFloat(), 0.0f); + assertEquals(getters.getDouble(), 0.0d); + assertNull(getters.getString()); + assertNull(getters.getObjectArray()); + assertNull(getters.getIntArray()); } - } Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/worker/UnclaimedFieldWorkerTest.java URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/worker/UnclaimedFieldWorkerTest.java?rev=395987&r1=395986&r2=395987&view=diff ============================================================================== --- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/worker/UnclaimedFieldWorkerTest.java (original) +++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/worker/UnclaimedFieldWorkerTest.java Fri Apr 21 11:59:10 2006 @@ -18,10 +18,10 @@ import org.apache.hivemind.service.BodyBuilder; import org.apache.tapestry.events.ComponentLifecycle; -import org.apache.tapestry.internal.transform.TransformConstants; import org.apache.tapestry.model.MutableComponentModel; import org.apache.tapestry.test.BaseTestCase; import org.apache.tapestry.transform.ClassTransformation; +import org.apache.tapestry.transform.TransformConstants; import org.testng.annotations.Test; /** --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]