You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2006/11/20 22:10:28 UTC

svn commit: r477336 - in /tapestry/tapestry5/tapestry-core/trunk: ./ src/main/java/org/apache/tapestry/ src/main/java/org/apache/tapestry/corelib/components/ src/main/java/org/apache/tapestry/internal/bindings/ src/main/java/org/apache/tapestry/interna...

Author: hlship
Date: Mon Nov 20 13:10:27 2006
New Revision: 477336

URL: http://svn.apache.org/viewvc?view=rev&rev=477336
Log:
Add events for passivating page state (into render URLs) and activating pages (to restore page state from the context).

Added:
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/NumberSelect.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/ShowSelection.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageRenderDispatcherTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/NumberSelect.html
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ShowSelection.html
Modified:
    tapestry/tapestry5/tapestry-core/trunk/pom.xml
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentEventHandler.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/TapestryConstants.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Form.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PropertyAccessImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentSourceImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/DefaultRequestExceptionHandler.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/OnEventWorker.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageRenderDispatcher.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/StringEventHandler.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/internal/structure/Page.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java
    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/integration/app1/pages/Wilma.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentSourceImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java

Modified: tapestry/tapestry5/tapestry-core/trunk/pom.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/pom.xml?view=diff&rev=477336&r1=477335&r2=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/pom.xml (original)
+++ tapestry/tapestry5/tapestry-core/trunk/pom.xml Mon Nov 20 13:10:27 2006
@@ -131,10 +131,4 @@
              </plugin>
         </plugins>
     </reporting>
-    <distributionManagement>
-        <site>
-            <id>tapestry</id>
-            <url>scpexe://apache.org/www/tapestry.apache.org/tapestry5</url>
-        </site>
-    </distributionManagement>
 </project>

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentEventHandler.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentEventHandler.java?view=diff&rev=477336&r1=477335&r2=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentEventHandler.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentEventHandler.java Mon Nov 20 13:10:27 2006
@@ -12,31 +12,31 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry;
-
-import org.apache.tapestry.runtime.Component;
-import org.apache.tapestry.runtime.ComponentEvent;
-
-/**
- * Handler for a {@link ComponentEvent}, notified when a non-null value is returned from some event
- * handler method.
- * <p>
- * TODO: Allow handler to return boolean (abort vs. don't abort)
- * <p>
- * TODO: Multiple handlers for different result types / strategy pattern?
- */
-public interface ComponentEventHandler<T>
-{
-    /**
-     * Invoked to handle a non-null event handler method result. The handler should determine
-     * whether the value is acceptible, and throw an exception if not.
-     * 
-     * @param result
-     *            the result value provided by a method
-     * @param component
-     *            the component from which the result was obtained
-     * @param methodDescription
-     *            a string description of the class and method name (used when errors occur).
-     */
-    void handleResult(T result, Component component, String methodDescription);
-}
+package org.apache.tapestry;
+
+import org.apache.tapestry.runtime.Component;
+import org.apache.tapestry.runtime.ComponentEvent;
+
+/**
+ * Handler for a {@link ComponentEvent}, notified when a non-null value is returned from some event
+ * handler method.
+ * <p>
+ * TODO: Allow handler to return boolean (abort vs. don't abort)
+ * <p>
+ * TODO: Multiple handlers for different result types / strategy pattern?
+ */
+public interface ComponentEventHandler<T>
+{
+    /**
+     * Invoked to handle a non-null event handler method result. The handler should determine
+     * whether the value is acceptible, and throw an exception if not.
+     * 
+     * @param result
+     *            the result value provided by a method
+     * @param component
+     *            the component from which the result was obtained
+     * @param methodDescription
+     *            a string description of the class and method name (used when errors occur).
+     */
+    void handleResult(T result, Component component, String methodDescription);
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java?view=diff&rev=477336&r1=477335&r2=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java Mon Nov 20 13:10:27 2006
@@ -12,85 +12,88 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry;
-
-import org.apache.commons.logging.Log;
-import org.apache.tapestry.internal.structure.ComponentPageElement;
-import org.apache.tapestry.model.ComponentModel;
-import org.apache.tapestry.services.ComponentSource;
-
-/**
- * Operations shared by {@link ComponentResources} and {@link ComponentPageElement}.
- */
-public interface ComponentResourcesCommon
-{
-    /**
-     * Returns the id of the component. The id will be unique within the component's immediate
-     * container. For a page's root component, the value null is returned.
-     */
-    String getId();
-
-    /**
-     * Return a string consisting the concatinated ids of all containing components, separated by
-     * periods. I.e., "foo.bar.baz". Returns null for a page.
-     */
-
-    String getNestedId();
-
-    /**
-     * Creates a link that will trigger an action for this component.
-     * 
-     * @param action
-     *            a name for the action associated with the link
-     * @param forForm
-     *            if true, the link will be used as the action for an HTML form submission, which
-     *            may affect what information is encoded into the link
-     * @param context
-     *            additional objects to be encoded into the path portion of the link; each is
-     *            converted to a string an URI encoded
-     */
-    Link createActionLink(String action, boolean forForm, Object... context);
-
-    /**
-     * Returns a string consisting of the fully qualified class name of the containing page, and the
-     * {@link #getNestedId() nested id} of this component, separated by a colon. I.e.,
-     * "com.foo.pages.MyPage:foo.bar.baz". For a page, returns just the page class name.
-     * <p>
-     * This value is often used to obtain an equivalent component instance in a later request.
-     * 
-     * @see ComponentSource
-     */
-
-    String getCompleteId();
-
-    /**
-     * Triggers a component event. A search for an event handling method will occur, first in the
-     * component, then its container, and so on.
-     * 
-     * @param eventType
-     *            event type (as determined from the request, or otherwise by design)
-     * @param context
-     *            the context (as extracted from the request, or provided by the triggering
-     *            component); these values may be provided to event handler methods via their
-     *            parameters
-     * @param handler
-     *            the handler to be informed of the result
-     * @return true if any event handler was invoked (even if no event handler method returns a
-     *         non-null value)
-     */
-    boolean triggerEvent(String eventType, Object[] context, ComponentEventHandler handler);
-
-    /**
-     * Returns true if the component is currently rendering, false otherwise. This is most often
-     * used to determine if parameter values should be cached.
-     */
-    boolean isRendering();
-
-    /**
-     * Returns the log instance associated with the component (which is based on the component or
-     * mixin's class name).
-     * 
-     * @see ComponentModel#getLog()
-     */
-    Log getLog();
-}
+package org.apache.tapestry;
+
+import org.apache.commons.logging.Log;
+import org.apache.tapestry.internal.structure.ComponentPageElement;
+import org.apache.tapestry.model.ComponentModel;
+import org.apache.tapestry.services.ComponentSource;
+
+/**
+ * Operations shared by {@link ComponentResources} and {@link ComponentPageElement}.
+ */
+public interface ComponentResourcesCommon
+{
+    /**
+     * Returns the id of the component. The id will be unique within the component's immediate
+     * container. For a page's root component, the value null is returned.
+     */
+    String getId();
+
+    /**
+     * Return a string consisting the concatinated ids of all containing components, separated by
+     * periods. I.e., "foo.bar.baz". Returns null for a page.
+     */
+
+    String getNestedId();
+
+    /**
+     * Creates a link that will trigger an action for this component.
+     * 
+     * @param action
+     *            a name for the action associated with the link
+     * @param forForm
+     *            if true, the link will be used as the action for an HTML form submission, which
+     *            may affect what information is encoded into the link
+     * @param context
+     *            additional objects to be encoded into the path portion of the link; each is
+     *            converted to a string an URI encoded
+     */
+    Link createActionLink(String action, boolean forForm, Object... context);
+
+    /**
+     * Returns a string consisting of the fully qualified class name of the containing page, and the
+     * {@link #getNestedId() nested id} of this component, separated by a colon. I.e.,
+     * "com.foo.pages.MyPage:foo.bar.baz". For a page, returns just the page class name.
+     * <p>
+     * This value is often used to obtain an equivalent component instance in a later request.
+     * 
+     * @see ComponentSource
+     */
+
+    String getCompleteId();
+
+    /**
+     * Triggers a component event. A search for an event handling method will occur, first in the
+     * component, then its container, and so on. When a matching event handler method is located, it
+     * is invoked. If the method returns a value, the value is passed to the handler (if handler is
+     * null, then it is an error for a method to return a non-null vavlue).
+     * 
+     * @param eventType
+     *            event type (as determined from the request, or otherwise by design)
+     * @param context
+     *            the context (as extracted from the request, or provided by the triggering
+     *            component); these values may be provided to event handler methods via their
+     *            parameters
+     * @param handler
+     *            the handler to be informed of the result, or null if the event is a notification
+     *            that does not support return values from event handler methods
+     * @return true if any event handler was invoked (even if no event handler method returns a
+     *         non-null value)
+     */
+    boolean triggerEvent(String eventType, Object[] context, ComponentEventHandler handler);
+
+    /**
+     * Returns true if the component is currently rendering, false otherwise. This is most often
+     * used to determine if parameter values should be cached.
+     */
+    boolean isRendering();
+
+    /**
+     * Returns the log instance associated with the component (which is based on the component or
+     * mixin's class name).
+     * 
+     * @see ComponentModel#getLog()
+     */
+    Log getLog();
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/TapestryConstants.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/TapestryConstants.java?view=diff&rev=477336&r1=477335&r2=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/TapestryConstants.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/TapestryConstants.java Mon Nov 20 13:10:27 2006
@@ -12,17 +12,32 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry;
-
-/**
- * Collection of common constant values used throughout Tapestry.
- */
-public final class TapestryConstants
-{
-    /** Default client event name, used in most situations. */
-    public static final String DEFAULT_EVENT = "action";
-
-    private TapestryConstants()
-    {
-    }
-}
+package org.apache.tapestry;
+
+/**
+ * Collection of common constant values used throughout Tapestry.
+ */
+public final class TapestryConstants
+{
+    /** Default client event name, used in most situations. */
+    public static final String DEFAULT_EVENT = "action";
+
+    /**
+     * Event triggered when a page is activated (for rendering). The component event handler will be
+     * passed the context provided by the passivate event.
+     */
+
+    public static final String ACTIVATE_EVENT = "activate";
+
+    /**
+     * Event triggered when a link for a page is generated. The event handler for the page may
+     * provide an object, or an array of objects, as the context for the page. These values will
+     * become part of the page's context, and will be provided back when the page is activated.
+     */
+    
+    public static final String PASSIVATE_EVENT = "passivate";
+
+    private TapestryConstants()
+    {
+    }
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Form.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Form.java?view=diff&rev=477336&r1=477335&r2=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Form.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Form.java Mon Nov 20 13:10:27 2006
@@ -12,207 +12,195 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.corelib.components;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-
-import org.apache.tapestry.ComponentAction;
-import org.apache.tapestry.ComponentEventHandler;
-import org.apache.tapestry.ComponentResources;
-import org.apache.tapestry.Link;
-import org.apache.tapestry.MarkupWriter;
-import org.apache.tapestry.TapestryConstants;
-import org.apache.tapestry.annotations.AfterRender;
-import org.apache.tapestry.annotations.BeginRender;
-import org.apache.tapestry.annotations.CleanupRender;
-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.OnEvent;
-import org.apache.tapestry.annotations.SetupRender;
-import org.apache.tapestry.corelib.mixins.RenderInformals;
-import org.apache.tapestry.dom.Element;
-import org.apache.tapestry.internal.util.AcceptVoidEventHandler;
-import org.apache.tapestry.internal.util.Base64ObjectInputStream;
-import org.apache.tapestry.internal.util.Base64ObjectOutputStream;
-import org.apache.tapestry.runtime.Component;
-import org.apache.tapestry.services.ComponentSource;
-import org.apache.tapestry.services.Environment;
-import org.apache.tapestry.services.FormSupport;
-import org.apache.tapestry.services.PageRenderSupport;
-import org.apache.tapestry.services.WebRequest;
-
-/**
- * An HTML form, which will enclose other components to render out the various types of fields.
- */
-@ComponentClass
-public class Form
-{
-    /**
-     * Invoked to let the containing component(s) prepare for the form rendering or the form
-     * submission.
-     */
-    public static final String PREPARE_EVENT = "prepare";
-
-    /** Event type for a notification after the form has submitted. */
-    public static final String SUBMIT = "submit";
-
-    /**
-     * Query parameter name storing form data (the serialized commands needed to process a form
-     * submission).
-     */
-    public static final String FORM_DATA = "t:formdata";
-
-    @Inject("infrastructure:environment")
-    private Environment _environment;
-
-    @Inject
-    private ComponentResources _resources;
-
-    @Environmental
-    private PageRenderSupport _pageRenderSupport;
-
-    @Inject("infrastructure:request")
-    private WebRequest _request;
-
-    @Inject("infrastructure:componentSource")
-    private ComponentSource _source;
-
-    // Collects a stream of component actions. Each action goes in as a UTF string (the component
-    // component id),
-    // followed by a ComponentAction
-
-    private Base64ObjectOutputStream _actions;
-
-    
-    @SuppressWarnings("unused")
-    @Mixin
-    private RenderInformals _renderInformals;
-    
-    @SetupRender
-    void setup()
-    {
-        try
-        {
-            _actions = new Base64ObjectOutputStream();
-        }
-        catch (IOException ex)
-        {
-            throw new RuntimeException(ex);
-        }
-
-        FormSupport support = new FormSupportImpl(_actions);
-
-        // TODO: Forms should not allow to nest. Perhaps a set() method instead of a push() method
-        // for this kind of check?
-
-        _environment.push(FormSupport.class, support);
-
-        // Now that the environment is setup.
-
-        fireNotification(PREPARE_EVENT);
-    }
-
-    private void fireNotification(String eventType)
-    {
-        // Use the handler that rejects all non-null return values.
-
-        ComponentEventHandler handler = new AcceptVoidEventHandler(eventType, _resources
-                .getCompleteId());
-
-        _resources.triggerEvent(eventType, null, handler);
-    }
-
-    private Element _div;
-
-    @BeginRender
-    void begin(MarkupWriter writer)
-    {
-        String name = _pageRenderSupport.allocateClientId(_resources.getId());
-
-        Link link = _resources.createActionLink(TapestryConstants.DEFAULT_EVENT, true);
-
-        writer.element("form", "name", name, "id", name, "method", "post", "action", link
-                .toFormURI());
-
-        // TODO: Informal parameters
-
-        _div = writer.element("div", "style", "invisible");
-
-        for (String parameterName : link.getParameterNames())
-        {
-            String value = link.getParameterValue(parameterName);
-
-            writer.element("input", "type", "hidden", "name", parameterName, "value", value);
-            writer.end();
-        }
-
-        writer.end(); // div
-
-    }
-
-    @AfterRender
-    void after(MarkupWriter writer)
-    {
-        writer.end(); // form
-
-        // Now, inject into the div the remaining hidden field (the list of actions).
-
-        try
-        {
-            _actions.close();
-        }
-        catch (IOException ex)
-        {
-            throw new RuntimeException(ex);
-        }
-
-        _div.element("input", "type", "hidden", "name", FORM_DATA, "value", _actions.toBase64());
-    }
-
-    @CleanupRender
-    void cleanup()
-    {
-        _environment.pop(FormSupport.class);
-    }
-
-    @SuppressWarnings("unchecked")
-    @OnEvent("action")
-    void onSubmit()
-    {
-        // TODO: Ajax stuff will eventually mean there are multiple values for this parameter name
-
-        String actionsBase64 = _request.getParameter(FORM_DATA);
-
-        try
-        {
-            ObjectInputStream ois = new Base64ObjectInputStream(actionsBase64);
-
-            while (true)
-            {
-                String componentId = ois.readUTF();
-                ComponentAction action = (ComponentAction) ois.readObject();
-
-                Component component = _source.getComponent(componentId);
-
-                action.execute(component);
-            }
-        }
-        catch (EOFException ex)
-        {
-            // Expected.
-        }
-        catch (Exception ex)
-        {
-            throw new RuntimeException(ex);
-        }
-
-        // TODO: The return value should be used to control what renders.
-
-        fireNotification(SUBMIT);
-    }
-
-}
+package org.apache.tapestry.corelib.components;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+
+import org.apache.tapestry.ComponentAction;
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.Link;
+import org.apache.tapestry.MarkupWriter;
+import org.apache.tapestry.TapestryConstants;
+import org.apache.tapestry.annotations.AfterRender;
+import org.apache.tapestry.annotations.BeginRender;
+import org.apache.tapestry.annotations.CleanupRender;
+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.OnEvent;
+import org.apache.tapestry.annotations.SetupRender;
+import org.apache.tapestry.corelib.mixins.RenderInformals;
+import org.apache.tapestry.dom.Element;
+import org.apache.tapestry.internal.util.Base64ObjectInputStream;
+import org.apache.tapestry.internal.util.Base64ObjectOutputStream;
+import org.apache.tapestry.runtime.Component;
+import org.apache.tapestry.services.ComponentSource;
+import org.apache.tapestry.services.Environment;
+import org.apache.tapestry.services.FormSupport;
+import org.apache.tapestry.services.PageRenderSupport;
+import org.apache.tapestry.services.WebRequest;
+
+/**
+ * An HTML form, which will enclose other components to render out the various types of fields.
+ */
+@ComponentClass
+public class Form
+{
+    /**
+     * Invoked to let the containing component(s) prepare for the form rendering or the form
+     * submission.
+     */
+    public static final String PREPARE_EVENT = "prepare";
+
+    /** Event type for a notification after the form has submitted. */
+    public static final String SUBMIT = "submit";
+
+    /**
+     * Query parameter name storing form data (the serialized commands needed to process a form
+     * submission).
+     */
+    public static final String FORM_DATA = "t:formdata";
+
+    @Inject("infrastructure:environment")
+    private Environment _environment;
+
+    @Inject
+    private ComponentResources _resources;
+
+    @Environmental
+    private PageRenderSupport _pageRenderSupport;
+
+    @Inject("infrastructure:request")
+    private WebRequest _request;
+
+    @Inject("infrastructure:componentSource")
+    private ComponentSource _source;
+
+    // Collects a stream of component actions. Each action goes in as a UTF string (the component
+    // component id),
+    // followed by a ComponentAction
+
+    private Base64ObjectOutputStream _actions;
+
+    @SuppressWarnings("unused")
+    @Mixin
+    private RenderInformals _renderInformals;
+
+    @SetupRender
+    void setup()
+    {
+        try
+        {
+            _actions = new Base64ObjectOutputStream();
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex);
+        }
+
+        FormSupport support = new FormSupportImpl(_actions);
+
+        // TODO: Forms should not allow to nest. Perhaps a set() method instead of a push() method
+        // for this kind of check?
+
+        _environment.push(FormSupport.class, support);
+
+        // Now that the environment is setup, inform the component or other listeners that the form is about to 
+        // render.
+
+        _resources.triggerEvent(PREPARE_EVENT, null, null);
+    }
+
+    private Element _div;
+
+    @BeginRender
+    void begin(MarkupWriter writer)
+    {
+        String name = _pageRenderSupport.allocateClientId(_resources.getId());
+
+        Link link = _resources.createActionLink(TapestryConstants.DEFAULT_EVENT, true);
+
+        writer.element("form", "name", name, "id", name, "method", "post", "action", link
+                .toFormURI());
+
+        // TODO: Informal parameters
+
+        _div = writer.element("div", "style", "invisible");
+
+        for (String parameterName : link.getParameterNames())
+        {
+            String value = link.getParameterValue(parameterName);
+
+            writer.element("input", "type", "hidden", "name", parameterName, "value", value);
+            writer.end();
+        }
+
+        writer.end(); // div
+
+    }
+
+    @AfterRender
+    void after(MarkupWriter writer)
+    {
+        writer.end(); // form
+
+        // Now, inject into the div the remaining hidden field (the list of actions).
+
+        try
+        {
+            _actions.close();
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex);
+        }
+
+        _div.element("input", "type", "hidden", "name", FORM_DATA, "value", _actions.toBase64());
+    }
+
+    @CleanupRender
+    void cleanup()
+    {
+        _environment.pop(FormSupport.class);
+    }
+
+    @SuppressWarnings("unchecked")
+    @OnEvent("action")
+    void onSubmit()
+    {
+        // TODO: Ajax stuff will eventually mean there are multiple values for this parameter name
+
+        String actionsBase64 = _request.getParameter(FORM_DATA);
+
+        try
+        {
+            ObjectInputStream ois = new Base64ObjectInputStream(actionsBase64);
+
+            while (true)
+            {
+                String componentId = ois.readUTF();
+                ComponentAction action = (ComponentAction) ois.readObject();
+
+                Component component = _source.getComponent(componentId);
+
+                action.execute(component);
+            }
+        }
+        catch (EOFException ex)
+        {
+            // Expected.
+        }
+        catch (Exception ex)
+        {
+            throw new RuntimeException(ex);
+        }
+
+        // TODO: The return value should be used to control what renders.
+
+        _resources.triggerEvent(SUBMIT, null, null);
+    }
+
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java?view=diff&rev=477336&r1=477335&r2=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/bindings/PropBindingFactory.java Mon Nov 20 13:10:27 2006
@@ -96,7 +96,7 @@
 
             return cons.newBindingInstance(target, toString, location);
         }
-        catch (Exception ex)
+        catch (Throwable ex)
         {
             throw new TapestryException(ex.getMessage(), location, ex);
         }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PropertyAccessImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PropertyAccessImpl.java?view=diff&rev=477336&r1=477335&r2=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PropertyAccessImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PropertyAccessImpl.java Mon Nov 20 13:10:27 2006
@@ -79,7 +79,7 @@
 
             return new ClassPropertyAdapterImpl(forClass, info.getPropertyDescriptors());
         }
-        catch (Exception ex)
+        catch (Throwable ex)
         {
             throw new RuntimeException(ex);
         }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentSourceImpl.java?view=diff&rev=477336&r1=477335&r2=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentSourceImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentSourceImpl.java Mon Nov 20 13:10:27 2006
@@ -12,38 +12,38 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.internal.services;
-
-import org.apache.tapestry.internal.structure.Page;
-import org.apache.tapestry.runtime.Component;
-import org.apache.tapestry.services.ComponentSource;
-
-public class ComponentSourceImpl implements ComponentSource
-{
-    private final RequestPageCache _pageCache;
-
-    public ComponentSourceImpl(RequestPageCache pageCache)
-    {
-        _pageCache = pageCache;
-    }
-
-    public Component getComponent(String componentId)
-    {
-        int colonx = componentId.indexOf(':');
-
-        if (colonx < 0)
-        {
-            Page page = _pageCache.getByClassName(componentId);
-
-            return page.getRootElement().getComponent();
-        }
-
-        String pageName = componentId.substring(0, colonx);
-
-        Page page = _pageCache.getByClassName(pageName);
-        String nestedId = componentId.substring(colonx + 1);
-
-        return page.getComponentElementByNestedId(nestedId).getComponent();
-    }
-
-}
+package org.apache.tapestry.internal.services;
+
+import org.apache.tapestry.internal.structure.Page;
+import org.apache.tapestry.runtime.Component;
+import org.apache.tapestry.services.ComponentSource;
+
+public class ComponentSourceImpl implements ComponentSource
+{
+    private final RequestPageCache _pageCache;
+
+    public ComponentSourceImpl(RequestPageCache pageCache)
+    {
+        _pageCache = pageCache;
+    }
+
+    public Component getComponent(String componentId)
+    {
+        int colonx = componentId.indexOf(':');
+
+        if (colonx < 0)
+        {
+            Page page = _pageCache.getByClassName(componentId);
+
+            return page.getRootComponent();
+        }
+
+        String pageName = componentId.substring(0, colonx);
+
+        Page page = _pageCache.getByClassName(pageName);
+        String nestedId = componentId.substring(colonx + 1);
+
+        return page.getComponentElementByNestedId(nestedId).getComponent();
+    }
+
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/DefaultRequestExceptionHandler.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/DefaultRequestExceptionHandler.java?view=diff&rev=477336&r1=477335&r2=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/DefaultRequestExceptionHandler.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/DefaultRequestExceptionHandler.java Mon Nov 20 13:10:27 2006
@@ -47,7 +47,7 @@
     {
         Page page = _pageCache.get("ExceptionReport");
 
-        ExceptionReporter rootComponent = (ExceptionReporter) page.getRootElement().getComponent();
+        ExceptionReporter rootComponent = (ExceptionReporter) page.getRootComponent();
 
         // Let the page set up for the new exception.
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java?view=diff&rev=477336&r1=477335&r2=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java Mon Nov 20 13:10:27 2006
@@ -12,80 +12,157 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.internal.services;
-
-import org.apache.tapestry.Link;
-import org.apache.tapestry.internal.structure.ComponentPageElement;
-import org.apache.tapestry.internal.structure.Page;
-import org.apache.tapestry.services.ComponentClassResolver;
-import org.apache.tapestry.services.WebRequest;
-import org.apache.tapestry.services.WebResponse;
-import org.apache.tapestry.util.Defense;
-
-public class LinkFactoryImpl implements LinkFactory
-{
-    private final WebRequest _request;
-
-    private final WebResponse _response;
-
-    private final ComponentClassResolver _componentClassResolver;
-
-    public LinkFactoryImpl(WebRequest request, WebResponse response,
-            ComponentClassResolver componentClassResolver)
-    {
-        _request = request;
-        _response = response;
-        _componentClassResolver = componentClassResolver;
-    }
-
-    public Link createActionLink(ComponentPageElement component, String action, boolean forForm,
-            Object... context)
-    {
-        Defense.notBlank(action, "action");
-
-        String pageName = component.getContainingPage().getName();
-
-        String logicalPageName = _componentClassResolver.resolvePageClassNameToPageName(pageName);
-
-        StringBuilder builder = new StringBuilder();
-
-        builder.append(_request.getContextPath());
-        builder.append("/");
-        builder.append(logicalPageName);
-        builder.append(".");
-        builder.append(action);
-        builder.append("/");
-        builder.append(component.getNestedId());
-
-        for (Object id : context)
-        {
-            builder.append("/");
-
-            // TODO: Need to encode this for URLs? What if the string contains slashes, etc.?
-
-            builder.append(id.toString());
-        }
-
-        // TODO: Much more: query parameter for case where active page != component page.
-        // Letting listeners add extra parameters.
-
-        return new LinkImpl(_response, builder.toString());
-    }
-
-    public Link createPageLink(Page page)
-    {
-        Defense.notNull(page, "page");
-
-        String pageName = page.getName();
-        String logicalPageName = _componentClassResolver.resolvePageClassNameToPageName(pageName);
-
-        StringBuilder builder = new StringBuilder();
-
-        builder.append(_request.getContextPath());
-        builder.append("/");
-        builder.append(logicalPageName);
-        builder.append(".html");
-
-        return new LinkImpl(_response, builder.toString());
-    }
-}
+package org.apache.tapestry.internal.services;
+
+import static org.apache.tapestry.util.CollectionFactory.newList;
+import static org.apache.tapestry.util.CollectionFactory.newMap;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tapestry.ComponentEventHandler;
+import org.apache.tapestry.Link;
+import org.apache.tapestry.TapestryConstants;
+import org.apache.tapestry.internal.structure.ComponentPageElement;
+import org.apache.tapestry.internal.structure.Page;
+import org.apache.tapestry.runtime.Component;
+import org.apache.tapestry.services.ComponentClassResolver;
+import org.apache.tapestry.services.WebRequest;
+import org.apache.tapestry.services.WebResponse;
+import org.apache.tapestry.util.Defense;
+import org.apache.tapestry.util.StrategyRegistry;
+
+public class LinkFactoryImpl implements LinkFactory
+{
+    private final WebRequest _request;
+
+    private final WebResponse _response;
+
+    private final ComponentClassResolver _componentClassResolver;
+
+    private interface PassivateContextHandler<T>
+    {
+        void handle(T result, List context);
+    }
+
+    private final StrategyRegistry<PassivateContextHandler> _registry;
+
+    public LinkFactoryImpl(WebRequest request, WebResponse response,
+            ComponentClassResolver componentClassResolver)
+    {
+        _request = request;
+        _response = response;
+        _componentClassResolver = componentClassResolver;
+
+        Map<Class, PassivateContextHandler> registrations = newMap();
+
+        registrations.put(Object.class, new PassivateContextHandler()
+        {
+            @SuppressWarnings("unchecked")
+            public void handle(Object result, List context)
+            {
+                context.add(result);
+            }
+        });
+
+        registrations.put(Object[].class, new PassivateContextHandler<Object[]>()
+        {
+
+            @SuppressWarnings("unchecked")
+            public void handle(Object[] result, List context)
+            {
+                for (Object o : result)
+                    context.add(o);
+            }
+        });
+
+        registrations.put(Collection.class, new PassivateContextHandler<Collection>()
+        {
+            @SuppressWarnings("unchecked")
+            public void handle(Collection result, List context)
+            {
+                context.addAll(result);
+            }
+        });
+
+        _registry = StrategyRegistry.newInstance(PassivateContextHandler.class, registrations);
+    }
+
+    public Link createActionLink(ComponentPageElement component, String action, boolean forForm,
+            Object... context)
+    {
+        Defense.notBlank(action, "action");
+
+        String pageName = component.getContainingPage().getName();
+
+        String logicalPageName = _componentClassResolver.resolvePageClassNameToPageName(pageName);
+
+        StringBuilder builder = new StringBuilder();
+
+        builder.append(_request.getContextPath());
+        builder.append("/");
+        builder.append(logicalPageName);
+        builder.append(".");
+        builder.append(action);
+        builder.append("/");
+        builder.append(component.getNestedId());
+
+        for (Object id : context)
+        {
+            builder.append("/");
+
+            // TODO: Need to encode this for URLs? What if the string contains slashes, etc.?
+
+            builder.append(id.toString());
+        }
+
+        // TODO: Much more: query parameter for case where active page != component page.
+        // Letting listeners add extra parameters.
+
+        return new LinkImpl(_response, builder.toString());
+    }
+
+    public Link createPageLink(final Page page)
+    {
+        Defense.notNull(page, "page");
+
+        String pageName = page.getName();
+        String logicalPageName = _componentClassResolver.resolvePageClassNameToPageName(pageName);
+
+        StringBuilder builder = new StringBuilder();
+
+        builder.append(_request.getContextPath());
+        builder.append("/");
+        builder.append(logicalPageName);
+        builder.append(".html");
+
+        final List context = newList();
+
+        ComponentEventHandler handler = new ComponentEventHandler()
+        {
+            @SuppressWarnings("unchecked")
+            public void handleResult(Object result, Component component, String methodDescription)
+            {
+                PassivateContextHandler contextHandler = _registry.getByInstance(result);
+
+                contextHandler.handle(result, context);
+            }
+        };
+
+        ComponentPageElement rootElement = page.getRootElement();
+
+        rootElement.triggerEvent(TapestryConstants.PASSIVATE_EVENT, null, handler);
+
+        for (Object id : context)
+        {
+            builder.append("/");
+
+            // TODO: Need to encode this for URLs? What if the string contains slashes, etc.?
+
+            builder.append(id.toString());
+        }
+
+        return new LinkImpl(_response, builder.toString());
+    }
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/OnEventWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/OnEventWorker.java?view=diff&rev=477336&r1=477335&r2=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/OnEventWorker.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/OnEventWorker.java Mon Nov 20 13:10:27 2006
@@ -106,9 +106,11 @@
                 .getMediumDescription());
 
         boolean isNonVoid = !method.getReturnType().equals("void");
-
+
+        // Store the result, converting primitives to wrappers automatically.
+        
         if (isNonVoid)
-            builder.add("if ($1.storeResult(");
+            builder.add("if ($1.storeResult(($w) ");
 
         builder.add("%s(", method.getMethodName());
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageRenderDispatcher.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageRenderDispatcher.java?view=diff&rev=477336&r1=477335&r2=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageRenderDispatcher.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageRenderDispatcher.java Mon Nov 20 13:10:27 2006
@@ -12,54 +12,67 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.internal.services;
-
-import java.io.IOException;
-
-import org.apache.tapestry.internal.structure.Page;
-import org.apache.tapestry.services.Dispatcher;
-import org.apache.tapestry.services.WebRequest;
-import org.apache.tapestry.services.WebResponse;
-
-/**
- * Dispatches incoming requests whose path ends with ".html". In these cases, the path is
- * interpreted as a page name.
- * 
- * 
- */
-public class PageRenderDispatcher implements Dispatcher
-{
-    private final PageResponseRenderer _renderer;
-
-    private final RequestPageCache _cache;
-
-    public PageRenderDispatcher(PageResponseRenderer renderer, RequestPageCache cache)
-    {
-        _renderer = renderer;
-        _cache = cache;
-    }
-
-    public boolean dispatch(WebRequest request, WebResponse response) throws IOException
-    {
-        String path = request.getPath();
-
-        // For the moment, just matching things that end with .html
-
-        int pos = path.indexOf(".html");
-
-        if (pos < 0)
-            return false;
-
-        // We have a match!
-
-        // The first character will be the leading slash
-
-        String logicalPageName = path.substring(1, pos);
-
-        Page page = _cache.get(logicalPageName);
-
-        _renderer.renderPageResponse(page, response);
-
-        return true;
-    }
-}
+package org.apache.tapestry.internal.services;
+
+import java.io.IOException;
+
+import org.apache.tapestry.TapestryConstants;
+import org.apache.tapestry.internal.structure.Page;
+import org.apache.tapestry.services.Dispatcher;
+import org.apache.tapestry.services.WebRequest;
+import org.apache.tapestry.services.WebResponse;
+
+/**
+ * Dispatches incoming requests whose path ends with ".html". In these cases, the path is
+ * interpreted as a page name.
+ */
+public class PageRenderDispatcher implements Dispatcher
+{
+    private final PageResponseRenderer _renderer;
+
+    private final RequestPageCache _cache;
+
+    public PageRenderDispatcher(PageResponseRenderer renderer, RequestPageCache cache)
+    {
+        _renderer = renderer;
+        _cache = cache;
+    }
+
+    public boolean dispatch(WebRequest request, WebResponse response) throws IOException
+    {
+        String path = request.getPath();
+
+        // For the moment, just matching things that end with .html
+
+        int pos = path.indexOf(".html");
+
+        if (pos < 0)
+            return false;
+
+        // We have a match!
+
+        // The first character will be the leading slash
+
+        String logicalPageName = path.substring(1, pos);
+
+        Page page = _cache.get(logicalPageName);
+
+        String[] terms = path.substring(pos).split("/");
+
+        Object[] context = new Object[terms.length - 1];
+
+        for (int i = 1; i < terms.length; i++)
+        {
+            // TODO: Decode strings?
+            context[i - 1] = terms[i];
+        }
+
+        // Fire a notification so that the page can set itself up for the given context
+
+        page.getRootElement().triggerEvent(TapestryConstants.ACTIVATE_EVENT, context, null);
+
+        _renderer.renderPageResponse(page, response);
+
+        return true;
+    }
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/StringEventHandler.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/StringEventHandler.java?view=diff&rev=477336&r1=477335&r2=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/StringEventHandler.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/StringEventHandler.java Mon Nov 20 13:10:27 2006
@@ -1,3 +1,17 @@
+// 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.ComponentEventHandler;

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=477336&r1=477335&r2=477336
==============================================================================
--- 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 Mon Nov 20 13:10:27 2006
@@ -32,6 +32,7 @@
 import org.apache.tapestry.internal.TapestryException;
 import org.apache.tapestry.internal.services.ComponentEventImpl;
 import org.apache.tapestry.internal.services.Instantiator;
+import org.apache.tapestry.internal.util.AcceptVoidEventHandler;
 import org.apache.tapestry.internal.util.InternalUtils;
 import org.apache.tapestry.ioc.IOCUtilities;
 import org.apache.tapestry.ioc.services.TypeCoercer;
@@ -109,7 +110,7 @@
     private final RenderCommand _afterRender = new RenderCommand()
     {
         public void render(final MarkupWriter writer, RenderQueue queue)
-        
+
         {
             final LifecycleEvent<Boolean> event = newEvent(false);
 
@@ -779,7 +780,7 @@
 
     public Component getPage()
     {
-        return _page.getRootElement().getComponent();
+        return _page.getRootComponent();
     }
 
     public boolean handleEvent(ComponentEvent event)
@@ -860,6 +861,7 @@
             _page.persistFieldChange(this, fieldName, newValue);
     }
 
+    /** Generate a toString() for the inner classes that represent render phases. */
     private String phaseToString(String phaseName)
     {
         return String.format("%s[%s]", phaseName, _completeId);
@@ -884,6 +886,11 @@
     public boolean triggerEvent(String eventType, Object[] context, ComponentEventHandler handler)
     {
         boolean result = false;
+
+        // Provide a default handler for when the provided handler is null.
+
+        if (handler == null)
+            handler = new AcceptVoidEventHandler(eventType, _completeId);
 
         ComponentEvent event = new ComponentEventImpl(eventType, _id, context, handler,
                 _typeCoercer);

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=477336&r1=477335&r2=477336
==============================================================================
--- 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 Mon Nov 20 13:10:27 2006
@@ -126,7 +126,7 @@
 
     public Component getPage()
     {
-        return _element.getContainingPage().getRootElement().getComponent();
+        return _element.getContainingPage().getRootComponent();
     }
 
     public boolean isInvariant(String parameterName)

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java?view=diff&rev=477336&r1=477335&r2=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java Mon Nov 20 13:10:27 2006
@@ -14,16 +14,17 @@
 
 package org.apache.tapestry.internal.structure;
 
-import java.util.Locale;
-
-import org.apache.commons.logging.Log;
-import org.apache.tapestry.Link;
-import org.apache.tapestry.runtime.PageLifecycleListener;
+import java.util.Locale;
+
+import org.apache.commons.logging.Log;
+import org.apache.tapestry.Link;
+import org.apache.tapestry.runtime.Component;
+import org.apache.tapestry.runtime.PageLifecycleListener;
 
 /**
  * Represents a unique page within the application. Pages are part of the <em>internal</em>
  * structure of a Tapestry application; end developers who refer to "page" are really referring to
- * the {@link #getRootElement() root component} of the actual page.
+ * the {@link #getRootComponent() root component} of the actual page.
  * <p>
  * One of the most important aspects of a Page is that it <em>does not</em> have to be coded in a
  * thread-safe manner. Pages are always accessed within a single thread, associated with a single
@@ -57,7 +58,11 @@
      * The root component of the page. This is the wrapper around the end developer's view of the
      * page.
      */
-    ComponentPageElement getRootElement();
+    ComponentPageElement getRootElement();
+    
+    /** The root component of the page. A convienience over ivoking getRootElement().getComponent(). */
+    
+    Component getRootComponent();
 
     /**
      * Invoked to inform the page that it is being detached from the current request. This occurs

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java?view=diff&rev=477336&r1=477335&r2=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java Mon Nov 20 13:10:27 2006
@@ -22,6 +22,7 @@
 import org.apache.commons.logging.Log;
 import org.apache.tapestry.Link;
 import org.apache.tapestry.internal.services.LinkFactory;
+import org.apache.tapestry.runtime.Component;
 import org.apache.tapestry.runtime.PageLifecycleListener;
 import org.apache.tapestry.services.PersistentFieldBundle;
 import org.apache.tapestry.services.PersistentFieldManager;
@@ -92,7 +93,13 @@
     {
         return _rootElement;
     }
-
+
+    
+    public Component getRootComponent()
+    {
+        return _rootElement.getComponent();
+    }
+
     public void addLifecycleListener(PageLifecycleListener listener)
     {
         _listeners.add(listener);

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java?view=diff&rev=477336&r1=477335&r2=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java Mon Nov 20 13:10:27 2006
@@ -458,4 +458,10 @@
         model.getMixinClassNames();
         setReturnValue(Arrays.asList(names));
     }
+
+    protected final void train_getRootComponent(Page page, Component component)
+    {
+        page.getRootComponent();
+        setReturnValue(component).atLeastOnce();
+    }
 }

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=477336&r1=477335&r2=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/app1/index.html Mon Nov 20 13:10:27 2006
@@ -42,6 +42,9 @@
                 <li>
                     <a href="SimpleForm.html">SimpleForm</a> -- first pass at writing Form and TextField components
                 </li>
+            <li>
+                <a href="NumberSelect.html">NumberSelect</a> -- passivate/activate page context demo
+            </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=477336&r1=477335&r2=477336
==============================================================================
--- 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 Mon Nov 20 13:10:27 2006
@@ -146,7 +146,7 @@
         clickAndWait("link=Barney");
 
         assertTextPresent("You clicked Barney.");
-        
+
         clickAndWait("link=Back");
         clickAndWait("link=Wilma");
         assertTextPresent("You clicked Wilma.");
@@ -295,6 +295,15 @@
         assertTextPresent("[foo@bar.baz]");
         assertTextPresent("[Message for you, sir!]");
         assertTextPresent("[true]");
+    }
+
+    @Test
+    public void app1_passivate_activate() throws Exception
+    {
+        _selenium.open(BASE_URL);
+        clickAndWait("link=NumberSelect");
+        clickAndWait("link=5");
+        assertTextPresent("You chose 5.");
     }
 
     private void assertText(String locator, String expected)

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/NumberSelect.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/NumberSelect.java?view=auto&rev=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/NumberSelect.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/NumberSelect.java Mon Nov 20 13:10:27 2006
@@ -0,0 +1,51 @@
+// 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.annotations.ComponentClass;
+import org.apache.tapestry.annotations.InjectPage;
+import org.apache.tapestry.annotations.OnEvent;
+
+@ComponentClass
+public class NumberSelect
+{
+    private int _index;
+
+    public int getIndex()
+    {
+        return _index;
+    }
+
+    public void setIndex(int index)
+    {
+        _index = index;
+    }
+
+    public boolean isNotFirst()
+    {
+        return _index != 1;
+    }
+
+    @InjectPage
+    private ShowSelection _showSelection;
+
+    @OnEvent(component = "select")
+    Object doSelect(int index)
+    {
+        _showSelection.setSelected(index);
+
+        return _showSelection;
+    }
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/ShowSelection.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/ShowSelection.java?view=auto&rev=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/ShowSelection.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/ShowSelection.java Mon Nov 20 13:10:27 2006
@@ -0,0 +1,46 @@
+// 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.annotations.ComponentClass;
+import org.apache.tapestry.annotations.OnEvent;
+
+@ComponentClass
+public class ShowSelection
+{
+    private int _selected;
+
+    public int getSelected()
+    {
+        return _selected;
+    }
+
+    public void setSelected(int selected)
+    {
+        _selected = selected;
+    }
+
+    @OnEvent("passivate")
+    int passivate()
+    {
+        return _selected;
+    }
+
+    @OnEvent("activate")
+    void activate(int selected)
+    {
+        _selected = selected;
+    }
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Wilma.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Wilma.java?view=diff&rev=477336&r1=477335&r2=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Wilma.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/Wilma.java Mon Nov 20 13:10:27 2006
@@ -1,3 +1,17 @@
+// 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.annotations.ComponentClass;

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentSourceImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentSourceImplTest.java?view=diff&rev=477336&r1=477335&r2=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentSourceImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentSourceImplTest.java Mon Nov 20 13:10:27 2006
@@ -32,14 +32,11 @@
     {
         RequestPageCache cache = newRequestPageCache();
         Page page = newPage();
-        ComponentPageElement element = newComponentPageElement();
         Component component = newComponent();
 
         train_getByClassName(cache, PAGE_NAME, page);
 
-        train_getRootElement(page, element);
-
-        train_getComponent(element, component);
+        train_getRootComponent(page, component);
 
         replay();
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java?view=diff&rev=477336&r1=477335&r2=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java Mon Nov 20 13:10:27 2006
@@ -12,103 +12,139 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.internal.services;
-
-import org.apache.tapestry.Link;
-import org.apache.tapestry.internal.structure.ComponentPageElement;
-import org.apache.tapestry.internal.structure.Page;
-import org.apache.tapestry.internal.test.InternalBaseTestCase;
-import org.apache.tapestry.services.ComponentClassResolver;
-import org.apache.tapestry.services.WebRequest;
-import org.apache.tapestry.services.WebResponse;
-import org.testng.annotations.Test;
-
-public class LinkFactoryImplTest extends InternalBaseTestCase
-{
-    private static final String PAGE_LOGICAL_NAME = "sub/MyPage";
-
-    private static final String PAGE_CLASS_NAME = "foo.pages.sub.MyPage";
-
-    @Test
-    public void action_link_root_context_no_ids()
-    {
-        run(PAGE_CLASS_NAME, PAGE_LOGICAL_NAME, "foo.bar", "", "/sub/MyPage.someaction/foo.bar");
-    }
-
-    @Test
-    public void action_link_root_context_with_ids()
-    {
-        run(
-                PAGE_CLASS_NAME,
-                PAGE_LOGICAL_NAME,
-                "foo.bar",
-                "",
-                "/sub/MyPage.someaction/foo.bar/fred/5",
-                "fred",
-                5);
-    }
-
-    @Test
-    public void action_link_named_context_no_ids()
-    {
-        run(
-                PAGE_CLASS_NAME,
-                PAGE_LOGICAL_NAME,
-                "foo.bar",
-                "/fred",
-                "/fred/sub/MyPage.someaction/foo.bar");
-    }
-
-    @Test
-    public void page_link()
-    {
-        WebRequest request = newWebRequest();
-        WebResponse response = newWebResponse();
-        ComponentClassResolver resolver = newComponentClassResolver();
-        Page page = newPage();
-
-        train_getName(page, PAGE_CLASS_NAME);
-        train_resolvePageClassNameToPageName(resolver, PAGE_CLASS_NAME, PAGE_LOGICAL_NAME);
-        train_getContextPath(request, "/barney");
-
-        train_encodeRedirectURL(response, "/barney/" + PAGE_LOGICAL_NAME + ".html", "*encoded*");
-
-        replay();
-
-        LinkFactory factory = new LinkFactoryImpl(request, response, resolver);
-
-        Link link = factory.createPageLink(page);
-
-        assertEquals(link.toRedirectURI(), "*encoded*");
-
-        verify();
-    }
-
-    private void run(String pageClassName, String logicalPageName, String nestedId,
-            String contextPath, String expectedURI, Object... context)
-    {
-        WebRequest request = newWebRequest();
-        WebResponse response = newWebResponse();
-        ComponentClassResolver resolver = newComponentClassResolver();
-        ComponentPageElement element = newComponentPageElement();
-        Page page = newPage();
-
-        train_getContainingPage(element, page);
-        train_getName(page, pageClassName);
-        train_resolvePageClassNameToPageName(resolver, pageClassName, logicalPageName);
-        train_getContextPath(request, contextPath);
-        train_getNestedId(element, nestedId);
-
-        train_encodeURL(response, expectedURI, "*encoded*");
-
-        replay();
-
-        LinkFactory factory = new LinkFactoryImpl(request, response, resolver);
-
-        Link link = factory.createActionLink(element, "someaction", false, context);
-
-        assertEquals(link.toURI(), "*encoded*");
-
-        verify();
-    }
-}
+package org.apache.tapestry.internal.services;
+
+import org.apache.tapestry.ComponentEventHandler;
+import org.apache.tapestry.Link;
+import org.apache.tapestry.TapestryConstants;
+import org.apache.tapestry.internal.structure.ComponentPageElement;
+import org.apache.tapestry.internal.structure.Page;
+import org.apache.tapestry.internal.test.InternalBaseTestCase;
+import org.apache.tapestry.services.ComponentClassResolver;
+import org.apache.tapestry.services.WebRequest;
+import org.apache.tapestry.services.WebResponse;
+import org.easymock.EasyMock;
+import org.easymock.IAnswer;
+import org.testng.annotations.Test;
+
+public class LinkFactoryImplTest extends InternalBaseTestCase
+{
+    private static final String PAGE_LOGICAL_NAME = "sub/MyPage";
+
+    private static final String PAGE_CLASS_NAME = "foo.pages.sub.MyPage";
+
+    @Test
+    public void action_link_root_context_no_ids()
+    {
+        run(PAGE_CLASS_NAME, PAGE_LOGICAL_NAME, "foo.bar", "", "/sub/MyPage.someaction/foo.bar");
+    }
+
+    @Test
+    public void action_link_root_context_with_ids()
+    {
+        run(
+                PAGE_CLASS_NAME,
+                PAGE_LOGICAL_NAME,
+                "foo.bar",
+                "",
+                "/sub/MyPage.someaction/foo.bar/fred/5",
+                "fred",
+                5);
+    }
+
+    @Test
+    public void action_link_named_context_no_ids()
+    {
+        run(
+                PAGE_CLASS_NAME,
+                PAGE_LOGICAL_NAME,
+                "foo.bar",
+                "/fred",
+                "/fred/sub/MyPage.someaction/foo.bar");
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void page_link()
+    {
+        WebRequest request = newWebRequest();
+        WebResponse response = newWebResponse();
+        ComponentClassResolver resolver = newComponentClassResolver();
+        Page page = newPage();
+        ComponentPageElement rootElement = newComponentPageElement();
+
+        train_getName(page, PAGE_CLASS_NAME);
+        train_resolvePageClassNameToPageName(resolver, PAGE_CLASS_NAME, PAGE_LOGICAL_NAME);
+        train_getContextPath(request, "/barney");
+
+        train_getRootElement(page, rootElement);
+
+        // Answer for triggerEvent() which returns a boolean.
+
+        IAnswer<Boolean> answer = new IAnswer<Boolean>()
+        {
+            public Boolean answer() throws Throwable
+            {
+                ComponentEventHandler handler = (ComponentEventHandler) EasyMock
+                        .getCurrentArguments()[2];
+
+                handler.handleResult(new Object[]
+                { "foo", "bar" }, null, null);
+
+                return true;
+            }
+        };
+
+        // Intercept the call to handle component event, and let the IAnswer
+        // do the work.
+
+        rootElement.triggerEvent(
+                EasyMock.eq(TapestryConstants.PASSIVATE_EVENT),
+                (Object[]) EasyMock.isNull(),
+                EasyMock.isA(ComponentEventHandler.class));
+        getMocksControl().andAnswer(answer);
+
+        train_encodeRedirectURL(
+                response,
+                "/barney/" + PAGE_LOGICAL_NAME + ".html/foo/bar",
+                "*encoded*");
+
+        replay();
+
+        LinkFactory factory = new LinkFactoryImpl(request, response, resolver);
+
+        Link link = factory.createPageLink(page);
+
+        assertEquals(link.toRedirectURI(), "*encoded*");
+
+        verify();
+    }
+
+    private void run(String pageClassName, String logicalPageName, String nestedId,
+            String contextPath, String expectedURI, Object... context)
+    {
+        WebRequest request = newWebRequest();
+        WebResponse response = newWebResponse();
+        ComponentClassResolver resolver = newComponentClassResolver();
+        ComponentPageElement element = newComponentPageElement();
+        Page page = newPage();
+
+        train_getContainingPage(element, page);
+        train_getName(page, pageClassName);
+        train_resolvePageClassNameToPageName(resolver, pageClassName, logicalPageName);
+        train_getContextPath(request, contextPath);
+        train_getNestedId(element, nestedId);
+
+        train_encodeURL(response, expectedURI, "*encoded*");
+
+        replay();
+
+        LinkFactory factory = new LinkFactoryImpl(request, response, resolver);
+
+        Link link = factory.createActionLink(element, "someaction", false, context);
+
+        assertEquals(link.toURI(), "*encoded*");
+
+        verify();
+    }
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageRenderDispatcherTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageRenderDispatcherTest.java?view=auto&rev=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageRenderDispatcherTest.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageRenderDispatcherTest.java Mon Nov 20 13:10:27 2006
@@ -0,0 +1,136 @@
+// 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.ComponentEventHandler;
+import org.apache.tapestry.TapestryConstants;
+import org.apache.tapestry.internal.structure.ComponentPageElement;
+import org.apache.tapestry.internal.structure.Page;
+import org.apache.tapestry.internal.test.InternalBaseTestCase;
+import org.apache.tapestry.services.Dispatcher;
+import org.apache.tapestry.services.WebRequest;
+import org.apache.tapestry.services.WebResponse;
+import org.easymock.EasyMock;
+import org.testng.annotations.Test;
+
+public class PageRenderDispatcherTest extends InternalBaseTestCase
+{
+    @Test
+    public void not_a_page_request() throws Exception
+    {
+        PageResponseRenderer renderer = newPageResponseRenderer();
+        RequestPageCache cache = newRequestPageCache();
+        WebRequest request = newWebRequest();
+        WebResponse response = newWebResponse();
+
+        train_getPath(request, "/foo/Bar.baz");
+
+        replay();
+
+        Dispatcher d = new PageRenderDispatcher(renderer, cache);
+
+        assertFalse(d.dispatch(request, response));
+
+        verify();
+    }
+
+    @Test
+    public void no_extra_context() throws Exception
+    {
+        PageResponseRenderer renderer = newPageResponseRenderer();
+        RequestPageCache cache = newRequestPageCache();
+        WebRequest request = newWebRequest();
+        WebResponse response = newWebResponse();
+        Page page = newPage();
+        ComponentPageElement rootElement = newComponentPageElement();
+
+        train_getPath(request, "/foo/Bar.html");
+
+        train_get(cache, "foo/Bar", page);
+        train_getRootElement(page, rootElement);
+
+        train_triggerEvent(
+                rootElement,
+                TapestryConstants.ACTIVATE_EVENT,
+                new Object[0],
+                null,
+                false);
+
+        renderer.renderPageResponse(page, response);
+
+        replay();
+
+        Dispatcher d = new PageRenderDispatcher(renderer, cache);
+
+        assertTrue(d.dispatch(request, response));
+
+        verify();
+    }
+
+    @Test
+    public void context_passed_in_path() throws Exception
+    {
+        PageResponseRenderer renderer = newPageResponseRenderer();
+        RequestPageCache cache = newRequestPageCache();
+        WebRequest request = newWebRequest();
+        WebResponse response = newWebResponse();
+        Page page = newPage();
+        ComponentPageElement rootElement = newComponentPageElement();
+
+        train_getPath(request, "/foo/Bar.html/zip/zoom");
+
+        train_get(cache, "foo/Bar", page);
+        train_getRootElement(page, rootElement);
+
+        train_triggerEvent(rootElement, TapestryConstants.ACTIVATE_EVENT, new Object[]
+        { "zip", "zoom" }, null, false);
+
+        renderer.renderPageResponse(page, response);
+
+        replay();
+
+        Dispatcher d = new PageRenderDispatcher(renderer, cache);
+
+        assertTrue(d.dispatch(request, response));
+
+        verify();
+    }
+
+    private void train_triggerEvent(ComponentPageElement element, String eventType,
+            Object[] context, ComponentEventHandler handler, boolean handled)
+    {
+        element.triggerEvent(EasyMock.eq(eventType), EasyMock.aryEq(context), EasyMock
+                .same(handler));
+        setReturnValue(handled);
+    }
+
+    protected final void train_get(RequestPageCache cache, String pageName, Page page)
+    {
+        cache.get(pageName);
+        setReturnValue(page).atLeastOnce();
+    }
+
+    protected final PageResponseRenderer newPageResponseRenderer()
+    {
+        return newMock(PageResponseRenderer.class);
+    }
+
+    protected final void train_getPath(WebRequest request, String path)
+    {
+        request.getPath();
+        setReturnValue(path).atLeastOnce();
+    }
+
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/NumberSelect.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/NumberSelect.html?view=auto&rev=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/NumberSelect.html (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/NumberSelect.html Mon Nov 20 13:10:27 2006
@@ -0,0 +1,17 @@
+<t:comp type="Border" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+    <h1>Number Selection</h1>
+    
+    <p>  Demonstration of passivate/activate page logic.</p>
+
+    <p>
+        Select a number from this list:
+        
+        <t:comp type="Loop" source="1..10" value="index">
+            <t:comp type="If" test="notFirst"> - </t:comp>
+            <t:comp type="ActionLink" id="select" context="index">${index}</t:comp>
+        </t:comp> 
+    </p>
+    
+    
+    
+</t:comp>

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ShowSelection.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ShowSelection.html?view=auto&rev=477336
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ShowSelection.html (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ShowSelection.html Mon Nov 20 13:10:27 2006
@@ -0,0 +1,10 @@
+<t:comp type="Border" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+    <h1>Number Selection</h1>
+    
+    <p>  You chose ${selected}.</p>
+    
+    <p>
+    [<a href="/NumberSelect.html">Back</a>]
+    </p>
+    
+</t:comp>