You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2010/01/26 17:02:55 UTC

svn commit: r903293 - in /tapestry/tapestry5/trunk/tapestry-core/src: main/java/org/apache/tapestry5/internal/services/ main/java/org/apache/tapestry5/internal/transform/ main/java/org/apache/tapestry5/services/ main/resources/org/apache/tapestry5/inte...

Author: hlship
Date: Tue Jan 26 16:02:54 2010
New Revision: 903293

URL: http://svn.apache.org/viewvc?rev=903293&view=rev
Log:
Build new methods onto ClassTransformation to match fields and methods, returning TransformFields and TransformMethods
Add assignIndirect() methods to TransformField
Add extend() method to TransformMethod

Removed:
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/transform/MixinWorkerTest.java
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/BlockInjectionProvider.java
    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/services/ServicesMessages.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ComponentWorker.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/InjectContainerWorker.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/MixinWorker.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ClassTransformation.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/FieldFilter.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/MethodFilter.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformField.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformMethod.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/services/ServicesStrings.properties
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/TextOnlyOnDisabledTextField.java

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/BlockInjectionProvider.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/BlockInjectionProvider.java?rev=903293&r1=903292&r2=903293&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/BlockInjectionProvider.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/BlockInjectionProvider.java Tue Jan 26 16:02:54 2010
@@ -25,6 +25,7 @@
 import org.apache.tapestry5.services.ComponentValueProvider;
 import org.apache.tapestry5.services.InjectionProvider;
 import org.apache.tapestry5.services.TransformConstants;
+import org.apache.tapestry5.services.TransformMethod;
 
 /**
  * Identifies fields of type {@link Block} that have the {@link Inject} annotation and converts them
@@ -57,8 +58,10 @@
             }
         };
 
-        transformation.assignFieldIndirect(fieldName,
-                TransformConstants.CONTAINING_PAGE_DID_ATTACH_SIGNATURE, provider);
+        TransformMethod method = transformation
+                .getMethod(TransformConstants.CONTAINING_PAGE_DID_ATTACH_SIGNATURE);
+
+        transformation.getField(fieldName).assignIndirect(method, provider);
 
         return true; // claim the field
     }

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=903293&r1=903292&r2=903293&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 Tue Jan 26 16:02:54 2010
@@ -17,6 +17,7 @@
 import java.lang.annotation.Annotation;
 import java.lang.annotation.Inherited;
 import java.lang.reflect.Modifier;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Formatter;
 import java.util.List;
@@ -29,6 +30,7 @@
 
 import org.apache.tapestry5.ComponentResources;
 import org.apache.tapestry5.internal.InternalComponentResources;
+import org.apache.tapestry5.ioc.Predicate;
 import org.apache.tapestry5.ioc.internal.services.CtClassSource;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.internal.util.Defense;
@@ -93,6 +95,11 @@
 
         }
 
+        public int compareTo(TransformMethod o)
+        {
+            return sig.compareTo(o.getSignature());
+        }
+
         public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
         {
             if (annotations == null)
@@ -101,11 +108,27 @@
             return findAnnotationInList(annotationClass, annotations);
         }
 
-        @Override
         public TransformMethodSignature getSignature()
         {
             return sig;
         }
+
+        public void extend(String body)
+        {
+            failIfFrozen();
+
+            try
+            {
+                method.insertAfter(body);
+            }
+            catch (CannotCompileException ex)
+            {
+                throw new MethodCompileException(
+                        ServicesMessages.methodCompileError(sig, body, ex), body, ex);
+            }
+
+            addMethodToDescription("extend", sig, body);
+        }
     }
 
     class TransformFieldImpl implements TransformField
@@ -142,6 +165,11 @@
             primitive = ClassFabUtils.isPrimitiveType(type);
         }
 
+        public int compareTo(TransformField o)
+        {
+            return name.compareTo(o.getName());
+        }
+
         public String getName()
         {
             return name;
@@ -228,6 +256,40 @@
 
             removeField(name);
         }
+
+        public <T> void assignIndirect(TransformMethod method, ComponentValueProvider<T> provider)
+        {
+            Defense.notNull(method, "method");
+            Defense.notNull(provider, "provider");
+
+            String providerFieldName = addInjectedField(ComponentValueProvider.class, name
+                    + "$provider", provider);
+
+            CtClass fieldType = null;
+
+            try
+            {
+                fieldType = field.getType();
+            }
+            catch (NotFoundException ex)
+            {
+                throw new RuntimeException(ex);
+            }
+
+            String body = String.format("%s = (%s) %s.get(%s);", name, fieldType.getName(),
+                    providerFieldName, resourcesFieldName);
+
+            extendMethod(method.getSignature(), body);
+
+            makeReadOnly(name);
+        }
+
+        @Override
+        public <T> void assignIndirect(TransformMethodSignature signature,
+                ComponentValueProvider<T> provider)
+        {
+            assignIndirect(getMethod(signature), provider);
+        }
     }
 
     private final Map<TransformMethodSignature, TransformMethodImpl> methods = CollectionFactory
@@ -485,17 +547,7 @@
     public <T extends Annotation> T getMethodAnnotation(TransformMethodSignature signature,
             Class<T> annotationClass)
     {
-        failIfFrozen();
-
-        CtMethod method = findDeclaredOrSuperclassMethod(signature);
-
-        if (method == null)
-            throw new IllegalArgumentException(ServicesMessages
-                    .noDeclaredMethod(ctClass, signature));
-
-        List<Annotation> annotations = findMethodAnnotations(method);
-
-        return findAnnotationInList(annotationClass, annotations);
+        return getMethod(signature).getAnnotation(annotationClass);
     }
 
     /**
@@ -527,20 +579,6 @@
         return findAnnotationInList(annotationClass, getClassAnnotations());
     }
 
-    private List<Annotation> findMethodAnnotations(CtMethod method)
-    {
-        List<Annotation> annotations = methodAnnotations.get(method);
-
-        if (annotations == null)
-        {
-            annotations = extractAnnotations(method);
-
-            methodAnnotations.put(method, annotations);
-        }
-
-        return annotations;
-    }
-
     private List<Annotation> extractAnnotations(CtMember member)
     {
         try
@@ -713,11 +751,13 @@
         }
     }
 
-    private void recordMethod(CtMethod method, boolean asNew)
+    private TransformMethodImpl recordMethod(CtMethod method, boolean asNew)
     {
         TransformMethodImpl tmi = new TransformMethodImpl(method, asNew);
 
         methods.put(tmi.getSignature(), tmi);
+
+        return tmi;
     }
 
     /**
@@ -891,40 +931,12 @@
 
     public void extendMethod(TransformMethodSignature methodSignature, String methodBody)
     {
-        failIfFrozen();
-
-        CtMethod method = findDeclaredOrSuperclassMethod(methodSignature);
-
-        try
-        {
-            method.insertAfter(methodBody);
-        }
-        catch (CannotCompileException ex)
-        {
-            throw new MethodCompileException(ServicesMessages.methodCompileError(methodSignature,
-                    methodBody, ex), methodBody, ex);
-        }
-
-        addMethodToDescription("extend", methodSignature, methodBody);
+        getMethod(methodSignature).extend(methodBody);
     }
 
     public void extendExistingMethod(TransformMethodSignature methodSignature, String methodBody)
     {
-        failIfFrozen();
-
-        CtMethod method = findDeclaredOrSuperclassMethod(methodSignature);
-
-        try
-        {
-            method.insertAfter(methodBody);
-        }
-        catch (CannotCompileException ex)
-        {
-            throw new MethodCompileException(ServicesMessages.methodCompileError(methodSignature,
-                    methodBody, ex), methodBody, ex);
-        }
-
-        addMethodToDescription("extend existing", methodSignature, methodBody);
+        extendMethod(methodSignature, methodBody);
     }
 
     public void copyMethod(TransformMethodSignature sourceMethod, int modifiers,
@@ -935,7 +947,10 @@
         CtClass returnType = findCtClass(sourceMethod.getReturnType());
         CtClass[] parameters = buildCtClassList(sourceMethod.getParameterTypes());
         CtClass[] exceptions = buildCtClassList(sourceMethod.getExceptionTypes());
-        CtMethod source = findDeclaredOrSuperclassMethod(sourceMethod);
+
+        TransformMethodImpl tmi = findOrOverrideMethod(sourceMethod);
+
+        CtMethod source = tmi.method;
 
         try
         {
@@ -971,12 +986,12 @@
     {
         failIfFrozen();
 
-        CtMethod method = findDeclaredOrSuperclassMethod(methodSignature);
+        TransformMethodImpl tmi = findOrOverrideMethod(methodSignature);
         CtClass exceptionCtType = findCtClass(exceptionType);
 
         try
         {
-            method.addCatch(body, exceptionCtType);
+            tmi.method.addCatch(body, exceptionCtType);
         }
         catch (CannotCompileException ex)
         {
@@ -991,11 +1006,11 @@
     {
         failIfFrozen();
 
-        CtMethod method = findDeclaredOrSuperclassMethod(methodSignature);
+        TransformMethodImpl tmi = findOrOverrideMethod(methodSignature);
 
         try
         {
-            method.insertBefore(methodBody);
+            tmi.method.insertBefore(methodBody);
         }
         catch (CannotCompileException ex)
         {
@@ -1037,33 +1052,35 @@
         formatter.format("\n%s\n\n", methodBody);
     }
 
-    private CtMethod findDeclaredOrSuperclassMethod(TransformMethodSignature methodSignature)
+    public TransformMethod getMethod(TransformMethodSignature signature)
     {
-        CtMethod method = findDeclaredMethod(methodSignature);
+        failIfFrozen();
 
-        if (method != null)
-            return method;
+        return findOrOverrideMethod(signature);
+    }
 
-        CtMethod result = addOverrideOfSuperclassMethod(methodSignature);
+    private TransformMethodImpl findOrOverrideMethod(TransformMethodSignature signature)
+    {
+        TransformMethodImpl result = methods.get(signature);
 
         if (result != null)
             return result;
 
-        throw new IllegalArgumentException(ServicesMessages.noDeclaredMethod(ctClass,
-                methodSignature));
-    }
+        result = addOverrideOfSuperclassMethod(signature);
 
-    private CtMethod findDeclaredMethod(TransformMethodSignature methodSignature)
-    {
-        TransformMethodImpl tmi = methods.get(methodSignature);
+        if (result != null)
+            return result;
+
+        throw new IllegalArgumentException(String.format("Class %s does not declare method '%s'.",
+                ctClass.getName(), signature));
 
-        return tmi == null ? null : tmi.method;
     }
 
     // TODO: Rework this method for efficiency, i.e., so that we can leverage the methods
     // map in parent InternalClassTransformImpls, rather than the exhaustive
     // search.
-    private CtMethod addOverrideOfSuperclassMethod(TransformMethodSignature methodSignature)
+    private TransformMethodImpl addOverrideOfSuperclassMethod(
+            TransformMethodSignature methodSignature)
     {
         try
         {
@@ -1073,15 +1090,14 @@
                 {
                     if (match(method, methodSignature))
                     {
-                        // TODO: If the method is not overridable (i.e. private, or final)?
+                        // TODO: What if the method is not overridable (i.e. private, or final)?
                         // Perhaps we should limit it to just public methods.
 
                         CtMethod newMethod = CtNewMethod.delegator(method, ctClass);
                         ctClass.addMethod(newMethod);
 
-                        recordMethod(newMethod, true);
-
-                        return newMethod;
+                        // Record it as a new method.
+                        return recordMethod(newMethod, true);
                     }
                 }
             }
@@ -1150,7 +1166,7 @@
         return findFields(filter);
     }
 
-    public List<String> findFields(FieldFilter filter)
+    public List<String> findFields(final FieldFilter filter)
     {
         failIfFrozen();
 
@@ -1169,48 +1185,67 @@
         return result;
     }
 
-    public List<TransformMethodSignature> findMethodsWithAnnotation(
-            final Class<? extends Annotation> annotationClass)
+    public List<TransformField> matchFields(Predicate<TransformField> predicate)
     {
         failIfFrozen();
 
-        List<TransformMethodSignature> result = CollectionFactory.newList();
+        return InternalUtils.matchAndSort(fields.values(), predicate);
+    }
 
-        for (CtMethod method : ctClass.getDeclaredMethods())
+    @Override
+    public List<TransformField> matchFieldsWithAnnotation(
+            final Class<? extends Annotation> annotationClass)
+    {
+        return matchFields(new Predicate<TransformField>()
         {
-            List<Annotation> annotations = findMethodAnnotations(method);
-
-            if (findAnnotationInList(annotationClass, annotations) != null)
+            @Override
+            public boolean accept(TransformField field)
             {
-                TransformMethodSignature sig = getMethodSignature(method);
-                result.add(sig);
+                return field.getAnnotation(annotationClass) != null;
             }
-        }
+        });
+    }
 
-        Collections.sort(result);
+    public List<TransformMethodSignature> findMethodsWithAnnotation(
+            final Class<? extends Annotation> annotationClass)
+    {
+        List<TransformMethod> methods = matchMethods(new Predicate<TransformMethod>()
+        {
+            public boolean accept(TransformMethod method)
+            {
+                return method.getAnnotation(annotationClass) != null;
+            };
+        });
 
-        return result;
+        return toMethodSignatures(methods);
     }
 
-    public List<TransformMethodSignature> findMethods(MethodFilter filter)
+    public List<TransformMethodSignature> findMethods(final MethodFilter filter)
     {
         Defense.notNull(filter, "filter");
 
-        List<TransformMethodSignature> result = CollectionFactory.newList();
-
-        for (TransformMethodSignature sig : methods.keySet())
+        List<TransformMethod> methods = matchMethods(new Predicate<TransformMethod>()
         {
-            if (filter.accept(sig))
-                result.add(sig);
-        }
+            public boolean accept(TransformMethod object)
+            {
+                return filter.accept(object.getSignature());
+            };
+        });
 
-        Collections.sort(result);
+        return toMethodSignatures(methods);
+    }
 
-        return result;
+    public List<TransformMethod> matchMethods(final Predicate<TransformMethod> predicate)
+    {
+        failIfFrozen();
+
+        return InternalUtils.matchAndSort(methods.values(), predicate);
     }
 
     private TransformMethodSignature getMethodSignature(CtMethod method)
     {
+        failIfFrozen();
+
         TransformMethodSignature result = methodSignatures.get(method);
         if (result == null)
         {
@@ -1393,7 +1428,7 @@
     {
         Defense.notNull(type, "type");
         Defense.notNull(provider, "provider");
-        
+
         TransformField field = addTransformField(Modifier.PRIVATE | Modifier.FINAL, type.getName(),
                 suggestedName);
 
@@ -1503,7 +1538,7 @@
     {
         Defense.notNull(signature, "signature");
 
-        return findDeclaredMethod(signature) != null;
+        return methods.containsKey(signature);
     }
 
     /**
@@ -2072,7 +2107,8 @@
     {
         Defense.notNull(signature, "signature");
 
-        CtMethod method = findDeclaredOrSuperclassMethod(signature);
+        TransformMethodImpl tmi = findOrOverrideMethod(signature);
+        CtMethod method = tmi.method;
 
         int lineNumber = method.getMethodInfo2().getLineNumber(0);
         CtClass enclosingClass = method.getDeclaringClass();
@@ -2103,23 +2139,27 @@
         return "$" + constructorArgs.size();
     }
 
-    public <T> void assignFieldIndirect(String fieldName, TransformMethodSignature methodSig,
-            ComponentValueProvider<T> provider)
+    private static List<TransformMethodSignature> toMethodSignatures(List<TransformMethod> input)
     {
-        Defense.notBlank(fieldName, "fieldName");
-        Defense.notNull(methodSig, "methodSig");
-        Defense.notNull(provider, "provider");
+        List<TransformMethodSignature> result = CollectionFactory.newList();
 
-        String providerFieldName = addInjectedField(ComponentValueProvider.class, fieldName
-                + "$provider", provider);
+        for (TransformMethod m : input)
+        {
+            result.add(m.getSignature());
+        }
 
-        CtClass fieldType = getFieldCtType(fieldName);
+        return result;
+    }
 
-        String body = String.format("%s = (%s) %s.get(%s);", fieldName, fieldType.getName(),
-                providerFieldName, resourcesFieldName);
+    private static List<String> toFieldNames(List<TransformField> fields)
+    {
+        List<String> result = CollectionFactory.newList();
 
-        extendMethod(methodSig, body);
+        for (TransformField f : fields)
+        {
+            result.add(f.getName());
+        }
 
-        makeReadOnly(fieldName);
+        return result;
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java?rev=903293&r1=903292&r2=903293&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java Tue Jan 26 16:02:54 2010
@@ -52,11 +52,6 @@
         return MESSAGES.format("error-adding-method", ctClass.getName(), methodName, cause);
     }
 
-    static String noDeclaredMethod(CtClass ctClass, TransformMethodSignature methodSignature)
-    {
-        return MESSAGES.format("no-declared-method", ctClass.getName(), methodSignature);
-    }
-
     static String classNotTransformed(String className)
     {
         return MESSAGES.format("class-not-transformed", className);

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ComponentWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ComponentWorker.java?rev=903293&r1=903292&r2=903293&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ComponentWorker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ComponentWorker.java Tue Jan 26 16:02:54 2010
@@ -35,12 +35,13 @@
 import org.apache.tapestry5.services.ComponentClassTransformWorker;
 import org.apache.tapestry5.services.ComponentValueProvider;
 import org.apache.tapestry5.services.TransformConstants;
+import org.apache.tapestry5.services.TransformField;
 
 /**
  * Finds fields with the {@link org.apache.tapestry5.annotations.Component} annotation and updates
  * the model. Also
- * checks for the {@link Mixins} and {@link MixinClasses} annotations and uses them to update the
- * {@link ComponentModel}.
+ * checks for the {@link Mixins} and {@link MixinClasses} annotations and uses them to update the {@link ComponentModel}
+ * .
  */
 public class ComponentWorker implements ComponentClassTransformWorker
 {
@@ -53,18 +54,20 @@
 
     public void transform(ClassTransformation transformation, MutableComponentModel model)
     {
-        for (String fieldName : transformation.findFieldsWithAnnotation(Component.class))
+        for (TransformField field : transformation.matchFieldsWithAnnotation(Component.class))
         {
-            Component annotation = transformation.getFieldAnnotation(fieldName, Component.class);
+            Component annotation = field.getAnnotation(Component.class);
 
-            transformation.claimField(fieldName, annotation);
+            field.claim(annotation);
 
             String annotationId = annotation.id();
 
+            String fieldName = field.getName();
+
             final String id = InternalUtils.isNonBlank(annotationId) ? annotationId : InternalUtils
                     .stripMemberName(fieldName);
 
-            String type = transformation.getFieldType(fieldName);
+            String type = field.getType();
 
             Location location = new StringLocation(String.format("%s.%s", transformation
                     .getClassName(), fieldName), 0);
@@ -82,25 +85,23 @@
             }
 
             ComponentValueProvider<Object> provider = new ComponentValueProvider<Object>()
-            {                
+            {
                 public Object get(ComponentResources resources)
                 {
                     return resources.getEmbeddedComponent(id);
                 }
             };
 
-            transformation.assignFieldIndirect(fieldName,
-                    TransformConstants.CONTAINING_PAGE_DID_LOAD_SIGNATURE, provider);
+            field.assignIndirect(TransformConstants.CONTAINING_PAGE_DID_LOAD_SIGNATURE, provider);
 
-            addMixinClasses(fieldName, transformation, embedded);
-            addMixinTypes(fieldName, transformation, embedded);
+            addMixinClasses(field, embedded);
+            addMixinTypes(field, embedded);
         }
     }
 
-    private void addMixinClasses(String fieldName, ClassTransformation transformation,
-            MutableEmbeddedComponentModel model)
+    private void addMixinClasses(TransformField field, MutableEmbeddedComponentModel model)
     {
-        MixinClasses annotation = transformation.getFieldAnnotation(fieldName, MixinClasses.class);
+        MixinClasses annotation = field.getAnnotation(MixinClasses.class);
 
         if (annotation == null)
             return;
@@ -109,7 +110,7 @@
 
         if (!orderEmpty && annotation.order().length != annotation.value().length)
             throw new TapestryException(TransformMessages.badMixinConstraintLength(annotation,
-                    fieldName), model, null);
+                    field.getName()), model, null);
 
         for (int i = 0; i < annotation.value().length; i++)
         {
@@ -120,10 +121,9 @@
         }
     }
 
-    private void addMixinTypes(String fieldName, ClassTransformation transformation,
-            MutableEmbeddedComponentModel model)
+    private void addMixinTypes(TransformField field, MutableEmbeddedComponentModel model)
     {
-        Mixins annotation = transformation.getFieldAnnotation(fieldName, Mixins.class);
+        Mixins annotation = field.getAnnotation(Mixins.class);
 
         if (annotation == null)
             return;

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/InjectContainerWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/InjectContainerWorker.java?rev=903293&r1=903292&r2=903293&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/InjectContainerWorker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/InjectContainerWorker.java Tue Jan 26 16:02:54 2010
@@ -14,8 +14,6 @@
 
 package org.apache.tapestry5.internal.transform;
 
-import java.util.List;
-
 import org.apache.tapestry5.ComponentResources;
 import org.apache.tapestry5.annotations.InjectContainer;
 import org.apache.tapestry5.internal.services.ComponentClassCache;
@@ -25,6 +23,7 @@
 import org.apache.tapestry5.services.ComponentClassTransformWorker;
 import org.apache.tapestry5.services.ComponentValueProvider;
 import org.apache.tapestry5.services.TransformConstants;
+import org.apache.tapestry5.services.TransformField;
 
 /**
  * Identifies the {@link org.apache.tapestry5.annotations.InjectContainer} annotation and adds code
@@ -42,11 +41,12 @@
 
     public void transform(ClassTransformation transformation, MutableComponentModel model)
     {
-        List<String> names = transformation.findFieldsWithAnnotation(InjectContainer.class);
-
-        for (final String fieldName : names)
+        for (final TransformField field : transformation
+                .matchFieldsWithAnnotation(InjectContainer.class))
         {
-            final String fieldTypeName = transformation.getFieldType(fieldName);
+            final String fieldName = field.getName();
+
+            final String fieldTypeName = field.getType();
 
             final String componentClassName = model.getComponentClassName();
 
@@ -72,8 +72,7 @@
                 }
             };
 
-            transformation.assignFieldIndirect(fieldName,
-                    TransformConstants.CONTAINING_PAGE_DID_LOAD_SIGNATURE, provider);
+            field.assignIndirect(TransformConstants.CONTAINING_PAGE_DID_LOAD_SIGNATURE, provider);
         }
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/MixinWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/MixinWorker.java?rev=903293&r1=903292&r2=903293&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/MixinWorker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/MixinWorker.java Tue Jan 26 16:02:54 2010
@@ -14,8 +14,6 @@
 
 package org.apache.tapestry5.internal.transform;
 
-import java.util.List;
-
 import org.apache.tapestry5.ComponentResources;
 import org.apache.tapestry5.annotations.Mixin;
 import org.apache.tapestry5.internal.InternalComponentResources;
@@ -26,6 +24,7 @@
 import org.apache.tapestry5.services.ComponentClassTransformWorker;
 import org.apache.tapestry5.services.ComponentValueProvider;
 import org.apache.tapestry5.services.TransformConstants;
+import org.apache.tapestry5.services.TransformField;
 
 /**
  * Supports the {@link org.apache.tapestry5.annotations.Mixin} annotation, which allows a mixin to
@@ -45,19 +44,17 @@
 
     public void transform(ClassTransformation transformation, MutableComponentModel model)
     {
-        List<String> fields = transformation.findFieldsWithAnnotation(Mixin.class);
-
-        for (String fieldName : fields)
+        for (TransformField field : transformation.matchFieldsWithAnnotation(Mixin.class))
         {
-            Mixin annotation = transformation.getFieldAnnotation(fieldName, Mixin.class);
+            Mixin annotation = field.getAnnotation(Mixin.class);
 
-            transformation.claimField(fieldName, annotation);
+            field.claim(annotation);
 
             String mixinType = annotation.value();
 
             String[] order = annotation.order();
 
-            String fieldType = transformation.getFieldType(fieldName);
+            String fieldType = field.getType();
 
             final String mixinClassName = InternalUtils.isBlank(mixinType) ? fieldType : resolver
                     .resolveMixinTypeToClassName(mixinType);
@@ -74,8 +71,7 @@
                 }
             };
 
-            transformation.assignFieldIndirect(fieldName,
-                    TransformConstants.CONTAINING_PAGE_DID_LOAD_SIGNATURE, provider);
+            field.assignIndirect(TransformConstants.CONTAINING_PAGE_DID_LOAD_SIGNATURE, provider);
         }
     }
 }

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=903293&r1=903292&r2=903293&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 Tue Jan 26 16:02:54 2010
@@ -14,14 +14,17 @@
 
 package org.apache.tapestry5.services;
 
+import java.lang.annotation.Annotation;
+import java.util.List;
+
 import javassist.CtBehavior;
 
 import org.apache.tapestry5.ComponentResources;
 import org.apache.tapestry5.ioc.AnnotationProvider;
+import org.apache.tapestry5.ioc.Predicate;
 import org.slf4j.Logger;
 
-import java.lang.annotation.Annotation;
-import java.util.List;
+import com.sun.source.tree.MethodTree;
 
 /**
  * Contains class-specific information used when transforming a raw component class into an
@@ -87,10 +90,20 @@
      * Generates a list of the names of declared instance fields that have the indicated annotation.
      * Non-private and
      * static fields are ignored. Only the names of private instance fields are returned.
+     * 
+     * @deprecated Use {@link #matchFieldsWithAnnotation(Class)} instead
      */
     List<String> findFieldsWithAnnotation(Class<? extends Annotation> annotationClass);
 
     /**
+     * Returns a sorted list of declared instance fields with the indicated annotation. Non-private
+     * and static fields are ignored. Claimed fields are also ignored.
+     * 
+     * @since 5.2.0
+     */
+    List<TransformField> matchFieldsWithAnnotation(Class<? extends Annotation> annotationClass);
+
+    /**
      * Finds all methods defined in the class that are marked with the provided annotation.
      * 
      * @param annotationClass
@@ -108,20 +121,42 @@
      * @return a list of matching method signatures (which may be empty) in ascending order (by
      *         method name), but
      *         descending order (by parameter count) within overrides of a single method name.
+     * @deprecated Use {@link #matchMethods(Predicate)} instead
      */
     List<TransformMethodSignature> findMethods(MethodFilter filter);
 
     /**
+     * Finds all methods matched by the provided predicate.
+     * 
+     * @param predicate
+     *            Used to filter the list
+     * @return a list of matching methods (which may be empty) in ascending order (by
+     *         method name), but descending order (by parameter count) within overrides of a single method name.
+     */
+    List<TransformMethod> matchMethods(Predicate<TransformMethod> predicate);
+
+    /**
      * Finds all unclaimed fields matched by the provided filter. Only considers private instance
      * fields.
      * 
      * @param filter
      *            passed each field name and field type
      * @return the names of all matched fields, in ascending order
+     * @deprecated Use {@link #matchFields(Predicate)} instead
      */
     List<String> findFields(FieldFilter filter);
 
     /**
+     * Finds all unclaimed fields matched by the provided predicate. Only considers instance fields.
+     * 
+     * @param predicate
+     *            used for matching
+     * @return sorted list of matching fields
+     * @since 5.2.0
+     */
+    List<TransformField> matchFields(Predicate<TransformField> predicate);
+
+    /**
      * Finds an annotation on a declared instance field.
      * 
      * @param <T>
@@ -342,14 +377,15 @@
      *            the body of code
      * @throws org.apache.tapestry5.internal.services.MethodCompileException
      *             if the provided Javassist method body can not be compiled
-     * @see #extendExistingMethod(TransformMethodSignature, String)
+     * @deprecated Use {@link TransformMethod#extend(String)} instead
      */
     void extendMethod(TransformMethodSignature methodSignature, String methodBody);
 
     /**
      * Like {@link #extendMethod(TransformMethodSignature, String)}, but the extension does not mark
      * the method as new,
-     * and field changes <em>will</em> be processed.
+     * and field changes <em>will</em> be processed. Note: at some point, this is not longer true; extend and
+     * extendMethod work identically.
      * 
      * @param methodSignature
      *            signature of the method to extend
@@ -358,6 +394,7 @@
      * @throws org.apache.tapestry5.internal.services.MethodCompileException
      *             if the provided method body can not be compiled
      * @see #prefixMethod(TransformMethodSignature, String)
+     * @deprecated Use {@link TransformMethod#extend(String) instead}
      */
     void extendExistingMethod(TransformMethodSignature methodSignature, String methodBody);
 
@@ -510,21 +547,16 @@
     boolean isMethodOverride(TransformMethodSignature methodSignature);
 
     /**
-     * Uses {@link #extendMethod(TransformMethodSignature, String)} to make an assignment to
-     * a field within the provided method. In addition, the field is marked as read-only. This
-     * is an alternative to {@link #injectFieldIndirect(String, ComponentValueProvider)} for values
-     * that <em>can not</em> be calculated at the constructor.
+     * Locates and returns the method if declared in this class; if not, locates
+     * a super-class method that is implemented into this class. In the latter
+     * case, the method will be considered "new" (i.e., no field transformations),
+     * as with {@link #extendMethod(TransformMethodSignature, String)}).
      * 
-     * @param <T>
-     * @param fieldName
-     *            name of field to assign
-     * @param methodSig
-     *            identifies the method where the assignment will occur, often this is
-     *            {@link TransformConstants#CONTAINING_PAGE_DID_LOAD_SIGNATURE}
-     * @param provider
-     *            provides the value of the field
+     * @param signature
+     *            identifies the method to obtain
+     * @throw RuntimeException if the signature does not match a declared method of this
+     *        class, or a overridable method of a superclass
      * @since 5.2.0
      */
-    <T> void assignFieldIndirect(String fieldName, TransformMethodSignature methodSig,
-            ComponentValueProvider<T> provider);
- }
+    TransformMethod getMethod(TransformMethodSignature signature);
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/FieldFilter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/FieldFilter.java?rev=903293&r1=903292&r2=903293&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/FieldFilter.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/FieldFilter.java Tue Jan 26 16:02:54 2010
@@ -1,4 +1,4 @@
-// Copyright 2007 The Apache Software Foundation
+// Copyright 2007, 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.
@@ -14,8 +14,12 @@
 
 package org.apache.tapestry5.services;
 
+import org.apache.tapestry5.ioc.Predicate;
+
 /**
  * Used by {@link ClassTransformation#findFields(FieldFilter)} to identify which fields to keep.
+ * 
+ * @deprecated New APIs use {@link Predicate} instead
  */
 public interface FieldFilter
 {

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/MethodFilter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/MethodFilter.java?rev=903293&r1=903292&r2=903293&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/MethodFilter.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/MethodFilter.java Tue Jan 26 16:02:54 2010
@@ -1,10 +1,10 @@
-// Copyright 2006, 2007 The Apache Software Foundation
+// Copyright 2006, 2007, 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,8 +14,12 @@
 
 package org.apache.tapestry5.services;
 
+import org.apache.tapestry5.ioc.Predicate;
+
 /**
  * Used by {@link ClassTransformation#findMethods(MethodFilter)} to accept or reject each method.
+ * 
+ * @deprecated New APIs use {@link Predicate} instead
  */
 public interface MethodFilter
 {

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformField.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformField.java?rev=903293&r1=903292&r2=903293&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformField.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformField.java Tue Jan 26 16:02:54 2010
@@ -23,7 +23,7 @@
  * 
  * @since 5.2.0
  */
-public interface TransformField extends AnnotationProvider
+public interface TransformField extends AnnotationProvider, Comparable<TransformField>
 {
     /**
      * Returns the name of the field.
@@ -72,4 +72,25 @@
      *            identifies the field containing (via injection) an instance of {@link FieldValueConduit}
      */
     void replaceAccess(TransformField conduitField);
+
+    /**
+     * Extends the indicated method to add an assignment of the field with
+     * the value obtained by the provider. This is used when a value
+     * to be provided can not be provided from within the transformed class'
+     * constructor.
+     * 
+     * @param <T>
+     * @param method
+     *            identifies the method where the assignment will occur, often this is
+     *            {@link TransformConstants#CONTAINING_PAGE_DID_LOAD_SIGNATURE}
+     * @param provider
+     *            provides the value of the field
+     */
+    <T> void assignIndirect(TransformMethod method, ComponentValueProvider<T> provider);
+
+    /**
+     * Alternate version of {@link #assignIndirect(TransformMethod, ComponentValueProvider)} that operates using a
+     * method signature.
+     */
+    <T> void assignIndirect(TransformMethodSignature signature, ComponentValueProvider<T> provider);
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformMethod.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformMethod.java?rev=903293&r1=903292&r2=903293&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformMethod.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformMethod.java Tue Jan 26 16:02:54 2010
@@ -19,14 +19,34 @@
 /**
  * A method defined by (or created within) a {@link ClassTransformation}, allowing
  * for access and manipulation of the method.
+ * <p>
+ * The natural sorting order of TransformMethods is the same as {@link TransformMethodSignature}.
  * 
  * @since 5.2.0
  */
-public interface TransformMethod extends AnnotationProvider
+public interface TransformMethod extends AnnotationProvider, Comparable<TransformMethod>
 {
     /**
      * @return the signature for the method, defining name, visibility, return type, parameter types and thrown
      *         exceptions
      */
     TransformMethodSignature getSignature();
+
+    /**
+     * Extends an existing method. The provided method body is inserted at the end of the existing
+     * method (i.e. {@link javassist.CtBehavior#insertAfter(java.lang.String)}). To access or change
+     * the return value, use the <code>$_</code> pseudo variable.
+     * <p/>
+     * The method may be declared in the class, or may be inherited from a super-class. For inherited methods, a method
+     * body is added that first invokes the super implementation.
+     * <p/>
+     * The extended method is considered <em>new</em>. New methods <em>are not</em> scanned for removed fields, field
+     * access changes, etc.
+     * 
+     * @param body
+     *            the body of Javassist psuedo-code
+     * @throws RuntimeException
+     *             if the provided Javassist method body can not be compiled
+     */
+    void extend(String body);
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/services/ServicesStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/services/ServicesStrings.properties?rev=903293&r1=903292&r2=903293&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/services/ServicesStrings.properties (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/services/ServicesStrings.properties Tue Jan 26 16:02:54 2010
@@ -17,7 +17,6 @@
   The current element is established with the first call to element() and is \
   maintained across subsequent calls. 
 error-adding-method=Error adding method %s to class %s: %s 
-no-declared-method=Class %s does not declare method '%s'.
 class-not-transformed=Class %s was not transformed for use as a component; this can happen if it is an interface, or was not in a package subject to component transformation.
 missing-template-resource=Template resource %s does not exist.
 content-inside-body-not-allowed=Content inside a Tapestry body element is not allowed (at %s). The content has been ignored.

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/TextOnlyOnDisabledTextField.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/TextOnlyOnDisabledTextField.java?rev=903293&r1=903292&r2=903293&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/TextOnlyOnDisabledTextField.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/TextOnlyOnDisabledTextField.java Tue Jan 26 16:02:54 2010
@@ -1,10 +1,10 @@
-// Copyright 2009 The Apache Software Foundation
+// Copyright 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,17 +14,13 @@
 
 package org.apache.tapestry5.integration.app1.components;
 
-import org.apache.tapestry5.corelib.components.TextField;
 import org.apache.tapestry5.annotations.Mixin;
-import org.apache.tapestry5.integration.app1.mixins.TextOnlyOnDisabled;
+import org.apache.tapestry5.corelib.components.TextField;
 
-/**
- *
- */
 public class TextOnlyOnDisabledTextField extends TextField
 {
+    @SuppressWarnings("unused")
+    @Mixin(value = "textonlyondisabled", order = "after:*")
+    private Object theMixin;
 
-    @Mixin(order = "after:*")
-    private TextOnlyOnDisabled theMixin;
-    
 }