You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2008/06/08 21:11:58 UTC

svn commit: r664538 - 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/ site/apt/ site/apt/guide/ test/app1/ tes...

Author: hlship
Date: Sun Jun  8 12:11:57 2008
New Revision: 664538

URL: http://svn.apache.org/viewvc?rev=664538&view=rev
Log:
TAPESTRY-2311: "Parents before Child" concept for Component Rendering does not allow different rendering in subclasses

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ConstructorArg.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InjectionKey.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorker.java
      - copied, changed from r664019, tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ComponentLifecycleMethodWorker.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SimpleBeanSubclass.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorkerTest.java
      - copied, changed from r664019, tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/transform/ComponentLifecycleMethodWorkerTest.java
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformation.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/transform/OnEventWorker.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/PageLifecycleAnnotationWorker.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/TapestryModule.java
    tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/event.apt
    tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/rendering.apt
    tapestry/tapestry5/trunk/tapestry-core/src/site/apt/upgrade.apt
    tapestry/tapestry5/trunk/tapestry-core/src/test/app1/EventHandlerDemo.tml
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/IntegrationTests.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/InternalClassTransformationImplTest.java

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ConstructorArg.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ConstructorArg.java?rev=664538&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ConstructorArg.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ConstructorArg.java Sun Jun  8 12:11:57 2008
@@ -0,0 +1,51 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.internal.services;
+
+import javassist.CtClass;
+import org.apache.tapestry5.ioc.internal.util.Defense;
+
+/**
+ * Stores transformation type data about one argument to a class constructor.  Used with {@link
+ * org.apache.tapestry5.internal.services.InternalClassTransformation}.
+ */
+public class ConstructorArg
+{
+    private final CtClass type;
+
+    private final Object value;
+
+    /**
+     * Constructs new instance.
+     *
+     * @param type  type of the parameter to be created (may not be null)
+     * @param value value to be injected via the constructor (may be null)
+     */
+    ConstructorArg(CtClass type, Object value)
+    {
+        this.type = Defense.notNull(type, "type");
+        this.value = value;
+    }
+
+    public CtClass getType()
+    {
+        return type;
+    }
+
+    public Object getValue()
+    {
+        return value;
+    }
+}

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InjectionKey.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InjectionKey.java?rev=664538&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InjectionKey.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InjectionKey.java Sun Jun  8 12:11:57 2008
@@ -0,0 +1,63 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.internal.services;
+
+/**
+ * Used with {@link org.apache.tapestry5.internal.services.InternalClassTransformation} to search for prior injections
+ * of a give type and value.  Assumes the values have a reasonable hashCode() implementation.
+ */
+public final class InjectionKey
+{
+    private final Class type;
+    private final Object value;
+
+    private final int hashCode;
+
+    public InjectionKey(Class type, Object value)
+    {
+        this.type = type;
+        this.value = value;
+
+        hashCode = type.hashCode() * 31 + value.hashCode();
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return hashCode;
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (obj == null) return false;
+
+        if (obj instanceof InjectionKey)
+        {
+            InjectionKey other = (InjectionKey) obj;
+
+            return type.equals(other.type) &&
+                    value.equals(other.value);
+        }
+
+        return false;
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("InjectionKey[%s %s]", type.getName(), value);
+    }
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformation.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformation.java?rev=664538&r1=664537&r2=664538&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformation.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformation.java Sun Jun  8 12:11:57 2008
@@ -15,7 +15,6 @@
 package org.apache.tapestry5.internal.services;
 
 import javassist.CtClass;
-import org.apache.tapestry5.internal.util.MultiKey;
 import org.apache.tapestry5.ioc.internal.util.IdAllocator;
 import org.apache.tapestry5.model.MutableComponentModel;
 import org.apache.tapestry5.services.ClassTransformation;
@@ -53,7 +52,7 @@
     /**
      * Returns a copy of the list of constructor arguments for this class.
      */
-    List<InternalClassTransformationImpl.ConstructorArg> getConstructorArgs();
+    List<ConstructorArg> getConstructorArgs();
 
     /**
      * Searchs for an existing injection of an object, returning the name of the protected field into which the value
@@ -62,11 +61,16 @@
      * TODO: Howard sayz: Uggh! At least define a real key (MultiKey is intended for internal use, never part of an
      * API). Is this necessary?  The cost of re-injection is tiny.
      */
-    String searchForPreviousInjection(MultiKey key);
+    String searchForPreviousInjection(InjectionKey key);
 
     InternalClassTransformation createChildTransformation(CtClass childClass, MutableComponentModel childModel);
 
     /**
+     * Returns the parent transformation, or null for a root class.
+     */
+    InternalClassTransformation getParentTransformation();
+
+    /**
      * Creates a new method by copying the body of an existing method.  This is part of the scheme for providing method
      * advice.
      *
@@ -75,4 +79,12 @@
      * @param newMethodName name of new method to create
      */
     void copyMethod(TransformMethodSignature sourceMethod, int modifiers, String newMethodName);
+
+    /**
+     * Returns true if the provided signature is a method implemented by the transformed class.
+     *
+     * @param signature
+     * @return true if implemented
+     */
+    boolean isMethod(TransformMethodSignature signature);
 }

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=664538&r1=664537&r2=664538&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 Sun Jun  8 12:11:57 2008
@@ -19,13 +19,9 @@
 import javassist.expr.FieldAccess;
 import org.apache.tapestry5.ComponentResources;
 import org.apache.tapestry5.internal.InternalComponentResources;
-import org.apache.tapestry5.internal.util.MultiKey;
 import org.apache.tapestry5.ioc.internal.services.CtClassSource;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
-import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.*;
 import org.apache.tapestry5.ioc.internal.util.Defense;
-import static org.apache.tapestry5.ioc.internal.util.Defense.notBlank;
-import static org.apache.tapestry5.ioc.internal.util.Defense.notNull;
 import org.apache.tapestry5.ioc.internal.util.IdAllocator;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.apache.tapestry5.ioc.services.ClassFab;
@@ -39,7 +35,6 @@
 import org.apache.tapestry5.services.*;
 import org.slf4j.Logger;
 
-import static java.lang.String.format;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Modifier;
 import java.util.*;
@@ -64,23 +59,24 @@
     private final IdAllocator idAllocator;
 
     /**
-     * Map, keyed on InjectKey, of field name.
+     * 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<MultiKey, String> injectionCache = newMap();
+    private final Map<InjectionKey, String> injectionCache = CollectionFactory.newMap();
 
     /**
      * Map from a field to the annotation objects for that field.
      */
-    private Map<String, List<Annotation>> fieldAnnotations = newMap();
+    private Map<String, List<Annotation>> fieldAnnotations = CollectionFactory.newMap();
 
     /**
      * Used to identify fields that have been "claimed" by other annotations.
      */
-    private Map<String, Object> claimedFields = newMap();
+    private Map<String, Object> claimedFields = CollectionFactory.newMap();
 
-    private Set<String> addedFieldNames = newSet();
+    private Set<String> addedFieldNames = CollectionFactory.newSet();
 
-    private Set<CtBehavior> addedMethods = newSet();
+    private Set<CtBehavior> addedMethods = CollectionFactory.newSet();
 
     // Cache of class annotation
 
@@ -88,9 +84,9 @@
 
     // Cache of method annotation
 
-    private Map<CtMethod, List<Annotation>> methodAnnotations = newMap();
+    private Map<CtMethod, List<Annotation>> methodAnnotations = CollectionFactory.newMap();
 
-    private Map<CtMethod, TransformMethodSignature> methodSignatures = newMap();
+    private Map<CtMethod, TransformMethodSignature> methodSignatures = CollectionFactory.newMap();
 
     private Map<TransformMethodSignature, InvocationBuilder> methodToInvocationBuilder = CollectionFactory.newMap();
 
@@ -133,29 +129,6 @@
                                                                                       null);
 
     /**
-     * Stores transformation type data about one argument to a class constructor.
-     */
-    static class ConstructorArg
-    {
-        private final CtClass type;
-
-        private final Object value;
-
-        /**
-         * Constructs new instance.
-         *
-         * @param type  type of the parameter to be created (may not be null)
-         * @param value value to be injected via the constructor (may be null)
-         */
-        ConstructorArg(CtClass type, Object value)
-        {
-            this.type = Defense.notNull(type, "type");
-            this.value = value;
-        }
-
-    }
-
-    /**
      * This is a constructor for a base class.
      */
     public InternalClassTransformationImpl(ClassFactory classFactory, CtClass ctClass,
@@ -176,7 +149,7 @@
 
         preloadMemberNames();
 
-        constructorArgs = newList();
+        constructorArgs = CollectionFactory.newList();
         constructor.append("{\n");
 
         addImplementedInterface(Component.class);
@@ -285,7 +258,7 @@
 
     void verifyFields()
     {
-        List<String> names = newList();
+        List<String> names = CollectionFactory.newList();
 
         for (CtField field : ctClass.getDeclaredFields())
         {
@@ -408,7 +381,7 @@
     {
         try
         {
-            List<Annotation> result = newList();
+            List<Annotation> result = CollectionFactory.newList();
 
             addAnnotationsToList(result, member.getAnnotations());
 
@@ -445,7 +418,7 @@
     {
         failIfFrozen();
 
-        String memberName = InternalUtils.createMemberName(notBlank(suggested, "suggested"));
+        String memberName = InternalUtils.createMemberName(Defense.notBlank(suggested, "suggested"));
 
         return idAllocator.allocateId(memberName);
     }
@@ -564,8 +537,8 @@
 
     public void claimField(String fieldName, Object tag)
     {
-        notBlank(fieldName, "fieldName");
-        notNull(tag, "tag");
+        Defense.notBlank(fieldName, "fieldName");
+        Defense.notNull(tag, "tag");
 
         failIfFrozen();
 
@@ -947,7 +920,7 @@
     {
         failIfFrozen();
 
-        List<String> result = newList();
+        List<String> result = CollectionFactory.newList();
 
         try
         {
@@ -977,7 +950,7 @@
     {
         failIfFrozen();
 
-        List<TransformMethodSignature> result = newList();
+        List<TransformMethodSignature> result = CollectionFactory.newList();
 
         for (CtMethod method : ctClass.getDeclaredMethods())
         {
@@ -997,9 +970,9 @@
 
     public List<TransformMethodSignature> findMethods(MethodFilter filter)
     {
-        notNull(filter, "filter");
+        Defense.notNull(filter, "filter");
 
-        List<TransformMethodSignature> result = newList();
+        List<TransformMethodSignature> result = CollectionFactory.newList();
 
         for (CtMethod method : ctClass.getDeclaredMethods())
         {
@@ -1052,9 +1025,9 @@
     {
         failIfFrozen();
 
-        List<String> names = newList();
+        List<String> names = CollectionFactory.newList();
 
-        Set<String> skipped = newSet();
+        Set<String> skipped = CollectionFactory.newSet();
 
         skipped.addAll(claimedFields.keySet());
         skipped.addAll(addedFieldNames);
@@ -1173,11 +1146,11 @@
 
     public String addInjectedField(Class type, String suggestedName, Object value)
     {
-        notNull(type, "type");
+        Defense.notNull(type, "type");
 
         failIfFrozen();
 
-        MultiKey key = new MultiKey(type, value);
+        InjectionKey key = new InjectionKey(type, value);
 
         String fieldName = searchForPreviousInjection(key);
 
@@ -1221,7 +1194,7 @@
         return fieldName;
     }
 
-    public String searchForPreviousInjection(MultiKey key)
+    public String searchForPreviousInjection(InjectionKey key)
     {
         String result = injectionCache.get(key);
 
@@ -1248,6 +1221,39 @@
         builder.addAdvice(advice);
     }
 
+    public boolean isMethodOverride(TransformMethodSignature methodSignature)
+    {
+        Defense.notNull(methodSignature, "methodSignature");
+
+        if (!isMethod(methodSignature))
+            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;
+
+            search = search.getParentTransformation();
+        }
+
+        // Not found in any super-class.
+
+        return false;
+    }
+
+    public InternalClassTransformation getParentTransformation()
+    {
+        return parentTransformation;
+    }
+
+    public boolean isMethod(TransformMethodSignature signature)
+    {
+        Defense.notNull(signature, "signature");
+
+        return findDeclaredMethod(signature) != null;
+    }
+
     /**
      * Adds a parameter to the constructor for the class; the parameter is used to initialize the value for a field.
      *
@@ -1259,12 +1265,12 @@
     {
         constructorArgs.add(new ConstructorArg(fieldType, value));
 
-        extendConstructor(format("  %s = $%d;", fieldName, constructorArgs.size()));
+        extendConstructor(String.format("  %s = $%d;", fieldName, constructorArgs.size()));
     }
 
     public void injectField(String fieldName, Object value)
     {
-        notNull(fieldName, "fieldName");
+        Defense.notNull(fieldName, "fieldName");
 
         failIfFrozen();
 
@@ -1325,7 +1331,7 @@
         {
             ConstructorArg arg = constructorArgs.get(i);
 
-            types[i] = arg.type;
+            types[i] = arg.getType();
         }
 
         // Add a call to the initializer; the method converted fromt the classes default
@@ -1343,6 +1349,7 @@
         try
         {
             CtConstructor cons = CtNewConstructor.make(types, null, constructorBody, ctClass);
+
             ctClass.addConstructor(cons);
         }
         catch (CannotCompileException ex)
@@ -1403,7 +1410,7 @@
         {
             ConstructorArg arg = constructorArgs.get(i);
 
-            CtClass argCtType = arg.type;
+            CtClass argCtType = arg.getType();
             Class argType = toClass(argCtType.getName());
 
             boolean primitive = argCtType.isPrimitive();
@@ -1413,7 +1420,7 @@
             String fieldName = "_param_" + i;
 
             constructorParameterTypes[i + 1] = argType;
-            constructorParameterValues[i + 1] = arg.value;
+            constructorParameterValues[i + 1] = arg.getValue();
 
             cf.addField(fieldName, fieldType);
 
@@ -1489,7 +1496,7 @@
 
     private void assembleClassAnnotations()
     {
-        classAnnotations = newList();
+        classAnnotations = CollectionFactory.newList();
 
         try
         {
@@ -1553,7 +1560,7 @@
 
         String message = ServicesMessages.readOnlyField(ctClass.getName(), fieldName);
 
-        String body = format("throw new java.lang.RuntimeException(\"%s\");", message);
+        String body = String.format("throw new java.lang.RuntimeException(\"%s\");", message);
 
         addMethod(sig, body);
 
@@ -1566,7 +1573,7 @@
 
         // TODO: We could check that there's an existing field read and field write transform ...
 
-        if (removedFieldNames == null) removedFieldNames = newSet();
+        if (removedFieldNames == null) removedFieldNames = CollectionFactory.newSet();
 
         removedFieldNames.add(fieldName);
 
@@ -1579,7 +1586,7 @@
 
         String body = String.format("$_ = $0.%s();", methodName);
 
-        if (fieldReadTransforms == null) fieldReadTransforms = newMap();
+        if (fieldReadTransforms == null) fieldReadTransforms = CollectionFactory.newMap();
 
         // TODO: Collisions?
 
@@ -1595,7 +1602,7 @@
 
         String body = String.format("$0.%s($1);", methodName);
 
-        if (fieldWriteTransforms == null) fieldWriteTransforms = newMap();
+        if (fieldWriteTransforms == null) fieldWriteTransforms = CollectionFactory.newMap();
 
         // TODO: Collisions?
 
@@ -1635,9 +1642,9 @@
         // Provide empty maps here, to make the code in the inner class a tad
         // easier.
 
-        if (fieldReadTransforms == null) fieldReadTransforms = newMap();
+        if (fieldReadTransforms == null) fieldReadTransforms = CollectionFactory.newMap();
 
-        if (fieldWriteTransforms == null) fieldWriteTransforms = newMap();
+        if (fieldWriteTransforms == null) fieldWriteTransforms = CollectionFactory.newMap();
 
         ExprEditor editor = new ExprEditor()
         {
@@ -1717,7 +1724,7 @@
 
     public void extendConstructor(String statement)
     {
-        notNull(statement, "statement");
+        Defense.notNull(statement, "statement");
 
         failIfFrozen();
 
@@ -1727,7 +1734,7 @@
 
     public String getMethodIdentifier(TransformMethodSignature signature)
     {
-        notNull(signature, "signature");
+        Defense.notNull(signature, "signature");
 
         CtMethod method = findMethod(signature);
 
@@ -1735,7 +1742,7 @@
         CtClass enclosingClass = method.getDeclaringClass();
         String sourceFile = enclosingClass.getClassFile2().getSourceFile();
 
-        return format("%s.%s (at %s:%d)", enclosingClass.getName(), signature
+        return String.format("%s.%s (at %s:%d)", enclosingClass.getName(), signature
                 .getMediumDescription(), sourceFile, lineNumber);
     }
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java?rev=664538&r1=664537&r2=664538&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java Sun Jun  8 12:11:57 2008
@@ -43,8 +43,18 @@
         {
             public boolean accept(TransformMethodSignature signature)
             {
-                return signature.getMethodName().startsWith("on") || transformation.getMethodAnnotation(signature,
-                                                                                                        OnEvent.class) != null;
+                return (hasCorrectPrefix(signature) || hasAnnotation(signature)) &&
+                        !transformation.isMethodOverride(signature);
+            }
+
+            private boolean hasCorrectPrefix(TransformMethodSignature signature)
+            {
+                return signature.getMethodName().startsWith("on");
+            }
+
+            private boolean hasAnnotation(TransformMethodSignature signature)
+            {
+                return transformation.getMethodAnnotation(signature, OnEvent.class) != null;
             }
         };
 
@@ -80,6 +90,7 @@
         transformation.extendMethod(TransformConstants.DISPATCH_COMPONENT_EVENT, builder.toString());
     }
 
+
     private void addCodeForMethod(BodyBuilder builder, TransformMethodSignature method,
                                   ClassTransformation transformation)
     {

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/PageLifecycleAnnotationWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/PageLifecycleAnnotationWorker.java?rev=664538&r1=664537&r2=664538&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/PageLifecycleAnnotationWorker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/PageLifecycleAnnotationWorker.java Sun Jun  8 12:11:57 2008
@@ -24,8 +24,8 @@
 import java.lang.annotation.Annotation;
 
 /**
- * Similar to {@link ComponentLifecycleMethodWorker} but applies to annotations/methods related to the overall page
- * lifecycle.
+ * Similar to {@link org.apache.tapestry5.internal.transform.RenderPhaseMethodWorker} but applies to annotations/methods
+ * related to the overall page lifecycle.
  */
 public class PageLifecycleAnnotationWorker implements ComponentClassTransformWorker
 {

Copied: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorker.java (from r664019, tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ComponentLifecycleMethodWorker.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorker.java?p2=tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorker.java&p1=tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ComponentLifecycleMethodWorker.java&r1=664019&r2=664538&rev=664538&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ComponentLifecycleMethodWorker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorker.java Sun Jun  8 12:11:57 2008
@@ -32,7 +32,7 @@
  * Converts one of the methods of {@link org.apache.tapestry5.runtime.Component} into a chain of command that, itself,
  * invokes certain methods (render phase methods) marked with an annotation, or named in a specific way.
  */
-public class ComponentLifecycleMethodWorker implements ComponentClassTransformWorker
+public class RenderPhaseMethodWorker implements ComponentClassTransformWorker
 {
     private static final String CHECK_ABORT_FLAG = "if ($2.isAborted()) return;";
 
@@ -54,8 +54,8 @@
      * @param methodAnnotation         the class of the corresponding annotation
      * @param reverse                  if true, the normal method invocation order is reversed
      */
-    public ComponentLifecycleMethodWorker(TransformMethodSignature lifecycleMethodSignature,
-                                          Class<? extends Annotation> methodAnnotation, boolean reverse)
+    public RenderPhaseMethodWorker(TransformMethodSignature lifecycleMethodSignature,
+                                   Class<? extends Annotation> methodAnnotation, boolean reverse)
     {
         this.lifecycleMethodSignature = lifecycleMethodSignature;
         this.methodAnnotation = methodAnnotation;
@@ -72,7 +72,7 @@
     @Override
     public String toString()
     {
-        return String.format("ComponentLifecycleMethodWorker[%s]", methodAnnotation.getName());
+        return String.format("RenderPhaseMethodWorker[%s]", methodAnnotation.getName());
     }
 
     public void transform(final ClassTransformation transformation, MutableComponentModel model)
@@ -82,8 +82,7 @@
             public boolean accept(TransformMethodSignature signature)
             {
                 // These methods get added to base classes and otherwise fall into this filter. If
-                // we don't
-                // include this filter, then we get endless loops.
+                // we don't include this filter, then we get endless loops.
 
                 if (signature.equals(lifecycleMethodSignature)) return false;
 
@@ -91,8 +90,18 @@
                 // annotation, say @AfterRender. In that case, this code is broken, as the method
                 // will be invoked for both phases!
 
-                return signature.getMethodName().equals(lifecycleMethodName)
-                        || transformation.getMethodAnnotation(signature, methodAnnotation) != null;
+                return (correctName(signature) || correctAnnotation(signature)) &&
+                        !transformation.isMethodOverride(signature);
+            }
+
+            private boolean correctAnnotation(TransformMethodSignature signature)
+            {
+                return transformation.getMethodAnnotation(signature, methodAnnotation) != null;
+            }
+
+            private boolean correctName(TransformMethodSignature signature)
+            {
+                return signature.getMethodName().equals(lifecycleMethodName);
             }
         };
 
@@ -145,6 +154,7 @@
         transformation.addMethod(lifecycleMethodSignature, builder.toString());
     }
 
+
     private void addMethodCallToBody(BodyBuilder builder, TransformMethodSignature sig,
                                      ClassTransformation transformation)
     {

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=664538&r1=664537&r2=664538&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 Sun Jun  8 12:11:57 2008
@@ -45,8 +45,7 @@
  * any fields or methods inherited from a base class.
  *
  * @see org.apache.tapestry5.services.TapestryModule#contributeComponentClassTransformWorker(org.apache.tapestry5.ioc.OrderedConfiguration,
- *      org.apache.tapestry5.ioc.ObjectLocator, InjectionProvider, Environment, ComponentClassResolver,
- *      org.apache.tapestry5.internal.services.RequestPageCache, BindingSource)
+ *      org.apache.tapestry5.ioc.ObjectLocator, InjectionProvider, ComponentClassResolver)
  */
 public interface ClassTransformation extends AnnotationProvider
 {
@@ -376,4 +375,12 @@
      * Adds method advice for the indicated method.
      */
     void advise(TransformMethodSignature methodSignature, ComponentMethodAdvice advice);
+
+    /**
+     * Returns true if the method is an override of a method from the parent class.
+     *
+     * @param methodSignature signature of method to check
+     * @return true if the parent class contains a method with the name signature
+     */
+    boolean isMethodOverride(TransformMethodSignature methodSignature);
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java?rev=664538&r1=664537&r2=664538&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java Sun Jun  8 12:11:57 2008
@@ -734,7 +734,7 @@
 
         String name = annotationClass.getSimpleName();
 
-        configuration.add(name, new ComponentLifecycleMethodWorker(signature, annotationClass, reverse));
+        configuration.add(name, new RenderPhaseMethodWorker(signature, annotationClass, reverse));
     }
 
     // ========================================================================

Modified: tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/event.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/event.apt?rev=664538&r1=664537&r2=664538&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/event.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/event.apt Sun Jun  8 12:11:57 2008
@@ -34,7 +34,7 @@
   Tapestry 5 introduces the concept of <event handler methods>, identified via a naming convention, or
   via the 
   {{{../../apidocs/org/apache/tapestry5/annotations/OnEvent.html}OnEvent annotation}}.  Event handler methods
-  have any visibility, even private (normally they are given package private visibility, to support testing).
+  may have any visibility, even private.  Normally they are given package private visibility, to support testing.
   
   Rather than configure a component to invoke a particular method, you identify one or more
   methods to listen for events from that component. A single event handler method may receive notifications from
@@ -63,11 +63,12 @@
   When there are additional context values, they are appended to the path.
   
   This demonstrates a critical difference between Tapestry and a more traditional, action oriented framework.
-  This URL doesn't say what happens when the link is clicked, it identifies <which component is responsible>.
+  This URL doesn't say what happens when the link is clicked, it identifies <which component is responsible>
+  when the link is clicked.
 
   There's no simple mapping from URL to a piece of code; instead the component sends notifications, in the form
-  of invocations of event handler methods, and Tapestry ensures that the correct bit of code, that you supply,
-  gts invoked.
+  of invocations of event handler methods, and Tapestry ensures that the correct bit of code, code that you supply,
+  gets invoked.
   
    
   A Java method can be invoked when the link for the component is clicked by the user:
@@ -89,7 +90,7 @@
   []
   
   In the above example, the valueChosen() method will be invoked on the default event, "action", that originates
-  in component <<<choose>>> (and has at least one context value).
+  in component <<<select>>> (and has at least one context value).
   
   Some components can emit more than one type of event, in which case you will want to be more specific:
   
@@ -118,7 +119,7 @@
   they were annotated.  
   
   This style of event handler methods start with the prefix "on", followed by the name of the action.  You may then continue by adding "From" and
-  a capitalized component id.
+  a capitalized component id (remember that Tapestry is case insensitive about event names and component ids).
   
   The previous example may be rewritten as:
   
@@ -133,7 +134,8 @@
   
 Event Handler Method Return Values
   
-  For page navigation events, the value returned from an event handler method {{{pagenav.html}determines how Tapestry will render a response}}.
+  For page navigation events (originating in components such as ActionLink and Form),
+  the value returned from an event handler method {{{pagenav.html}determines how Tapestry will render a response}}.
 
 Multiple Method Matches
 
@@ -151,6 +153,10 @@
 
  There's only rare cases where it makes sense for more than one method to handle an event.
 
+ When a sub-class overrides an event handler method of a base class, the event handler method is only invoked once, along with
+ any other base class methods. The subclass can change the <implementation> of the base class method via an override, but
+ can't change the <timing> of when that method is invoked. See   {{{https://issues.apache.org/jira/browse/TAPESTRY-2311}TAPESTRY-2311}}.
+
 Event Context
 
   The context values (the context parameter to the ActionLink component) can be any object.
@@ -178,7 +184,7 @@
 * Collections
 
   To designate that an event handler method should be invoked regardless of how many context parameters are available,
-  change the method to accept a <single> parameter of type Object[], type List, or
+  change the method to accept a <single> parameter of type Object[], type List, or   type
   {{{../../apidocs/org/apache/tapestry5/EventContext.html}EventContext}}.
 
 Event Bubbling

Modified: tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/rendering.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/rendering.apt?rev=664538&r1=664537&r2=664538&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/rendering.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/rendering.apt Sun Jun  8 12:11:57 2008
@@ -299,7 +299,12 @@
 
   Ordering is always parent-first.  Methods defined in the parent class are always invoked
   before methods defined in the child class.
-  
+
+  When a sub-class overrides an render phase method of a base class, the
+  method is only invoked once, along with
+  any other base class methods. The subclass can change the <implementation> of the base class method via an override, but
+  can't change the <timing> of when that method is invoked. See   {{{https://issues.apache.org/jira/browse/TAPESTRY-2311}TAPESTRY-2311}}.
+    
 * Reverse Ordering for AfterXXX and CleanupRender
 
   The After<XXX> phases exists to balance the Begin<XXX> and Before<XXX> phases.  Often elements will

Modified: tapestry/tapestry5/trunk/tapestry-core/src/site/apt/upgrade.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/site/apt/upgrade.apt?rev=664538&r1=664537&r2=664538&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/site/apt/upgrade.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/site/apt/upgrade.apt Sun Jun  8 12:11:57 2008
@@ -12,6 +12,12 @@
   You should also check the {{{../release-notes.html}project-wide release notes}} for information
   about bugs fixes and other improvements.
 
+Release 5.0.13
+
+  As part of {{{https://issues.apache.org/jira/browse/TAPESTRY-2311}TAPESTRY-2311}}, there have been
+  subtle changes to how event handler and render phase methods are invoked, when the methods are
+  overrides of base class methods.
+
 Release 5.0.12
 
 * ReorderProperties annotation
@@ -23,9 +29,11 @@
 
 * {{{https://issues.apache.org/jira/browse/TAPESTRY-2421}TAPESTRY-2421}}
 
-  To enable deployment compatibility between Tapestry 4 and Tapestry 5, a
-  small number of classes
-  had to be renamed or refactored.
+  The root package for all Tapestry code was changed from
+  org.apache.tapestry to org.apache.tapestry<<5>>.  This will make it reasonable to deploy Tapestry 3 or Tapestry 4
+  applications side-by-side with a Tapestry 5 application.
+
+  In addition, a number of classes were refactored.
 
   PageRenderSupport has been renamed to just
   {{{../apidocs/org/apache/tapestry5/RenderSupport.html}RenderSupport}}.
@@ -37,18 +45,6 @@
   {{{../apidocs/org/apache/tapestry5/MarkupUtils.html}MarkupUtils}} and
   {{{../apidocs/org/apache/tapestry5/VersionUtils.html}VersionUtils}}.
 
-  Because of naming conflicts, several classes and interfaces were moved under an
-  org.apache.tapestry5 package:
-
-  * {{{../apidocs/org/apache/tapestry5/services/ApplicationInitializer.html}ApplicationInitializer}} and
-    {{{../apidocs/org/apache/tapestry5/services/ApplicationInitializerFilter.html}ApplicationInitializerFilter}}.
-
-  * {{{../apidocs/org/apache/tapestry5/services/ComponentMessagesSource.html}ComponentMessagesSource}}.
-
-  * {{{../apidocs/org/apache/tapestry5/services/RequestGlobals.html}RequestGlobals}}.
-
-  * {{{../apidocs/org/apache/tapestry5/json/JSONObject.html}JSONObject}} (and the other JSON classes).
-
   []
 
 * TapestryModule
@@ -74,7 +70,7 @@
 
 * Loop element parameter
 
-  The Loop components' elementName parameter was renamed to simply element (to be consistent
+  The Loop component's elementName parameter was renamed to simply element (to be consistent
   with element parameters added to the Any and FormInjector components).
 
 Release 5.0.11

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/app1/EventHandlerDemo.tml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/app1/EventHandlerDemo.tml?rev=664538&r1=664537&r2=664538&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/app1/EventHandlerDemo.tml (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/app1/EventHandlerDemo.tml Sun Jun  8 12:11:57 2008
@@ -1,29 +1,32 @@
 <html t:type="Border" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
 
-  <h1>EventHandler Demo</h1>
+    <h1>EventHandler Demo</h1>
 
-  <t:if test="methodNames">
-    <p> Event method names: ${methodNames} </p>
-  </t:if>
-
-
-  <p> [<t:pagelink page="eventhandlerdemo" context="'anything'">clear</t:pagelink>] </p>
-
-
-  <ul>
-    <li>
-      <t:actionlink t:id="wilma">No Context</t:actionlink>
-    </li>
-    <li>
-      <t:actionlink t:id="barney" context="literal:one">Single context value</t:actionlink>
-    </li>
-    <li>
-      <t:actionlink t:id="betty" context="twoContext">Two value context</t:actionlink>
-    </li>
-    <li>
-      <t:actionlink context="'two'" t:id="fred">Two value context (from fred)</t:actionlink>
-    </li>
-  </ul>
+    <t:if test="methodNames">
+        <p>Event method names:
+            <span id="methodNames">${methodNames}</span>
+        </p>
+    </t:if>
+
+
+    <p>[<t:pagelink page="eventhandlerdemo" context="'anything'">clear</t:pagelink>]
+    </p>
+
+
+    <ul>
+        <li>
+            <t:actionlink t:id="wilma">No Context</t:actionlink>
+        </li>
+        <li>
+            <t:actionlink t:id="barney" context="literal:one">Single context value</t:actionlink>
+        </li>
+        <li>
+            <t:actionlink t:id="betty" context="twoContext">Two value context</t:actionlink>
+        </li>
+        <li>
+            <t:actionlink context="'two'" t:id="fred">Two value context (from fred)</t:actionlink>
+        </li>
+    </ul>
 
 
 </html>

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/IntegrationTests.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/IntegrationTests.java?rev=664538&r1=664537&r2=664538&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/IntegrationTests.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/IntegrationTests.java Sun Jun  8 12:11:57 2008
@@ -885,25 +885,26 @@
         clickAndWait(clear);
 
         clickAndWait("link=No Context");
-        assertTextPresent(
-                "[parent.eventHandlerZero(), parent.onAction(), child.eventHandlerZeroChild(), child.onAction()]");
+        assertText("methodNames",
+                   "[parent.eventHandlerZero(), parent.onAction(), child.eventHandlerZeroChild()]");
 
         clickAndWait(clear);
         clickAndWait("link=Single context value");
 
-        assertTextPresent(
-                "[parent.eventHandlerOne(String), parent.eventHandlerZero(), parent.onAction(String), parent.onAction(), child.eventHandlerOneChild(), child.eventHandlerZeroChild(), child.onAction(String), child.onAction()]");
+        assertText("methodNames",
+                   "[parent.eventHandlerOne(String), parent.eventHandlerZero(), parent.onAction(String), parent.onAction(), child.eventHandlerOneChild(), child.eventHandlerZeroChild()]");
 
         clickAndWait(clear);
         clickAndWait("link=Two value context");
-        assertTextPresent(
-                "[parent.eventHandlerOne(String), parent.eventHandlerZero(), parent.onAction(String), parent.onAction(), child.eventHandlerOneChild(), child.eventHandlerZeroChild(), child.onAction(String), child.onAction()]");
+
+        assertText("methodNames",
+                   "[parent.eventHandlerOne(String), parent.eventHandlerZero(), parent.onAction(String), parent.onAction(), child.eventHandlerOneChild(), child.eventHandlerZeroChild()]");
 
         clickAndWait(clear);
         clickAndWait("link=Two value context (from fred)");
 
-        assertTextPresent(
-                "[parent.eventHandlerOne(String), parent.eventHandlerZero(), parent.onAction(String), parent.onAction(), child.eventHandlerForFred(), child.eventHandlerOneChild(), child.eventHandlerZeroChild(), child.onAction(String), child.onAction(), child.onActionFromFred(String), child.onActionFromFred()]");
+        assertText("methodNames",
+                   "[parent.eventHandlerOne(String), parent.eventHandlerZero(), parent.onAction(String), parent.onAction(), child.eventHandlerForFred(), child.eventHandlerOneChild(), child.eventHandlerZeroChild(), child.onActionFromFred(String), child.onActionFromFred()]");
     }
 
     @Test

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/InternalClassTransformationImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/InternalClassTransformationImplTest.java?rev=664538&r1=664537&r2=664538&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/InternalClassTransformationImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/InternalClassTransformationImplTest.java Sun Jun  8 12:11:57 2008
@@ -171,11 +171,16 @@
     {
         CtClass ctClass = findCtClass(targetClass);
 
-        MutableComponentModel model = new MutableComponentModelImpl("unknown-class", logger, null, null);
+        MutableComponentModel model = stubMutableComponentModel(logger);
 
         return new InternalClassTransformationImpl(classFactory, ctClass, null, model, null);
     }
 
+    private MutableComponentModel stubMutableComponentModel(Logger logger)
+    {
+        return new MutableComponentModelImpl("unknown-class", logger, null, null);
+    }
+
     @Test
     public void find_annotation_on_unknown_field() throws Exception
     {
@@ -1169,5 +1174,84 @@
         verify();
     }
 
+    @Test
+    public void base_class_methods_are_never_overridden() throws Exception
+    {
+        Logger logger = mockLogger();
+
+        replay();
+
+        MethodFilter filter = new MethodFilter()
+        {
+            public boolean accept(TransformMethodSignature signature)
+            {
+                return true;
+            }
+        };
+
+        ClassTransformation ct = createClassTransformation(SimpleBean.class, logger);
+
+        List<TransformMethodSignature> methods = ct.findMethods(filter);
+
+        assertFalse(methods.isEmpty());
+
+        for (TransformMethodSignature sig : methods)
+        {
+            assertFalse(ct.isMethodOverride(sig));
+        }
+
+
+        verify();
+    }
+
+    @Test
+    public void check_for_method_override_on_non_declared_method() throws Exception
+    {
+        Logger logger = mockLogger();
+
+        replay();
+
+        ClassTransformation ct = createClassTransformation(SimpleBean.class, logger);
+
+        TransformMethodSignature sig = new TransformMethodSignature("methodDoesNotExist");
+
+        try
+        {
+            ct.isMethodOverride(sig);
+            unreachable();
+        }
+        catch (IllegalArgumentException ex)
+        {
+            assertEquals(ex.getMessage(),
+                         "Method public void methodDoesNotExist() is not implemented by transformed class org.apache.tapestry5.internal.services.SimpleBean.");
+        }
+
+        verify();
+
+    }
+
+    @Test
+    public void check_for_overridden_methods() throws Exception
+    {
+        Logger logger = mockLogger();
+
+        replay();
+
+        InternalClassTransformation parentTransform = createClassTransformation(SimpleBean.class, logger);
+
+        parentTransform.finish();
+
+        CtClass childClass = findCtClass(SimpleBeanSubclass.class);
+
+        ClassTransformation childTransform = parentTransform.createChildTransformation(childClass,
+                                                                                       stubMutableComponentModel(
+                                                                                               logger));
+
+        assertFalse(childTransform.isMethodOverride(new TransformMethodSignature("notOverridden")));
+
+        assertTrue(childTransform.isMethodOverride(
+                new TransformMethodSignature(Modifier.PUBLIC, "void", "setAge", new String[] { "int" }, null)));
+    }
+
 
 }

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SimpleBeanSubclass.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SimpleBeanSubclass.java?rev=664538&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SimpleBeanSubclass.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SimpleBeanSubclass.java Sun Jun  8 12:11:57 2008
@@ -0,0 +1,28 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.internal.services;
+
+public class SimpleBeanSubclass extends SimpleBean
+{
+    public void notOverridden()
+    {
+    }
+
+    @Override
+    public void setAge(int age)
+    {
+        super.setAge(age);
+    }
+}

Copied: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorkerTest.java (from r664019, tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/transform/ComponentLifecycleMethodWorkerTest.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorkerTest.java?p2=tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorkerTest.java&p1=tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/transform/ComponentLifecycleMethodWorkerTest.java&r1=664019&r2=664538&rev=664538&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/transform/ComponentLifecycleMethodWorkerTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorkerTest.java Sun Jun  8 12:11:57 2008
@@ -27,7 +27,7 @@
  * Of course, we're committing the cardinal sin of testing the code that's generated, rather than the *behavior* of the
  * generated code. Fortunately, we back all this up with lots and lots of integration testing.
  */
-public class ComponentLifecycleMethodWorkerTest extends TapestryTestCase
+public class RenderPhaseMethodWorkerTest extends TapestryTestCase
 {
     @Test
     public void no_methods_with_annotation()
@@ -43,7 +43,7 @@
 
         replay();
 
-        ComponentClassTransformWorker worker = new ComponentLifecycleMethodWorker(
+        ComponentClassTransformWorker worker = new RenderPhaseMethodWorker(
                 TransformConstants.SETUP_RENDER_SIGNATURE, SetupRender.class, false);
 
         worker.transform(tf, model);
@@ -61,7 +61,7 @@
 
         replay();
 
-        ComponentClassTransformWorker worker = new ComponentLifecycleMethodWorker(
+        ComponentClassTransformWorker worker = new RenderPhaseMethodWorker(
                 TransformConstants.SETUP_RENDER_SIGNATURE, SetupRender.class, false);
 
         worker.transform(tf, model);