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 2006/12/20 19:06:40 UTC

svn commit: r489162 - in /tapestry/tapestry5/tapestry-core/trunk/src: main/java/org/apache/tapestry/ main/java/org/apache/tapestry/annotations/ main/java/org/apache/tapestry/corelib/base/ main/java/org/apache/tapestry/corelib/mixins/ main/java/org/apac...

Author: hlship
Date: Wed Dec 20 10:06:37 2006
New Revision: 489162

URL: http://svn.apache.org/viewvc?view=rev&rev=489162
Log:
Remove the PreBeginRender and PostBeginRender annotations and render phases.
Add the MixinAfter annotation and support, to allow a Mixin to operate after the component (not before) during rendering.
Add InjectComponent annotation, which allows a Mixin to see the component to which it is attached.
Create a RenderDisabled mixin, that attaches to Field components, that will render the disabled attribute if the field is disabled.

Added:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/FieldValidator.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/InjectComponent.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/MixinAfter.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/mixins/RenderDisabled.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InjectComponentWorker.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/MixinAfterWorker.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/InjectComponentMismatch.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/MixinAfterWorkerTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/InjectComponentMismatch.html
Removed:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/PostBeginRender.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/PreBeginRender.java
Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Validator.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/base/AbstractField.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/mixins/RenderInformals.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalComponentResources.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableComponentModelImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/InternalComponentResourcesImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/ComponentModel.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableComponentModel.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/Component.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TransformConstants.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties
    tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/DefaultComponent.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/log4j.properties

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/FieldValidator.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/FieldValidator.java?view=auto&rev=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/FieldValidator.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/FieldValidator.java Wed Dec 20 10:06:37 2006
@@ -0,0 +1,36 @@
+// Copyright 2006 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.tapestry;
+
+/**
+ * Responsible for validation of a single field.
+ * 
+ * @param <T>
+ * @see Validator
+ */
+public interface FieldValidator<T>
+{
+    /**
+     * Invoked after the client-submitted value has been {@link Translator translated} to check that
+     * the value conforms to expectations (often, in terms of minimum or maximum value). If and only
+     * if the value is approved by all Validators is the value applied by the field.
+     * 
+     * @param value
+     *            the translated value supplied by the user
+     * @throws ValidationException
+     *             if the value violates the constraint
+     */
+    void check(T value) throws ValidationException;
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Validator.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Validator.java?view=diff&rev=489162&r1=489161&r2=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Validator.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Validator.java Wed Dec 20 10:06:37 2006
@@ -14,22 +14,40 @@
 
 package org.apache.tapestry;
 
-import sun.security.validator.ValidatorException;
+import org.apache.tapestry.ioc.Messages;
 
 /**
  * Used by a {@link Field} to enforce a <strong>constraint</strong> related to a form submission.
+ * Validators themselves are stateless singletons.
+ * <p>
+ * Validators are usually encapsulated inside a {@link FieldValidator}.
  */
-public interface Validator<T>
+public interface Validator<C, T>
 {
     /**
+     * Returns the type of constraint value used with this validator. Constraint values are used to
+     * parameterize a validator, for example a "maxLength" validator will have a constraint value of
+     * type int (the maximum length allowed). For constraints that do not have a constraint value,
+     * this method returns void.class.
+     */
+    public Class<C> getConstraintType();
+
+    /**
      * Invoked after the client-submitted value has been {@link Translator translated} to check that
      * the value conforms to expectations (often, in terms of minimum or maximum value). If and only
      * if the value is approved by all Validators is the value applied by the field.
      * 
      * @param field
      *            the field for which a client submitted value is being validated
+     * @param constraintValue
+     *            the value used to constrain
+     * @param messages
+     *            Validation messages, in the appropriate locale
      * @param value
-     * @throws ValidatorException
+     *            the translated value supplied by the user
+     * @throws ValidationException
+     *             if the value violates the constraint
      */
-    void check(Field field, T value) throws ValidatorException;
+    void check(Field field, C constraintValue, Messages messages, T value)
+            throws ValidationException;
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/InjectComponent.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/InjectComponent.java?view=auto&rev=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/InjectComponent.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/InjectComponent.java Wed Dec 20 10:06:37 2006
@@ -0,0 +1,32 @@
+// Copyright 2006 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.tapestry.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Used exclusively inside a mixin to connect the mixin to the component to which it is attached.
+ */
+@Target(FIELD)
+@Documented
+@Retention(RUNTIME)
+public @interface InjectComponent {
+
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/MixinAfter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/MixinAfter.java?view=auto&rev=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/MixinAfter.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/MixinAfter.java Wed Dec 20 10:06:37 2006
@@ -0,0 +1,37 @@
+// Copyright 2006 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.tapestry.annotations;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * A marker annotation applied to a mixin to indicate that the mixin's render state behavior is
+ * deferred until after the the behavior of the component to which the mixin is attached. Normally,
+ * mixins occur before the component. This divides each phase in the render state machine into three
+ * virtual phases: before the component, the component itself, and after the component.
+ */
+@Target(TYPE)
+@Documented
+@Retention(RUNTIME)
+@Inherited
+public @interface MixinAfter {
+
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/base/AbstractField.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/base/AbstractField.java?view=diff&rev=489162&r1=489161&r2=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/base/AbstractField.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/base/AbstractField.java Wed Dec 20 10:06:37 2006
@@ -19,14 +19,13 @@
 import org.apache.tapestry.ComponentAction;
 import org.apache.tapestry.ComponentResources;
 import org.apache.tapestry.Field;
-import org.apache.tapestry.MarkupWriter;
 import org.apache.tapestry.annotations.ComponentClass;
 import org.apache.tapestry.annotations.Environmental;
 import org.apache.tapestry.annotations.Inject;
 import org.apache.tapestry.annotations.Mixin;
 import org.apache.tapestry.annotations.Parameter;
-import org.apache.tapestry.annotations.PostBeginRender;
 import org.apache.tapestry.annotations.SetupRender;
+import org.apache.tapestry.corelib.mixins.RenderDisabled;
 import org.apache.tapestry.corelib.mixins.RenderInformals;
 import org.apache.tapestry.internal.services.FormParameterLookup;
 import org.apache.tapestry.ioc.internal.util.InternalUtils;
@@ -54,6 +53,10 @@
     @Mixin
     private RenderInformals _renderInformals;
 
+    @SuppressWarnings("unused")
+    @Mixin
+    private RenderDisabled _renderDisabled;
+
     static class SetupAction implements ComponentAction<AbstractField>, Serializable
     {
         private static final long serialVersionUID = 2690270808212097020L;
@@ -113,13 +116,6 @@
 
         _formSupport.storeAndExecute(this, new SetupAction(elementName));
         _formSupport.store(this, PROCESS_SUBMISSION_ACTION);
-    }
-
-    @PostBeginRender
-    final void renderDisabledAttribute(MarkupWriter writer)
-    {
-        if (_disabled)
-            writer.attributes("disabled", "disabled");
     }
 
     public final String getClientId()

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/mixins/RenderDisabled.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/mixins/RenderDisabled.java?view=auto&rev=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/mixins/RenderDisabled.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/mixins/RenderDisabled.java Wed Dec 20 10:06:37 2006
@@ -0,0 +1,37 @@
+// Copyright 2006 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.tapestry.corelib.mixins;
+
+import org.apache.tapestry.Field;
+import org.apache.tapestry.MarkupWriter;
+import org.apache.tapestry.annotations.BeginRender;
+import org.apache.tapestry.annotations.ComponentClass;
+import org.apache.tapestry.annotations.InjectComponent;
+import org.apache.tapestry.annotations.MixinAfter;
+
+@ComponentClass
+@MixinAfter
+public class RenderDisabled
+{
+    @InjectComponent
+    private Field _field;
+
+    @BeginRender
+    void begin(MarkupWriter writer)
+    {
+        if (_field.isDisabled())
+            writer.attributes("disabled", "disabled");
+    }
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/mixins/RenderInformals.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/mixins/RenderInformals.java?view=diff&rev=489162&r1=489161&r2=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/mixins/RenderInformals.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/mixins/RenderInformals.java Wed Dec 20 10:06:37 2006
@@ -21,7 +21,7 @@
 import org.apache.tapestry.annotations.BeginRender;
 import org.apache.tapestry.annotations.ComponentClass;
 import org.apache.tapestry.annotations.Inject;
-import org.apache.tapestry.annotations.PostBeginRender;
+import org.apache.tapestry.annotations.MixinAfter;
 import org.apache.tapestry.annotations.SupportsInformalParameters;
 
 /**
@@ -38,13 +38,14 @@
  * This is often used as a base class, for cases where a component doesn't have other mixins.
  */
 @ComponentClass
+@MixinAfter
 @SupportsInformalParameters
 public class RenderInformals
 {
     @Inject
     private ComponentResources _resources;
 
-    @PostBeginRender
+    @BeginRender
     void write(MarkupWriter writer)
     {
         _resources.renderInformalParameters(writer);

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalComponentResources.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalComponentResources.java?view=diff&rev=489162&r1=489161&r2=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalComponentResources.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalComponentResources.java Wed Dec 20 10:06:37 2006
@@ -12,52 +12,58 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.internal;
-
-import org.apache.tapestry.ComponentResources;
-
-/**
- * An extension of {@link org.apache.tapestry.ComponentResources} that represents additional methods
- * that are private to the framework and not exposed in any public APIs. Ideally, there will not be
- * any need for this interface (we'll see as we go).
- */
-public interface InternalComponentResources extends ComponentResources,
-        InternalComponentResourcesCommon
-{
-    /**
-     * Reads the value of a parameter, via the parameter's {@link org.apache.tapestry.services.Binding}.
-     * 
-     * @param <T>
-     * @param parameterName
-     *            the name of the parameter to read
-     * @param expectedType
-     *            the expected type of parameter
-     * @return the value for the parameter, or null if the parameter is not bound.
-     */
-    <T> T readParameter(String parameterName, Class<T> expectedType);
-
-    /**
-     * Updates a parameter. It is an error to update a parameter which is not bound. The parameter
-     * {@link org.apache.tapestry.services.Binding binding} may also not support updates.
-     * 
-     * @param <T>
-     * @param parameterName
-     *            of parameter to update
-     * @param parameterValue
-     *            new value (which may be null)
-     */
-    <T> void writeParameter(String parameterName, T parameterValue);
-
-    /**
-     * Returns true if the named parameter's {@link org.apache.tapestry.services.Binding} is invariant, false
-     * if otherwise, or if the parameter is not bound. Invariant bindings are cached more
-     * aggresively than variant bindings.
-     * 
-     * @param parameterName
-     *            the name of parameter to check for invariance
-     * @return
-     */
-    boolean isInvariant(String parameterName);
-
-
-}
+package org.apache.tapestry.internal;
+
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.runtime.Component;
+
+/**
+ * An extension of {@link org.apache.tapestry.ComponentResources} that represents additional methods
+ * that are private to the framework and not exposed in any public APIs. Ideally, there will not be
+ * any need for this interface (we'll see as we go).
+ */
+public interface InternalComponentResources extends ComponentResources,
+        InternalComponentResourcesCommon
+{
+    /**
+     * Reads the value of a parameter, via the parameter's
+     * {@link org.apache.tapestry.services.Binding}.
+     * 
+     * @param <T>
+     * @param parameterName
+     *            the name of the parameter to read
+     * @param expectedType
+     *            the expected type of parameter
+     * @return the value for the parameter, or null if the parameter is not bound.
+     */
+    <T> T readParameter(String parameterName, Class<T> expectedType);
+
+    /**
+     * Updates a parameter. It is an error to update a parameter which is not bound. The parameter
+     * {@link org.apache.tapestry.services.Binding binding} may also not support updates.
+     * 
+     * @param <T>
+     * @param parameterName
+     *            of parameter to update
+     * @param parameterValue
+     *            new value (which may be null)
+     */
+    <T> void writeParameter(String parameterName, T parameterValue);
+
+    /**
+     * Returns true if the named parameter's {@link org.apache.tapestry.services.Binding} is
+     * invariant, false if otherwise, or if the parameter is not bound. Invariant bindings are
+     * cached more aggresively than variant bindings.
+     * 
+     * @param parameterName
+     *            the name of parameter to check for invariance
+     * @return
+     */
+    boolean isInvariant(String parameterName);
+
+    /**
+     * For a normal component, the same as {@link #getComponent()}, but for a mixin, returns the
+     * component to which the mixin is attached.
+     */
+    Component getCoreComponent();
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableComponentModelImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableComponentModelImpl.java?view=diff&rev=489162&r1=489161&r2=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableComponentModelImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableComponentModelImpl.java Wed Dec 20 10:06:37 2006
@@ -58,6 +58,8 @@
 
     private boolean _informalParametersSupported;
 
+    private boolean _mixinAfter;
+
     public MutableComponentModelImpl(String componentClassName, Log log, Resource baseResource,
             ComponentModel parentModel)
     {
@@ -263,6 +265,16 @@
     public ComponentModel getParentModel()
     {
         return _parentModel;
+    }
+
+    public boolean isMixinAfter()
+    {
+        return _mixinAfter;
+    }
+
+    public void setMixinAfter(boolean mixinAfter)
+    {
+        _mixinAfter = mixinAfter;
     }
 
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InjectComponentWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InjectComponentWorker.java?view=auto&rev=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InjectComponentWorker.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InjectComponentWorker.java Wed Dec 20 10:06:37 2006
@@ -0,0 +1,88 @@
+// Copyright 2006 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.tapestry.internal.services;
+
+import java.util.List;
+
+import org.apache.tapestry.annotations.InjectComponent;
+import org.apache.tapestry.ioc.util.BodyBuilder;
+import org.apache.tapestry.model.MutableComponentModel;
+import org.apache.tapestry.runtime.Component;
+import org.apache.tapestry.services.ClassTransformation;
+import org.apache.tapestry.services.ComponentClassTransformWorker;
+import org.apache.tapestry.services.TransformConstants;
+
+/**
+ * Identifies the {@link InjectComponent} annotation and adds code to initialize it to the core
+ * component.
+ */
+public class InjectComponentWorker implements ComponentClassTransformWorker
+{
+
+    public void transform(ClassTransformation transformation, MutableComponentModel model)
+    {
+        List<String> names = transformation.findFieldsWithAnnotation(InjectComponent.class);
+
+        if (names.isEmpty())
+            return;
+
+        // I can't imagine a scenario where a component would have more than one
+        // field with InjectComponent, but that's the way these APIs work, lists of names.
+
+        BodyBuilder builder = new BodyBuilder();
+        builder.begin();
+
+        builder.addln("%s core = %s.getCoreComponent();", Component.class.getName(), transformation
+                .getResourcesFieldName());
+
+        for (String fieldName : names)
+        {
+            InjectComponent annotation = transformation.getFieldAnnotation(
+                    fieldName,
+                    InjectComponent.class);
+
+            String fieldType = transformation.getFieldType(fieldName);
+
+            builder.addln("try");
+            builder.begin();
+            builder.addln("%s = (%s) core;", fieldName, fieldType);
+            builder.end();
+            builder.addln("catch (ClassCastException ex)");
+            builder.begin();
+            builder.addln(
+                    "String message = %s.buildCastExceptionMessage(core, \"%s.%s\", \"%s\");",
+                    InjectComponentWorker.class.getName(),
+                    model.getComponentClassName(),
+                    fieldName,
+                    fieldType);
+            builder.addln("throw new RuntimeException(message, ex);");
+            builder.end();
+
+            transformation.makeReadOnly(fieldName);
+            transformation.claimField(fieldName, annotation);
+        }
+
+        builder.end();
+
+        transformation.extendMethod(TransformConstants.CONTAINING_PAGE_DID_LOAD_SIGNATURE, builder
+                .toString());
+    }
+
+    public static String buildCastExceptionMessage(Component component, String fieldName,
+            String fieldType)
+    {
+        return ServicesMessages.componentNotAssignableToField(component, fieldName, fieldType);
+    }
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/MixinAfterWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/MixinAfterWorker.java?view=auto&rev=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/MixinAfterWorker.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/MixinAfterWorker.java Wed Dec 20 10:06:37 2006
@@ -0,0 +1,36 @@
+// Copyright 2006 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.tapestry.internal.services;
+
+import org.apache.tapestry.annotations.MixinAfter;
+import org.apache.tapestry.model.ComponentModel;
+import org.apache.tapestry.model.MutableComponentModel;
+import org.apache.tapestry.services.ClassTransformation;
+import org.apache.tapestry.services.ComponentClassTransformWorker;
+
+/**
+ * Looks for the {@link MixinAfter} annotatation and sets the
+ * {@link ComponentModel#isMixinAfter() mixinAfter flag} is present.
+ */
+public class MixinAfterWorker implements ComponentClassTransformWorker
+{
+
+    public void transform(ClassTransformation transformation, MutableComponentModel model)
+    {
+        if (transformation.getAnnotation(MixinAfter.class) != null)
+            model.setMixinAfter(true);
+    }
+
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java?view=diff&rev=489162&r1=489161&r2=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java Wed Dec 20 10:06:37 2006
@@ -265,4 +265,11 @@
     {
         return MESSAGES.format("wrong-asset-digest", resource.getPath());
     }
+
+    static String componentNotAssignableToField(Component component, String fieldName,
+            String fieldType)
+    {
+        return MESSAGES.format("component-not-assignable-to-field", component
+                .getComponentResources().getCompleteId(), fieldName, fieldType);
+    }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java?view=diff&rev=489162&r1=489161&r2=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java Wed Dec 20 10:06:37 2006
@@ -127,7 +127,7 @@
             invoke(true, callback);
 
             if (event.getResult())
-                queue.push(_preBeginRender);
+                queue.push(_beginRender);
         }
 
         @Override
@@ -253,6 +253,7 @@
     {
         public void render(final MarkupWriter writer, final RenderQueue queue)
         {
+
             final LifecycleEvent<Boolean> event = newEvent(true);
 
             ComponentCallback callback = new ComponentCallback()
@@ -265,8 +266,16 @@
 
             invoke(false, callback);
 
+            // If the component has no template whatsoever, then a
+            // renderBody element is added as the lone element of the component's template.
+            // So every component will have a non-empty template.
+
             if (event.getResult())
-                queue.push(_postBeginRender);
+            {
+                queue.push(_afterRender);
+                queue.push(_beforeRenderTemplate);
+            }
+
         }
 
         @Override
@@ -324,8 +333,9 @@
 
     private final String _completeId;
 
-    // The user-provided class, with runtime code enhancements.
-    private final Component _component;
+    // The user-provided class, with runtime code enhancements. In a component with mixins, this
+    // is the component to which the mixins are attached.
+    private final Component _coreComponent;
 
     private final ComponentMessagesSource _messagesSource;
 
@@ -353,67 +363,6 @@
 
     private final Page _page;
 
-    private final RenderCommand _postBeginRender = new RenderCommand()
-    {
-        public void render(final MarkupWriter writer, final RenderQueue queue)
-        {
-            final LifecycleEvent<Boolean> event = newEvent(true);
-
-            ComponentCallback callback = new ComponentCallback()
-            {
-                public void run(Component component)
-                {
-                    component.postBeginRender(writer, event);
-                }
-            };
-
-            invoke(false, callback);
-
-            // If the component has no template whatsoever, then a
-            // renderBody element is added as the lone element of the component's template.
-            // So every component will have a non-empty template.
-
-            if (event.getResult())
-            {
-                queue.push(_afterRender);
-                queue.push(_beforeRenderTemplate);
-            }
-        }
-
-        @Override
-        public String toString()
-        {
-            return phaseToString("PostBeginRender");
-        }
-    };
-
-    private final RenderCommand _preBeginRender = new RenderCommand()
-    {
-        public void render(final MarkupWriter writer, final RenderQueue queue)
-        {
-            final LifecycleEvent<Boolean> event = newEvent(true);
-
-            ComponentCallback callback = new ComponentCallback()
-            {
-                public void run(Component component)
-                {
-                    component.preBeginRender(writer, event);
-                }
-            };
-
-            invoke(false, callback);
-
-            if (event.getResult())
-                queue.push(_beginRender);
-        }
-
-        @Override
-        public String toString()
-        {
-            return phaseToString("PreBeginRender");
-        }
-    };
-
     private boolean _rendering;
 
     private final RenderCommand _setupRender = new RenderCommand()
@@ -437,7 +386,7 @@
             queue.push(_cleanupRender);
 
             if (event.getResult())
-                queue.push(_preBeginRender);
+                queue.push(_beginRender);
         }
 
         @Override
@@ -490,9 +439,9 @@
         _coreResources = new InternalComponentResourcesImpl(this, instantiator, _typeCoercer,
                 _messagesSource);
 
-        _component = _coreResources.getComponent();
+        _coreComponent = _coreResources.getComponent();
 
-        String componentClassName = _component.getClass().getName();
+        String componentClassName = _coreComponent.getClass().getName();
 
         // A page (really, the root component of a page) does not have a container.
 
@@ -681,7 +630,35 @@
         // mixins.
 
         if (_components != null)
-            _components.add(_component);
+        {
+            List<Component> ordered = newList();
+
+            Iterator<Component> i = _components.iterator();
+
+            // Add all the normal components to the final list.
+
+            while (i.hasNext())
+            {
+                Component mixin = i.next();
+
+                if (mixin.getComponentResources().getComponentModel().isMixinAfter())
+                    continue;
+
+                ordered.add(mixin);
+
+                // Remove from list, leaving just the late executing mixins
+
+                i.remove();
+            }
+
+            ordered.add(_coreComponent);
+
+            // Add the remaining, late executing mixins
+
+            ordered.addAll(_components);
+
+            _components = ordered;
+        }
 
         invoke(false, CONTAINING_PAGE_DID_LOAD);
     }
@@ -711,7 +688,7 @@
 
     public Component getComponent()
     {
-        return _component;
+        return _coreComponent;
     }
 
     public InternalComponentResources getComponentResources()
@@ -798,7 +775,7 @@
         // Simple case: no mixins
 
         if (_components == null)
-            return _component.handleComponentEvent(event);
+            return _coreComponent.handleComponentEvent(event);
 
         // Otherwise, iterate over mixins + core component
 
@@ -836,7 +813,7 @@
 
         if (_components == null)
         {
-            callback.run(_component);
+            callback.run(_coreComponent);
             return;
         }
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/InternalComponentResourcesImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/InternalComponentResourcesImpl.java?view=diff&rev=489162&r1=489161&r2=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/InternalComponentResourcesImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/InternalComponentResourcesImpl.java Wed Dec 20 10:06:37 2006
@@ -38,7 +38,6 @@
  * resources to the component, including access to its parameters, parameter bindings, and
  * persistent field data.
  */
-
 public class InternalComponentResourcesImpl implements InternalComponentResources
 {
     private final ComponentModel _componentModel;
@@ -260,6 +259,11 @@
             _messages = _messagesSource.getMessages(_componentModel, getLocale());
 
         return _messages;
+    }
+
+    public Component getCoreComponent()
+    {
+        return _element.getComponent();
     }
 
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/ComponentModel.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/ComponentModel.java?view=diff&rev=489162&r1=489161&r2=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/ComponentModel.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/ComponentModel.java Wed Dec 20 10:06:37 2006
@@ -18,6 +18,7 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.tapestry.annotations.ComponentClass;
+import org.apache.tapestry.annotations.MixinAfter;
 import org.apache.tapestry.annotations.Persist;
 import org.apache.tapestry.annotations.SupportsInformalParameters;
 import org.apache.tapestry.ioc.Resource;
@@ -116,4 +117,13 @@
      *         component class
      */
     ComponentModel getParentModel();
+
+    /**
+     * Relevant for component mixins only. Indicates that the mixin behavior should occur
+     * <em>after</em> (not before) the component. Normally, this flag is set by the presence of
+     * the {@link MixinAfter} annotation.
+     * 
+     * @return true if the mixin should operate after, not before, the component
+     */
+    boolean isMixinAfter();
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableComponentModel.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableComponentModel.java?view=diff&rev=489162&r1=489161&r2=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableComponentModel.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableComponentModel.java Wed Dec 20 10:06:37 2006
@@ -12,62 +12,68 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.model;
-
-import org.apache.tapestry.annotations.Persist;
-import org.apache.tapestry.internal.InternalComponentResources;
-
-/**
- * Mutable version of {@link org.apache.tapestry.model.ComponentModel} used during the
- * transformation phase.
- */
-public interface MutableComponentModel extends ComponentModel
-{
-    /**
-     * Adds a new parameter to the model.
-     * 
-     * @param name
-     *            new, unique n`ame for the parameter
-     * @param required
-     *            if true, the parameter must be bound
-     * @throws IllegalArgumentException
-     *             if a parameter with the given name has already been defined for this model
-     */
-    void addParameter(String name, boolean required);
-
-    /**
-     * Defines a new embedded component.
-     * 
-     * @param id
-     *            the unique id for the embedded component, which must not already exist.
-     * @param type
-     *            the type of the component (posslibly blank)
-     * @param componentClassName
-     *            the fully qualified class name (derived from the field), used if the type is blank
-     * @return a mutable model allowing parameters to be set
-     */
-    MutableEmbeddedComponentModel addEmbeddedComponent(String id, String type,
-            String componentClassName);
-
-    /**
-     * Used to define the field persistence strategy for a particular field name. Returns a logical
-     * name for the field, which is guaranteed to be unique (this is necessary for handling the case
-     * where a subclass has a persistent field with the same name as a persistent field from a
-     * super-class).
-     * 
-     * @param fieldName
-     *            the name of the field which is to be made persistent
-     * @param strategy
-     *            the strategy for persisting the field, from {@link Persist#value()}
-     * @return a logical name for the field, to be used with
-     *         {@link ComponentModel#getFieldPersistenceStrategy(String)}, and with
-     *         {@link InternalComponentResources#persistFieldChange(String, Object)}, etc.
-     */
-    String setFieldPersistenceStrategy(String fieldName, String strategy);
-
-    /** Adds a mixin to the component's implementation. */
-    void addMixinClassName(String mixinClassName);
-    
-    /** Sets the internal flag to indicate that this model (and all models that extend from it) support informal parameters. */
-    void enableSupportsInformalParameters();
-}
+package org.apache.tapestry.model;
+
+import org.apache.tapestry.annotations.Persist;
+import org.apache.tapestry.internal.InternalComponentResources;
+
+/**
+ * Mutable version of {@link org.apache.tapestry.model.ComponentModel} used during the
+ * transformation phase.
+ */
+public interface MutableComponentModel extends ComponentModel
+{
+    /**
+     * Adds a new parameter to the model.
+     * 
+     * @param name
+     *            new, unique n`ame for the parameter
+     * @param required
+     *            if true, the parameter must be bound
+     * @throws IllegalArgumentException
+     *             if a parameter with the given name has already been defined for this model
+     */
+    void addParameter(String name, boolean required);
+
+    /**
+     * Defines a new embedded component.
+     * 
+     * @param id
+     *            the unique id for the embedded component, which must not already exist.
+     * @param type
+     *            the type of the component (posslibly blank)
+     * @param componentClassName
+     *            the fully qualified class name (derived from the field), used if the type is blank
+     * @return a mutable model allowing parameters to be set
+     */
+    MutableEmbeddedComponentModel addEmbeddedComponent(String id, String type,
+            String componentClassName);
+
+    /**
+     * Used to define the field persistence strategy for a particular field name. Returns a logical
+     * name for the field, which is guaranteed to be unique (this is necessary for handling the case
+     * where a subclass has a persistent field with the same name as a persistent field from a
+     * super-class).
+     * 
+     * @param fieldName
+     *            the name of the field which is to be made persistent
+     * @param strategy
+     *            the strategy for persisting the field, from {@link Persist#value()}
+     * @return a logical name for the field, to be used with
+     *         {@link ComponentModel#getFieldPersistenceStrategy(String)}, and with
+     *         {@link InternalComponentResources#persistFieldChange(String, Object)}, etc.
+     */
+    String setFieldPersistenceStrategy(String fieldName, String strategy);
+
+    /** Adds a mixin to the component's implementation. */
+    void addMixinClassName(String mixinClassName);
+
+    /**
+     * Sets the internal flag to indicate that this model (and all models that extend from it)
+     * support informal parameters.
+     */
+    void enableSupportsInformalParameters();
+
+    /** Changes the value of the mixinAfter flag. The default value is false. */
+    void setMixinAfter(boolean mixinAfter);
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/Component.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/Component.java?view=diff&rev=489162&r1=489161&r2=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/Component.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/Component.java Wed Dec 20 10:06:37 2006
@@ -12,88 +12,76 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.runtime;
-
-import org.apache.tapestry.MarkupWriter;
-import org.apache.tapestry.annotations.OnEvent;
-
-/**
- * Interface that defining the lifecycle of a component, within a page, allowing for callbacks into
- * the component for many different events. This interface is part of the public API for Tapestry,
- * but is <em>not</em> expected to be directly implemented by component classes; it should only be
- * implemented as part of the component class transformation process.
- * <p>
- * Most of the methods are related to render phases; see the corresponding annotations and component
- * rendering documentation to see how they relate to each other.
- * <p>
- * This interface is likely to change without notice.
- */
-public interface Component extends ComponentResourcesAware, PageLifecycleListener
-{
-
-    /**
-     * Lifecycle method invoked at the end of the
-     * {@link org.apache.tapestry.annotations.CleanupRender} render phase. There is no annotation
-     * for this method, it is part of CleanupRender, but is always invoked. Its specific use is to
-     * allow components to clean up cached parameter values.
-     */
-    void postRenderCleanup();
-
-    /**
-     * Invoked before rendering a component (or its template).
-     */
-    void setupRender(MarkupWriter writer, LifecycleEvent<Boolean> event);
-
-    /**
-     * Invoked just before {@link #beginRender(MarkupWriter, LifecycleEvent)
-     */
-
-    void preBeginRender(MarkupWriter writer, LifecycleEvent<Boolean> event);
-
-    /**
-     * Invoked to allow a component to render its tag (start tag and attributes).
-     */
-    void beginRender(MarkupWriter writer, LifecycleEvent<Boolean> event);
-
-    /**
-     * Invoked just after {@link #beginRender(MarkupWriter, LifecycleEvent)}, typically to allow a
-     * mixin to write additional attributes into the component's tag.
-     */
-    void postBeginRender(MarkupWriter writer, LifecycleEvent<Boolean> event);
-
-    /**
-     * This phase is only invoked for components with templates.
-     */
-    void beforeRenderTemplate(MarkupWriter writer, LifecycleEvent<Boolean> event);
-
-    /** Invoked after rendering the template for a component (only for components with a template). */
-    void afterRenderTemplate(MarkupWriter writer, LifecycleEvent<Boolean> event);
-
-    /**
-     * Invoked just before rendering the body of component.
-     */
-    void beforeRenderBody(MarkupWriter writer, LifecycleEvent<Boolean> event);
-
-    /** Invoked just after rendering the body of the component. */
-    void afterRenderBody(MarkupWriter writer, LifecycleEvent<Boolean> event);
-
-    /**
-     * Generally used to write the close tag matching any open tag written by
-     * {@link #beginRender(MarkupWriter, LifecycleEvent)}.
-     */
-    void afterRender(MarkupWriter writer, LifecycleEvent<Boolean> event);
-
-    /**
-     * Generally used to perform final cleanup of the component after rendering.
-     */
-    void cleanupRender(MarkupWriter writer, LifecycleEvent<Boolean> event);
-
-    /**
-     * Invoked to handle a component event. Methods with the {@link OnEvent} annotation will be
-     * invoked until one returns a non-null value.
-     * 
-     * @param event
-     * @return true if any handler was found (and invoked), false otherwise
-     */
-    boolean handleComponentEvent(ComponentEvent event);
-}
+package org.apache.tapestry.runtime;
+
+import org.apache.tapestry.MarkupWriter;
+import org.apache.tapestry.annotations.OnEvent;
+
+/**
+ * Interface that defining the lifecycle of a component, within a page, allowing for callbacks into
+ * the component for many different events. This interface is part of the public API for Tapestry,
+ * but is <em>not</em> expected to be directly implemented by component classes; it should only be
+ * implemented as part of the component class transformation process.
+ * <p>
+ * Most of the methods are related to render phases; see the corresponding annotations and component
+ * rendering documentation to see how they relate to each other.
+ * <p>
+ * This interface is likely to change without notice.
+ */
+public interface Component extends ComponentResourcesAware, PageLifecycleListener
+{
+
+    /**
+     * Lifecycle method invoked at the end of the
+     * {@link org.apache.tapestry.annotations.CleanupRender} render phase. There is no annotation
+     * for this method, it is part of CleanupRender, but is always invoked. Its specific use is to
+     * allow components to clean up cached parameter values.
+     */
+    void postRenderCleanup();
+
+    /**
+     * Invoked before rendering a component (or its template).
+     */
+    void setupRender(MarkupWriter writer, LifecycleEvent<Boolean> event);
+
+    /**
+     * Invoked to allow a component to render its tag (start tag and attributes).
+     */
+    void beginRender(MarkupWriter writer, LifecycleEvent<Boolean> event);
+
+    /**
+     * This phase is only invoked for components with templates.
+     */
+    void beforeRenderTemplate(MarkupWriter writer, LifecycleEvent<Boolean> event);
+
+    /** Invoked after rendering the template for a component (only for components with a template). */
+    void afterRenderTemplate(MarkupWriter writer, LifecycleEvent<Boolean> event);
+
+    /**
+     * Invoked just before rendering the body of component.
+     */
+    void beforeRenderBody(MarkupWriter writer, LifecycleEvent<Boolean> event);
+
+    /** Invoked just after rendering the body of the component. */
+    void afterRenderBody(MarkupWriter writer, LifecycleEvent<Boolean> event);
+
+    /**
+     * Generally used to write the close tag matching any open tag written by
+     * {@link #beginRender(MarkupWriter, LifecycleEvent)}.
+     */
+    void afterRender(MarkupWriter writer, LifecycleEvent<Boolean> event);
+
+    /**
+     * Generally used to perform final cleanup of the component after rendering.
+     */
+    void cleanupRender(MarkupWriter writer, LifecycleEvent<Boolean> event);
+
+    /**
+     * Invoked to handle a component event. Methods with the {@link OnEvent} annotation will be
+     * invoked until one returns a non-null value.
+     * 
+     * @param event
+     * @return true if any handler was found (and invoked), false otherwise
+     */
+    boolean handleComponentEvent(ComponentEvent event);
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java?view=diff&rev=489162&r1=489161&r2=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java Wed Dec 20 10:06:37 2006
@@ -36,8 +36,6 @@
 import org.apache.tapestry.annotations.BeginRender;
 import org.apache.tapestry.annotations.CleanupRender;
 import org.apache.tapestry.annotations.InjectPage;
-import org.apache.tapestry.annotations.PostBeginRender;
-import org.apache.tapestry.annotations.PreBeginRender;
 import org.apache.tapestry.annotations.SetupRender;
 import org.apache.tapestry.internal.InternalConstants;
 import org.apache.tapestry.internal.bindings.ComponentBindingFactory;
@@ -70,11 +68,13 @@
 import org.apache.tapestry.internal.services.InfrastructureImpl;
 import org.apache.tapestry.internal.services.InfrastructureManagerImpl;
 import org.apache.tapestry.internal.services.InjectAnonymousWorker;
+import org.apache.tapestry.internal.services.InjectComponentWorker;
 import org.apache.tapestry.internal.services.InjectNamedWorker;
 import org.apache.tapestry.internal.services.InjectPageWorker;
 import org.apache.tapestry.internal.services.InternalModule;
 import org.apache.tapestry.internal.services.LinkFactory;
 import org.apache.tapestry.internal.services.MarkupWriterImpl;
+import org.apache.tapestry.internal.services.MixinAfterWorker;
 import org.apache.tapestry.internal.services.MixinWorker;
 import org.apache.tapestry.internal.services.OnEventWorker;
 import org.apache.tapestry.internal.services.PageLinkHandler;
@@ -608,6 +608,7 @@
                 new InjectAnonymousWorker(locator, injectionProvider),
                 "after:InjectNamed");
         configuration.add("AssetInject", new AssetInjectWorker(assetSource), "before:InjectNamed");
+        configuration.add("MixinAfter", new MixinAfterWorker());
 
         configuration.add("Parameter", new ParameterWorker());
         configuration.add("Component", new ComponentWorker(resolver));
@@ -616,22 +617,13 @@
         configuration.add("OnEvent", new OnEventWorker());
         configuration.add("SupportsInformalParameters", new SupportsInformalParametersWorker());
         configuration.add("InjectPage", new InjectPageWorker(requestPageCache));
+        configuration.add("InjectComponent", new InjectComponentWorker());
 
         // Workers for the component rendering state machine methods; this is in typical
         // execution order.
 
         add(configuration, TransformConstants.SETUP_RENDER_SIGNATURE, SetupRender.class, false);
-        add(
-                configuration,
-                TransformConstants.PRE_BEGIN_RENDER_SIGNATURE,
-                PreBeginRender.class,
-                false);
         add(configuration, TransformConstants.BEGIN_RENDER_SIGNATURE, BeginRender.class, false);
-        add(
-                configuration,
-                TransformConstants.POST_BEGIN_RENDER_SIGNATURE,
-                PostBeginRender.class,
-                false);
         add(
                 configuration,
                 TransformConstants.BEFORE_RENDER_TEMPLATE_SIGNATURE,

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TransformConstants.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TransformConstants.java?view=diff&rev=489162&r1=489161&r2=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TransformConstants.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TransformConstants.java Wed Dec 20 10:06:37 2006
@@ -12,148 +12,131 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.services;
-
-import java.lang.reflect.Modifier;
-
-import org.apache.tapestry.MarkupWriter;
-import org.apache.tapestry.annotations.PostBeginRender;
-import org.apache.tapestry.annotations.PreBeginRender;
-import org.apache.tapestry.runtime.Component;
-import org.apache.tapestry.runtime.ComponentEvent;
-import org.apache.tapestry.runtime.LifecycleEvent;
-
-/**
- * Constants used by implementations of
- * {@link org.apache.tapestry.services.ComponentClassTransformWorker}.
- */
-public final class TransformConstants
-{
-    // Shared parameters of a whole bunch of lifecycle methods, representing the different
-    // component render states.
-    private static final String[] RENDER_PHASE_METHOD_PARAMETERS =
-    { MarkupWriter.class.getName(), LifecycleEvent.class.getName() };
-
-    /**
-     * Signature for
-     * {@link org.apache.tapestry.runtime.Component#handleComponentEvent(ComponentEvent event)
-     * 
-     * @see org.apache.tapestry.annotations.OnEvent
-     */
-    public static final MethodSignature HANDLE_COMPONENT_EVENT = new MethodSignature(
-            Modifier.PUBLIC, "boolean", "handleComponentEvent", new String[]
-            { ComponentEvent.class.getName() }, null);
-
-    /**
-     * Signature for
-     * {@link org.apache.tapestry.runtime.PageLifecycleListener#containingPageDidLoad()}.
-     */
-    public static final MethodSignature CONTAINING_PAGE_DID_LOAD_SIGNATURE = new MethodSignature(
-            "containingPageDidLoad");
-
-    /** Signature for {@link org.apache.tapestry.runtime.Component#postRenderCleanup()}. */
-    public static final MethodSignature POST_RENDER_CLEANUP_SIGNATURE = new MethodSignature(
-            "postRenderCleanup");
-
-    /**
-     * Signature for
-     * {@link org.apache.tapestry.runtime.PageLifecycleListener#containingPageDidDetach()}.
-     */
-    public static final MethodSignature CONTAINING_PAGE_DID_DETACH_SIGNATURE = new MethodSignature(
-            "containingPageDidDetach");
-
-    /**
-     * Signature for
-     * {@link org.apache.tapestry.runtime.PageLifecycleListener#containingPageDidAttach()}.
-     */
-    public static final MethodSignature CONTAINING_PAGE_DID_ATTACH_SIGNATURE = new MethodSignature(
-            "containingPageDidAttach");
-
-    /**
-     * Signature for
-     * {@link org.apache.tapestry.runtime.Component#setupRender(MarkupWriter, LifecycleEvent)}.
-     * 
-     * @see org.apache.tapestry.annotations.SetupRender
-     */
-    public static final MethodSignature SETUP_RENDER_SIGNATURE = renderPhaseSignature("setupRender");
-
-    /**
-     * Signature for
-     * {@link org.apache.tapestry.runtime.Component#beginRender(MarkupWriter, LifecycleEvent)}.
-     * 
-     * @see org.apache.tapestry.annotations.BeginRender
-     */
-    public static final MethodSignature BEGIN_RENDER_SIGNATURE = renderPhaseSignature("beginRender");
-
-    /**
-     * Signature for {@link Component#preBeginRender(MarkupWriter, LifecycleEvent)}.
-     * 
-     * @see PreBeginRender
-     */
-    public static final MethodSignature PRE_BEGIN_RENDER_SIGNATURE = renderPhaseSignature("preBeginRender");
-    
-    
-    /** Signature for {@link Component#postBeginRender(MarkupWriter, LifecycleEvent)}.
-     * 
-     * @see PostBeginRender
-     */
-    public static final MethodSignature POST_BEGIN_RENDER_SIGNATURE = renderPhaseSignature("postBeginRender");
-    
-    /**
-     * Signature for
-     * {@link org.apache.tapestry.runtime.Component#beforeRenderTemplate(MarkupWriter, LifecycleEvent)}.
-     * 
-     * @see org.apache.tapestry.annotations.BeforeRenderTemplate
-     */
-    public static MethodSignature BEFORE_RENDER_TEMPLATE_SIGNATURE = renderPhaseSignature("beforeRenderTemplate");
-
-    /**
-     * Signature for
-     * {@link org.apache.tapestry.runtime.Component#afterRenderTemplate(MarkupWriter, LifecycleEvent)}.
-     * 
-     * @see org.apache.tapestry.annotations.BeforeRenderTemplate
-     */
-    public static MethodSignature AFTER_RENDER_TEMPLATE_SIGNATURE = renderPhaseSignature("afterRenderTemplate");
-
-    /**
-     * Signature for
-     * {@link org.apache.tapestry.runtime.Component#beforeRenderBody(MarkupWriter, LifecycleEvent)}.
-     * 
-     * @see org.apache.tapestry.annotations.BeforeRenderBody
-     */
-    public static final MethodSignature BEFORE_RENDER_BODY_SIGNATURE = renderPhaseSignature("beforeRenderBody");
-
-    /**
-     * Signature for
-     * {@link org.apache.tapestry.runtime.Component#afterRenderBody(MarkupWriter, LifecycleEvent)}.
-     * 
-     * @see org.apache.tapestry.annotations.AfterRenderBody
-     */
-    public static final MethodSignature AFTER_RENDER_BODY_SIGNATURE = renderPhaseSignature("afterRenderBody");
-
-    /**
-     * Signature for
-     * {@link org.apache.tapestry.runtime.Component#afterRender(MarkupWriter, LifecycleEvent)}
-     * 
-     * @see org.apache.tapestry.annotations.AfterRender
-     */
-    public static final MethodSignature AFTER_RENDER_SIGNATURE = renderPhaseSignature("afterRender");
-
-    /**
-     * Signature for
-     * {@link org.apache.tapestry.runtime.Component#cleanupRender(MarkupWriter, LifecycleEvent)}.
-     * 
-     * @see org.apache.tapestry.annotations.CleanupRender
-     */
-    public static final MethodSignature CLEANUP_RENDER_SIGNATURE = renderPhaseSignature("cleanupRender");
-
-    private TransformConstants()
-    {
-    }
-
-    private static MethodSignature renderPhaseSignature(String name)
-    {
-        return new MethodSignature(Modifier.PUBLIC, "void", name, RENDER_PHASE_METHOD_PARAMETERS,
-                null);
-    }
-}
+package org.apache.tapestry.services;
+
+import java.lang.reflect.Modifier;
+
+import org.apache.tapestry.MarkupWriter;
+import org.apache.tapestry.runtime.ComponentEvent;
+import org.apache.tapestry.runtime.LifecycleEvent;
+
+/**
+ * Constants used by implementations of
+ * {@link org.apache.tapestry.services.ComponentClassTransformWorker}.
+ */
+public final class TransformConstants
+{
+    // Shared parameters of a whole bunch of lifecycle methods, representing the different
+    // component render states.
+    private static final String[] RENDER_PHASE_METHOD_PARAMETERS =
+    { MarkupWriter.class.getName(), LifecycleEvent.class.getName() };
+
+    /**
+     * Signature for
+     * {@link org.apache.tapestry.runtime.Component#handleComponentEvent(ComponentEvent event)
+     * 
+     * @see org.apache.tapestry.annotations.OnEvent
+     */
+    public static final MethodSignature HANDLE_COMPONENT_EVENT = new MethodSignature(
+            Modifier.PUBLIC, "boolean", "handleComponentEvent", new String[]
+            { ComponentEvent.class.getName() }, null);
+
+    /**
+     * Signature for
+     * {@link org.apache.tapestry.runtime.PageLifecycleListener#containingPageDidLoad()}.
+     */
+    public static final MethodSignature CONTAINING_PAGE_DID_LOAD_SIGNATURE = new MethodSignature(
+            "containingPageDidLoad");
+
+    /** Signature for {@link org.apache.tapestry.runtime.Component#postRenderCleanup()}. */
+    public static final MethodSignature POST_RENDER_CLEANUP_SIGNATURE = new MethodSignature(
+            "postRenderCleanup");
+
+    /**
+     * Signature for
+     * {@link org.apache.tapestry.runtime.PageLifecycleListener#containingPageDidDetach()}.
+     */
+    public static final MethodSignature CONTAINING_PAGE_DID_DETACH_SIGNATURE = new MethodSignature(
+            "containingPageDidDetach");
+
+    /**
+     * Signature for
+     * {@link org.apache.tapestry.runtime.PageLifecycleListener#containingPageDidAttach()}.
+     */
+    public static final MethodSignature CONTAINING_PAGE_DID_ATTACH_SIGNATURE = new MethodSignature(
+            "containingPageDidAttach");
+
+    /**
+     * Signature for
+     * {@link org.apache.tapestry.runtime.Component#setupRender(MarkupWriter, LifecycleEvent)}.
+     * 
+     * @see org.apache.tapestry.annotations.SetupRender
+     */
+    public static final MethodSignature SETUP_RENDER_SIGNATURE = renderPhaseSignature("setupRender");
+
+    /**
+     * Signature for
+     * {@link org.apache.tapestry.runtime.Component#beginRender(MarkupWriter, LifecycleEvent)}.
+     * 
+     * @see org.apache.tapestry.annotations.BeginRender
+     */
+    public static final MethodSignature BEGIN_RENDER_SIGNATURE = renderPhaseSignature("beginRender");
+
+    /**
+     * Signature for
+     * {@link org.apache.tapestry.runtime.Component#beforeRenderTemplate(MarkupWriter, LifecycleEvent)}.
+     * 
+     * @see org.apache.tapestry.annotations.BeforeRenderTemplate
+     */
+    public static MethodSignature BEFORE_RENDER_TEMPLATE_SIGNATURE = renderPhaseSignature("beforeRenderTemplate");
+
+    /**
+     * Signature for
+     * {@link org.apache.tapestry.runtime.Component#afterRenderTemplate(MarkupWriter, LifecycleEvent)}.
+     * 
+     * @see org.apache.tapestry.annotations.BeforeRenderTemplate
+     */
+    public static MethodSignature AFTER_RENDER_TEMPLATE_SIGNATURE = renderPhaseSignature("afterRenderTemplate");
+
+    /**
+     * Signature for
+     * {@link org.apache.tapestry.runtime.Component#beforeRenderBody(MarkupWriter, LifecycleEvent)}.
+     * 
+     * @see org.apache.tapestry.annotations.BeforeRenderBody
+     */
+    public static final MethodSignature BEFORE_RENDER_BODY_SIGNATURE = renderPhaseSignature("beforeRenderBody");
+
+    /**
+     * Signature for
+     * {@link org.apache.tapestry.runtime.Component#afterRenderBody(MarkupWriter, LifecycleEvent)}.
+     * 
+     * @see org.apache.tapestry.annotations.AfterRenderBody
+     */
+    public static final MethodSignature AFTER_RENDER_BODY_SIGNATURE = renderPhaseSignature("afterRenderBody");
+
+    /**
+     * Signature for
+     * {@link org.apache.tapestry.runtime.Component#afterRender(MarkupWriter, LifecycleEvent)}
+     * 
+     * @see org.apache.tapestry.annotations.AfterRender
+     */
+    public static final MethodSignature AFTER_RENDER_SIGNATURE = renderPhaseSignature("afterRender");
+
+    /**
+     * Signature for
+     * {@link org.apache.tapestry.runtime.Component#cleanupRender(MarkupWriter, LifecycleEvent)}.
+     * 
+     * @see org.apache.tapestry.annotations.CleanupRender
+     */
+    public static final MethodSignature CLEANUP_RENDER_SIGNATURE = renderPhaseSignature("cleanupRender");
+
+    private TransformConstants()
+    {
+    }
+
+    private static MethodSignature renderPhaseSignature(String name)
+    {
+        return new MethodSignature(Modifier.PUBLIC, "void", name, RENDER_PHASE_METHOD_PARAMETERS,
+                null);
+    }
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties?view=diff&rev=489162&r1=489161&r2=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties Wed Dec 20 10:06:37 2006
@@ -62,4 +62,5 @@
 failure-reading-messages=Unable to read message catalog from %s: %s
 unknown-asset-prefix=Unknown prefix for asset path '%s'.
 asset-does-not-exist=Unable to locate asset '%s' (the file does not exist).
-wrong-asset-digest=The asset digest in the request does not match the actual digest for asset '%s'. This indicates that the content of the asset has changed between requests. 
\ No newline at end of file
+wrong-asset-digest=The asset digest in the request does not match the actual digest for asset '%s'. This indicates that the content of the asset has changed between requests. 
+component-not-assignable-to-field=Component %s is not assignable to field %s (of type %s).
\ No newline at end of file

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html?view=diff&rev=489162&r1=489161&r2=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html Wed Dec 20 10:06:37 2006
@@ -54,6 +54,9 @@
             <li>
                 <a href="ExpansionSubclass.html">ExpansionSubclass</a> -- components can inherit templates from base classes
             </li>
+            <li>
+                <a href="InjectComponentMismatch.html">InjectComponentMismatch</a> -- check error reporting when @InjectComponent doesn't match the actual field type
+            </li>
             </ul>
         </p>
     </body>

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java?view=diff&rev=489162&r1=489161&r2=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java Wed Dec 20 10:06:37 2006
@@ -21,6 +21,8 @@
 import java.io.InputStream;
 import java.net.URL;
 
+import org.apache.tapestry.corelib.mixins.RenderDisabled;
+import org.apache.tapestry.internal.services.InjectComponentWorker;
 import org.apache.tapestry.ioc.Resource;
 import org.apache.tapestry.ioc.internal.util.ClasspathResource;
 import org.openqa.selenium.server.SeleniumServer;
@@ -52,7 +54,7 @@
     private SeleniumServer _server;
 
     private JettyRunner _jettyRunner;
-    
+
     @BeforeClass
     public void startupBackground() throws Exception
     {
@@ -239,7 +241,7 @@
         clickAndWait("link=Action Page");
 
         assertFalse(_selenium.isTextPresent(YOU_CHOSE));
-        
+
         for (int i = 2; i < 5; i++)
         {
             clickAndWait("link=" + i);
@@ -370,6 +372,22 @@
         byte[] actual = readContent(classpathResource.toURL());
 
         assertEquals(downloaded, actual);
+    }
+
+    /**
+     * {@link InjectComponentWorker} is largely tested by the forms tests ({@link RenderDisabled}
+     * is built on it). This test is for the failure case, where a mixin class is used with the
+     * wrong type of component.
+     */
+    @Test
+    public void inject_component_failure() throws Exception
+    {
+        _selenium.open(BASE_URL);
+        clickAndWait("link=InjectComponentMismatch");
+
+        // And exception message:
+
+        assertTextPresent("Component org.apache.tapestry.integration.app1.pages.InjectComponentMismatch is not assignable to field org.apache.tapestry.corelib.mixins.RenderDisabled._field (of type org.apache.tapestry.Field).");
     }
 
     private byte[] readContent(URL url) throws Exception

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/InjectComponentMismatch.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/InjectComponentMismatch.java?view=auto&rev=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/InjectComponentMismatch.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/InjectComponentMismatch.java Wed Dec 20 10:06:37 2006
@@ -0,0 +1,32 @@
+// Copyright 2006 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.tapestry.integration.app1.pages;
+
+import org.apache.tapestry.Field;
+import org.apache.tapestry.annotations.ComponentClass;
+import org.apache.tapestry.annotations.Mixin;
+import org.apache.tapestry.corelib.mixins.RenderDisabled;
+
+@ComponentClass
+public class InjectComponentMismatch
+{
+    /**
+     * This mixin only works with components of type {@link Field}. That's the mismatch right
+     * there.
+     */
+    @SuppressWarnings("unused")
+    @Mixin
+    private RenderDisabled _renderDisabled;
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/DefaultComponent.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/DefaultComponent.java?view=diff&rev=489162&r1=489161&r2=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/DefaultComponent.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/DefaultComponent.java Wed Dec 20 10:06:37 2006
@@ -16,8 +16,8 @@
 
 import org.apache.tapestry.ComponentResources;
 import org.apache.tapestry.MarkupWriter;
-import org.apache.tapestry.runtime.ComponentEvent;
 import org.apache.tapestry.runtime.Component;
+import org.apache.tapestry.runtime.ComponentEvent;
 import org.apache.tapestry.runtime.LifecycleEvent;
 
 /**
@@ -88,16 +88,4 @@
     {
         // TODO Auto-generated method stub
     }
-
-    public void postBeginRender(MarkupWriter writer, LifecycleEvent<Boolean> event)
-    {
-        // TODO Auto-generated method stub
-
-    }
-
-    public void preBeginRender(MarkupWriter writer, LifecycleEvent<Boolean> event)
-    {
-        // TODO Auto-generated method stub
-    }
-
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/MixinAfterWorkerTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/MixinAfterWorkerTest.java?view=auto&rev=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/MixinAfterWorkerTest.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/MixinAfterWorkerTest.java Wed Dec 20 10:06:37 2006
@@ -0,0 +1,56 @@
+// Copyright 2006 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.tapestry.internal.services;
+
+import org.apache.tapestry.annotations.MixinAfter;
+import org.apache.tapestry.internal.test.InternalBaseTestCase;
+import org.apache.tapestry.model.MutableComponentModel;
+import org.apache.tapestry.services.ClassTransformation;
+import org.testng.annotations.Test;
+
+public class MixinAfterWorkerTest extends InternalBaseTestCase
+{
+    @Test
+    public void annotation_not_present()
+    {
+        ClassTransformation transformation = newClassTransformation();
+        MutableComponentModel model = newMutableComponentModel();
+
+        train_getAnnotation(transformation, MixinAfter.class, null);
+
+        replay();
+
+        new MixinAfterWorker().transform(transformation, model);
+
+        verify();
+    }
+
+    @Test
+    public void annotation_present()
+    {
+        ClassTransformation transformation = newClassTransformation();
+        MutableComponentModel model = newMutableComponentModel();
+        MixinAfter annotation = newMock(MixinAfter.class);
+
+        train_getAnnotation(transformation, MixinAfter.class, annotation);
+        model.setMixinAfter(true);
+
+        replay();
+
+        new MixinAfterWorker().transform(transformation, model);
+
+        verify();
+    }
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/resources/log4j.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/resources/log4j.properties?view=diff&rev=489162&r1=489161&r2=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/resources/log4j.properties (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/resources/log4j.properties Wed Dec 20 10:06:37 2006
@@ -28,4 +28,5 @@
 
 log4j.category.app=info
 log4j.category.org.apache.tapestry.integration.app1=debug
+log4j.category.org.apache.tapestry.corelib=debug
 

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/InjectComponentMismatch.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/InjectComponentMismatch.html?view=auto&rev=489162
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/InjectComponentMismatch.html (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/InjectComponentMismatch.html Wed Dec 20 10:06:37 2006
@@ -0,0 +1,7 @@
+<t:comp type="Border" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+
+    <p>
+        This page should never be rendered because of the InjectComponent mismatch inside it.
+    </p>
+
+</t:comp>