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/24 21:16:19 UTC

svn commit: r478977 - in /tapestry/tapestry5/tapestry-core/trunk/src: main/java/org/apache/tapestry/corelib/components/ main/java/org/apache/tapestry/internal/services/ main/java/org/apache/tapestry/runtime/ site/apt/guide/ test/conf/ test/java/org/apa...

Author: hlship
Date: Fri Nov 24 12:16:18 2006
New Revision: 478977

URL: http://svn.apache.org/viewvc?view=rev&rev=478977
Log:
Allow direct access to the raw event context from component event handler methods.
Change the Form component to emit more notification events.

Modified:
    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/services/ComponentEventImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryListener.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/runtime/ComponentEvent.java
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/coercion.apt
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/event.apt
    tapestry/tapestry5/tapestry-core/trunk/src/test/conf/testng.xml
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/OnEventWorkerTest.java

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=478977&r1=478976&r2=478977
==============================================================================
--- 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 Fri Nov 24 12:16:18 2006
@@ -17,6 +17,7 @@
 import java.io.EOFException;
 import java.io.IOException;
 import java.io.ObjectInputStream;
+import java.util.List;
 
 import org.apache.tapestry.ComponentAction;
 import org.apache.tapestry.ComponentResources;
@@ -31,6 +32,7 @@
 import org.apache.tapestry.annotations.Inject;
 import org.apache.tapestry.annotations.Mixin;
 import org.apache.tapestry.annotations.OnEvent;
+import org.apache.tapestry.annotations.Parameter;
 import org.apache.tapestry.annotations.SetupRender;
 import org.apache.tapestry.corelib.mixins.RenderInformals;
 import org.apache.tapestry.dom.Element;
@@ -45,6 +47,18 @@
 
 /**
  * An HTML form, which will enclose other components to render out the various types of fields.
+ * <p>
+ * A Form emits several notification events; when it renders it sends a
+ * {@link #PREPARE_EVENT#prepare} notification event, to allow any listeners to set up the state of
+ * the page prior to rendering out the form's content.
+ * <p>
+ * When the form is submitted, the component emits two events: first another prepare event to allow
+ * the page to update its state as necessary to prepare for the form submission, and then a
+ * {@link #SUBMIT submit} event once all data in the form has been propogated into the data model.
+ * <p>
+ * For all of these notifications, the event context is derived from the <strong>context</strong>
+ * parameter. This context is encoded into the form's action URI (the parameter is not read when the
+ * form is submitted, instead the values encoded into the form are used).
  */
 @ComponentClass
 public class Form
@@ -59,6 +73,14 @@
     public static final String SUBMIT = "submit";
 
     /**
+     * The context for the link (optional parameter). This list of values will be converted into
+     * strings and included in the URI. The strings will be coerced back to whatever their values
+     * are and made available to event handler methods.
+     */
+    @Parameter
+    private List<?> _context;
+
+    /**
      * Query parameter name storing form data (the serialized commands needed to process a form
      * submission).
      */
@@ -108,10 +130,6 @@
 
         _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;
@@ -119,9 +137,17 @@
     @BeginRender
     void begin(MarkupWriter writer)
     {
+        // Now that the environment is setup, inform the component or other listeners that the form
+        // is about to
+        // render.
+
+        Object[] contextArray = _context == null ? new Object[0] : _context.toArray();
+
+        _resources.triggerEvent(PREPARE_EVENT, contextArray, null);
+
         String name = _pageRenderSupport.allocateClientId(_resources.getId());
 
-        Link link = _resources.createActionLink(TapestryConstants.DEFAULT_EVENT, true);
+        Link link = _resources.createActionLink(TapestryConstants.DEFAULT_EVENT, true, contextArray);
 
         writer.element("form", "name", name, "id", name, "method", "post", "action", link
                 .toFormURI());
@@ -169,8 +195,10 @@
 
     @SuppressWarnings("unchecked")
     @OnEvent("action")
-    void onSubmit()
+    void onSubmit(Object[] context)
     {
+        _resources.triggerEvent(PREPARE_EVENT, context, null);
+
         // TODO: Ajax stuff will eventually mean there are multiple values for this parameter name
 
         String actionsBase64 = _request.getParameter(FORM_DATA);
@@ -200,7 +228,7 @@
 
         // TODO: The return value should be used to control what renders.
 
-        _resources.triggerEvent(SUBMIT, null, null);
+        _resources.triggerEvent(SUBMIT, context, null);
     }
 
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentEventImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentEventImpl.java?view=diff&rev=478977&r1=478976&r2=478977
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentEventImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentEventImpl.java Fri Nov 24 12:16:18 2006
@@ -12,137 +12,142 @@
 // 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.ComponentResources;
+package org.apache.tapestry.internal.services;
+
+import org.apache.tapestry.ComponentEventHandler;
+import org.apache.tapestry.ComponentResources;
 import org.apache.tapestry.ioc.internal.util.Defense;
-import org.apache.tapestry.ioc.services.TypeCoercer;
-import org.apache.tapestry.runtime.Component;
-import org.apache.tapestry.runtime.ComponentEvent;
-
-public class ComponentEventImpl implements ComponentEvent
-{
-    private final String _eventType;
-
-    private final String _originatingComponentId;
-
-    private final Object[] _context;
-
-    private final ComponentEventHandler _handler;
-
-    private final TypeCoercer _typeCoercer;
-
-    private final int _contextLength;
-
-    private boolean _aborted;
-
-    private Component _component;
-    private String _methodDescription;
-
-    /**
-     * @param eventType
-     *            non blank string used to identify the type of event that was triggered
-     * @param originatingComponentId
-     *            the id of the component that triggered the event (this will likely need to change
-     *            somewhat)
-     * @param context
-     *            an array of values that can be made available to handler methods via method
-     *            parameters
-     * @param handler
-     *            invoked when a non-null return value is obtained from an event handler method
-     * @param typeCoercer
-     *            used when coercing context values to parameter types
-     */
-    public ComponentEventImpl(String eventType, String originatingComponentId, Object[] context,
-            ComponentEventHandler handler, TypeCoercer typeCoercer)
-    {
-        _eventType = Defense.notBlank(eventType, "eventType");
-        _originatingComponentId = originatingComponentId;
-        _context = context;
-        _handler = Defense.notNull(handler, "handler");
-        _typeCoercer = Defense.notNull(typeCoercer, "typeCoercer");
-
-        _contextLength = context != null ? context.length : 0;
-    }
-
-    public boolean isAborted()
-    {
-        return _aborted;
-    }
-
-    /**
-     * TODO: This implementation is broken, but will get the job done for simple cases. It just
-     * doesn't do quite the right think when an event bubbles up past its originating component's
-     * container (can lead to false matches).
-     */
-    public boolean matchesByComponentId(ComponentResources resources, String[] componentId)
-    {
-        for (String id : componentId)
-        {
-            if (id.equals(_originatingComponentId))
-                return true;
-        }
-
-        return false;
-    }
-
-    public boolean matchesByEventType(String[] eventTypes)
-    {
-        for (String type : eventTypes)
-        {
-            if (type.equals(_eventType))
-                return true;
-        }
-
-        return false;
-    }
-
-    public void setSource(Component component, String methodDescription)
-    {
-        _component = component;
-        _methodDescription = methodDescription;
-    }
-
-    public boolean storeResult(Object result)
-    { // Given that this method is *only* invoked from code
-        // that is generated at runtime and proven to be correct,
-        // this should never, ever happen. But what the hell,
-        // let's check anyway.
-
-        if (_aborted)
-            throw new IllegalStateException(ServicesMessages
-                    .componentEventIsAborted(_methodDescription));
-
-        if (result == null)
-            return false;
-
-        _handler.handleResult(result, _component, _methodDescription);
-
-        _aborted = true;
-
-        return true;
-    }
-
-    @SuppressWarnings("unchecked")
-    public Object coerceContext(int index, String desiredTypeName)
-    {
-        if (index >= _contextLength)
-            throw new IllegalArgumentException(ServicesMessages
-                    .contextIndexOutOfRange(_methodDescription));
-
-        try
-        {
-            Class desiredType = Class.forName(desiredTypeName);
-
-            return _typeCoercer.coerce(_context[index], desiredType);
-        }
-        catch (Exception ex)
-        {
-            throw new IllegalArgumentException(ServicesMessages.exceptionInMethodParameter(
-                    _methodDescription,
-                    index,
-                    ex), ex);
-        }
-    }
-}
+import org.apache.tapestry.ioc.services.TypeCoercer;
+import org.apache.tapestry.runtime.Component;
+import org.apache.tapestry.runtime.ComponentEvent;
+
+public class ComponentEventImpl implements ComponentEvent
+{
+    private final String _eventType;
+
+    private final String _originatingComponentId;
+
+    private final Object[] _context;
+
+    private final ComponentEventHandler _handler;
+
+    private final TypeCoercer _typeCoercer;
+
+    private boolean _aborted;
+
+    private Component _component;
+
+    private String _methodDescription;
+
+    /**
+     * @param eventType
+     *            non blank string used to identify the type of event that was triggered
+     * @param originatingComponentId
+     *            the id of the component that triggered the event (this will likely need to change
+     *            somewhat)
+     * @param context
+     *            an array of values that can be made available to handler methods via method
+     *            parameters
+     * @param handler
+     *            invoked when a non-null return value is obtained from an event handler method
+     * @param typeCoercer
+     *            used when coercing context values to parameter types
+     */
+    public ComponentEventImpl(String eventType, String originatingComponentId, Object[] context,
+            ComponentEventHandler handler, TypeCoercer typeCoercer)
+    {
+        _eventType = Defense.notBlank(eventType, "eventType");
+        _originatingComponentId = originatingComponentId;
+        _context = context != null ? context : new Object[0];
+        _handler = Defense.notNull(handler, "handler");
+        _typeCoercer = Defense.notNull(typeCoercer, "typeCoercer");
+
+    }
+
+    public boolean isAborted()
+    {
+        return _aborted;
+    }
+
+    /**
+     * TODO: This implementation is broken, but will get the job done for simple cases. It just
+     * doesn't do quite the right think when an event bubbles up past its originating component's
+     * container (can lead to false matches).
+     */
+    public boolean matchesByComponentId(ComponentResources resources, String[] componentId)
+    {
+        for (String id : componentId)
+        {
+            if (id.equals(_originatingComponentId))
+                return true;
+        }
+
+        return false;
+    }
+
+    public boolean matchesByEventType(String[] eventTypes)
+    {
+        for (String type : eventTypes)
+        {
+            if (type.equals(_eventType))
+                return true;
+        }
+
+        return false;
+    }
+
+    public void setSource(Component component, String methodDescription)
+    {
+        _component = component;
+        _methodDescription = methodDescription;
+    }
+
+    @SuppressWarnings("unchecked")
+    public boolean storeResult(Object result)
+    { // Given that this method is *only* invoked from code
+        // that is generated at runtime and proven to be correct,
+        // this should never, ever happen. But what the hell,
+        // let's check anyway.
+
+        if (_aborted)
+            throw new IllegalStateException(ServicesMessages
+                    .componentEventIsAborted(_methodDescription));
+
+        if (result == null)
+            return false;
+
+        _handler.handleResult(result, _component, _methodDescription);
+
+        _aborted = true;
+
+        return true;
+    }
+
+    @SuppressWarnings("unchecked")
+    public Object coerceContext(int index, String desiredTypeName)
+    {
+        if (index >= _context.length)
+            throw new IllegalArgumentException(ServicesMessages
+                    .contextIndexOutOfRange(_methodDescription));
+
+        try
+        {
+            Class desiredType = Class.forName(desiredTypeName);
+
+            return _typeCoercer.coerce(_context[index], desiredType);
+        }
+        catch (Exception ex)
+        {
+            throw new IllegalArgumentException(ServicesMessages.exceptionInMethodParameter(
+                    _methodDescription,
+                    index,
+                    ex), ex);
+        }
+    }
+
+    public Object[] getContext()
+    {
+        return _context;
+    }
+
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryListener.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryListener.java?view=diff&rev=478977&r1=478976&r2=478977
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryListener.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryListener.java Fri Nov 24 12:16:18 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.Link;

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=478977&r1=478976&r2=478977
==============================================================================
--- 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 Fri Nov 24 12:16:18 2006
@@ -12,145 +12,158 @@
 // 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.OnEvent;
+package org.apache.tapestry.internal.services;
+
+import java.util.List;
+
+import org.apache.tapestry.annotations.OnEvent;
 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.MethodSignature;
-import org.apache.tapestry.services.TransformConstants;
-import org.apache.tapestry.services.TransformUtils;
-
-/**
- * Provides implementations of the
- * {@link Component#handleComponentEvent(org.apache.tapestry.runtime.ComponentEvent)} method, based
- * on {@link OnEvent} annotations.
- */
-public class OnEventWorker implements ComponentClassTransformWorker
-{
-    public void transform(ClassTransformation transformation, MutableComponentModel model)
-    {
-        List<MethodSignature> methods = transformation.findMethodsWithAnnotation(OnEvent.class);
-
-        // No methods, no work.
-
-        if (methods.isEmpty())
-            return;
-
-        BodyBuilder builder = new BodyBuilder();
-        builder.begin();
-
-        builder.addln("if ($1.isAborted()) return $_;");
-
-        for (MethodSignature method : methods)
-            addCodeForMethod(builder, method, transformation);
-
-        builder.end();
-
-        transformation.extendMethod(TransformConstants.HANDLE_COMPONENT_EVENT, builder.toString());
-    }
-
-    private void addCodeForMethod(BodyBuilder builder, MethodSignature method,
-            ClassTransformation transformation)
-    {
-        // $1 is the event
-
-        int closeCount = 0;
-
-        OnEvent annotation = transformation.getMethodAnnotation(method, OnEvent.class);
-
-        String[] eventTypes = annotation.value();
-
-        if (eventTypes.length > 0)
-        {
-            String fieldName = transformation.addInjectedField(
-                    String[].class,
-                    "eventTypes",
-                    eventTypes);
-
-            builder.addln("if ($1.matchesByEventType(%s))", fieldName);
-            builder.begin();
-
-            closeCount++;
-        }
-
-        String[] componentIds = annotation.component();
-
-        if (componentIds.length > 0)
-        {
-            String fieldName = transformation.addInjectedField(
-                    String[].class,
-                    "componentIds",
-                    componentIds);
-
-            builder.addln("if ($1.matchesByComponentId(%s, %s))", transformation
-                    .getResourcesFieldName(), fieldName);
-            builder.begin();
-
-            closeCount++;
-        }
-
-        // Ensure that we return true, because *some* event handler method was invoked,
-        // even if it chose not to abort the event.
-
-        builder.addln("$_ = true;");
-
-        // Several subsequent calls need to know the method name.
-
-        builder.addln("$1.setSource(this, \"%s.%s\");", transformation.getClassName(), method
-                .getMediumDescription());
-
-        boolean isNonVoid = !method.getReturnType().equals("void");
+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.MethodSignature;
+import org.apache.tapestry.services.TransformConstants;
+import org.apache.tapestry.services.TransformUtils;
+
+/**
+ * Provides implementations of the
+ * {@link Component#handleComponentEvent(org.apache.tapestry.runtime.ComponentEvent)} method, based
+ * on {@link OnEvent} annotations.
+ */
+public class OnEventWorker implements ComponentClassTransformWorker
+{
+    static final String OBJECT_ARRAY_TYPE = "java.lang.Object[]";
+
+    public void transform(ClassTransformation transformation, MutableComponentModel model)
+    {
+        List<MethodSignature> methods = transformation.findMethodsWithAnnotation(OnEvent.class);
+
+        // No methods, no work.
+
+        if (methods.isEmpty())
+            return;
+
+        BodyBuilder builder = new BodyBuilder();
+        builder.begin();
+
+        builder.addln("if ($1.isAborted()) return $_;");
+
+        for (MethodSignature method : methods)
+            addCodeForMethod(builder, method, transformation);
+
+        builder.end();
+
+        transformation.extendMethod(TransformConstants.HANDLE_COMPONENT_EVENT, builder.toString());
+    }
+
+    private void addCodeForMethod(BodyBuilder builder, MethodSignature method,
+            ClassTransformation transformation)
+    {
+        // $1 is the event
+
+        int closeCount = 0;
+
+        OnEvent annotation = transformation.getMethodAnnotation(method, OnEvent.class);
+
+        String[] eventTypes = annotation.value();
+
+        if (eventTypes.length > 0)
+        {
+            String fieldName = transformation.addInjectedField(
+                    String[].class,
+                    "eventTypes",
+                    eventTypes);
+
+            builder.addln("if ($1.matchesByEventType(%s))", fieldName);
+            builder.begin();
+
+            closeCount++;
+        }
+
+        String[] componentIds = annotation.component();
+
+        if (componentIds.length > 0)
+        {
+            String fieldName = transformation.addInjectedField(
+                    String[].class,
+                    "componentIds",
+                    componentIds);
+
+            builder.addln("if ($1.matchesByComponentId(%s, %s))", transformation
+                    .getResourcesFieldName(), fieldName);
+            builder.begin();
+
+            closeCount++;
+        }
+
+        // Ensure that we return true, because *some* event handler method was invoked,
+        // even if it chose not to abort the event.
+
+        builder.addln("$_ = true;");
+
+        // Several subsequent calls need to know the method name.
+
+        builder.addln("$1.setSource(this, \"%s.%s\");", transformation.getClassName(), method
+                .getMediumDescription());
+
+        boolean isNonVoid = !method.getReturnType().equals("void");
 
         // Store the result, converting primitives to wrappers automatically.
-        
-        if (isNonVoid)
-            builder.add("if ($1.storeResult(($w) ");
-
-        builder.add("%s(", method.getMethodName());
-
-        for (int i = 0; i < method.getParameterTypes().length; i++)
-        {
-            if (i > 0)
-                builder.add(", ");
-
-            addCoercedContextAsParameter(builder, i, method.getParameterTypes()[i]);
-        }
-
-        if (isNonVoid)
-            builder.addln("))) return true;");
-        else
-            builder.addln(");");
-
-        for (int i = 0; i < closeCount; i++)
-            builder.end();
-    }
-
-    private void addCoercedContextAsParameter(BodyBuilder builder, int index, String type)
-    {
-        boolean isPrimitive = TransformUtils.isPrimitive(type);
-        String wrapperType = TransformUtils.getWrapperTypeName(type);
-
-        // Add a cast to the wrapper type up front
-
-        if (isPrimitive)
-            builder.add("((%s)", wrapperType);
-
-        // The strings for desired type name will likely repeat a bit; it may be
-        // worth it to inject them as final fields. Could increase the number
-        // of constructor parameters pretty dramatically, however, and will reduce
-        // the readability of the output method bodies.
-
-        builder.add("$1.coerceContext(%d, \"%s\")", index, wrapperType);
-
-        // and invoke a method on the cast value to get back to primitive
-        if (isPrimitive)
-            builder.add(").%s()", TransformUtils.getUnwrapperMethodName(type));
-    }
-
-}
+
+        if (isNonVoid)
+            builder.add("if ($1.storeResult(($w) ");
+
+        builder.add("%s(", method.getMethodName());
+
+        buildMethodParameters(builder, method);
+
+        if (isNonVoid)
+            builder.addln("))) return true;");
+        else
+            builder.addln(");");
+
+        for (int i = 0; i < closeCount; i++)
+            builder.end();
+    }
+
+    private void buildMethodParameters(BodyBuilder builder, MethodSignature method)
+    {
+        int contextIndex = 0;
+
+        for (int i = 0; i < method.getParameterTypes().length; i++)
+        {
+            if (i > 0)
+                builder.add(", ");
+
+            String type = method.getParameterTypes()[i];
+
+            // Type Object[] is a special case, it gets all of the context parameters in one go.
+
+            if (type.equals(OBJECT_ARRAY_TYPE))
+            {
+                builder.add("$1.getContext()");
+                continue;
+            }
+
+            boolean isPrimitive = TransformUtils.isPrimitive(type);
+            String wrapperType = TransformUtils.getWrapperTypeName(type);
+
+            // Add a cast to the wrapper type up front
+
+            if (isPrimitive)
+                builder.add("((%s)", wrapperType);
+
+            // The strings for desired type name will likely repeat a bit; it may be
+            // worth it to inject them as final fields. Could increase the number
+            // of constructor parameters pretty dramatically, however, and will reduce
+            // the readability of the output method bodies.
+
+            builder.add("$1.coerceContext(%d, \"%s\")", contextIndex++, wrapperType);
+
+            // and invoke a method on the cast value to get back to primitive
+            if (isPrimitive)
+                builder.add(").%s()", TransformUtils.getUnwrapperMethodName(type));
+        }
+    }
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/ComponentEvent.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/ComponentEvent.java?view=diff&rev=478977&r1=478976&r2=478977
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/ComponentEvent.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/ComponentEvent.java Fri Nov 24 12:16:18 2006
@@ -12,77 +12,80 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.runtime;
-
-import org.apache.tapestry.ComponentEventHandler;
-import org.apache.tapestry.ComponentResources;
-import org.apache.tapestry.ComponentResourcesCommon;
-
-/**
- * An event that may originate in application logic, or as a result of a client interaction (a GET
- * or POST from the client).
- * 
- * @see ComponentResourcesCommon#triggerEvent(String, Object[],
- *      org.apache.tapestry.ComponentEventHandler)
- * @see ComponentEventHandler
- */
-public interface ComponentEvent
-{
-    /**
-     * Returns true if the event has been aborted (meaning that the return value from some event
-     * handler method was accepted, and processing of the event was terminated).
-     * 
-     * @return true if no further event handler methods should be invoked
-     */
-    boolean isAborted();
-
-    /**
-     * Returns true if the component event's type matches
-     * 
-     * @param eventTypes
-     * @return
-     */
-    boolean matchesByEventType(String[] eventTypes);
-
-    /**
-     * Returns true if the originating component matches any of the components identified by their
-     * ids. This filter is only relevent in the immediate container of the originating component (it
-     * will never match at higher levels).
-     */
-    boolean matchesByComponentId(ComponentResources resources, String[] componentId);
-
-    /**
-     * Invoked before {@link #coerceContext(int, String, String)} or
-     * {@link #storeResult(Object, String)} to identify, to the event, what method is being acted
-     * upon (used for some kinds of exception reporting).
-     * 
-     * @param component
-     *            the component instance from which the result was obtained
-     * @param methodDescription
-     */
-    void setSource(Component component, String methodDescription);
-
-    /**
-     * Stores a result for the event. Storing a non-null result value will abort the event.
-     * 
-     * @param result
-     *            the result obtained from a method invocations
-     * @return true if the event is now aborted
-     */
-    boolean storeResult(Object result);
-
-    /**
-     * Coerces a context value to a particular type. The context is an array of objects; typically
-     * it is an array of strings of extra path information encoded into the action URL.
-     * 
-     * @param <T>
-     * @param index
-     *            the index of the context value
-     * @param desiredTypeName
-     *            the desired type
-     * @param methodDescription
-     *            the method for which the conversion will take place (used if reporting an error)
-     * @return the coerced value (a wrapper type if the desired type is a primitive)
-     */
-    Object coerceContext(int index, String desiredTypeName);
+package org.apache.tapestry.runtime;
+
+import org.apache.tapestry.ComponentEventHandler;
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.ComponentResourcesCommon;
+
+/**
+ * An event that may originate in application logic, or as a result of a client interaction (a GET
+ * or POST from the client).
+ * 
+ * @see ComponentResourcesCommon#triggerEvent(String, Object[],
+ *      org.apache.tapestry.ComponentEventHandler)
+ * @see ComponentEventHandler
+ */
+public interface ComponentEvent
+{
+    /**
+     * Returns true if the event has been aborted (meaning that the return value from some event
+     * handler method was accepted, and processing of the event was terminated).
+     * 
+     * @return true if no further event handler methods should be invoked
+     */
+    boolean isAborted();
+
+    /**
+     * Returns true if the component event's type matches
+     * 
+     * @param eventTypes
+     * @return
+     */
+    boolean matchesByEventType(String[] eventTypes);
+
+    /**
+     * Returns true if the originating component matches any of the components identified by their
+     * ids. This filter is only relevent in the immediate container of the originating component (it
+     * will never match at higher levels).
+     */
+    boolean matchesByComponentId(ComponentResources resources, String[] componentId);
+
+    /**
+     * Invoked before {@link #coerceContext(int, String, String)} or
+     * {@link #storeResult(Object, String)} to identify, to the event, what method is being acted
+     * upon (used for some kinds of exception reporting).
+     * 
+     * @param component
+     *            the component instance from which the result was obtained
+     * @param methodDescription
+     */
+    void setSource(Component component, String methodDescription);
+
+    /**
+     * Stores a result for the event. Storing a non-null result value will abort the event.
+     * 
+     * @param result
+     *            the result obtained from a method invocations
+     * @return true if the event is now aborted
+     */
+    boolean storeResult(Object result);
+
+    /**
+     * Coerces a context value to a particular type. The context is an array of objects; typically
+     * it is an array of strings of extra path information encoded into the action URL.
+     * 
+     * @param <T>
+     * @param index
+     *            the index of the context value
+     * @param desiredTypeName
+     *            the desired type
+     * @param methodDescription
+     *            the method for which the conversion will take place (used if reporting an error)
+     * @return the coerced value (a wrapper type if the desired type is a primitive)
+     */
+    Object coerceContext(int index, String desiredTypeName);
+
+    /** Returns the raw context as a (possibly empty) array. */
+    Object[] getContext();
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/coercion.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/coercion.apt?view=diff&rev=478977&r1=478976&r2=478977
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/coercion.apt (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/coercion.apt Fri Nov 24 12:16:18 2006
@@ -35,9 +35,10 @@
   Merry Christmas: <t:comp type="Count" end="3"> Ho! </t:comp>
 +---+
 
-  Here the end parameter is bound to the <literal string> "3".
+  A bare whole number is interpreted by the prop binding prefix as a long. So this is the <long> value 3.
   
-  Tapestry will automatically coerce the bound value, a string, to the parameter's type, int.
+  Tapestry will automatically coerce the bound value, a long, to the parameter's type, int. This may be a lossy coercion (if the long represents a number larger
+  than can be stored in an int).
   
 TypeCoercer Service
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/event.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/event.apt?view=diff&rev=478977&r1=478976&r2=478977
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/event.apt (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/event.apt Fri Nov 24 12:16:18 2006
@@ -77,6 +77,11 @@
   {{{coercion.html}coercion}} from string to the actual type occurs. A runtime exception
   occurs if there are more parameters than context strings.
   
+  Alternately, an event handler method may take a parameter of type java.lang.Object[].  This parameter
+  will receive the entire context array. This is useful when, for example, the context
+  is off different lengths at different times. You should use either explicit, typed parameters or
+  a single parameter of type Object[].
+  
 Event Bubbling
 
   The event will bubble up the hierarchy, until it is aborted. The event is aborted
@@ -121,5 +126,41 @@
   {{{persist.html}page persistence}} to allow information from the action request
   to survive to the next render request.
   
+Passivate and Activate Events
+
+  When a link to a page is created, the page will be sent a passivate event. The event handler method for the
+  passivate event may return a single value, or a list or array of values. 
+  These objects will become the context portion of the URI.  When a page is later rendered, the context is extracted from the URI and
+  passed to any activate event handlers.  In this way, information about the page can be encoded into URIs for the page, in a light-weight
+  (and human readable) manner.
+  
+  This was created with the intent of handling pages that exist to view (or perhaps edit) a specific instance of some type; in the
+  context of a CRUD application, the context is used to store the primary key of some entity object:
+    
++---+
+
+  @ComponentClass
+  public class ViewCustomer
+  {
+    private Customer _customer;
+    
+    @Inject
+    private CustomerDAO _customerDAO;
+    
+    @OnEvent("passivate')
+    long passivate()
+    {
+      return _customer.getId();
+    }
+  
+    @OnEvent("activate")
+    void activate(long customerId)
+    {
+      _customer = _customerDAO.get(customerId);
+    }
+  }
++---+
+  
+  Render page request URI's for this page would be created as <<</ViewCustomer.html/1234>>>.
   
    

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/conf/testng.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/conf/testng.xml?view=diff&rev=478977&r1=478976&r2=478977
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/conf/testng.xml (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/conf/testng.xml Fri Nov 24 12:16:18 2006
@@ -15,7 +15,7 @@
    limitations under the License.
 -->
 
-<suite name="Tapestry Core" parallel="true" thread-count="10" annotations="1.5" verbose="2">
+<suite name="Tapestry Core" parallel="false" annotations="1.5" verbose="2">
   <test name="Tapestry Core">
     <packages>
       <package name="org.apache.tapestry.integration"/>

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/OnEventWorkerTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/OnEventWorkerTest.java?view=diff&rev=478977&r1=478976&r2=478977
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/OnEventWorkerTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/OnEventWorkerTest.java Fri Nov 24 12:16:18 2006
@@ -14,6 +14,8 @@
 
 package org.apache.tapestry.internal.services;
 
+import static java.lang.String.format;
+
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Modifier;
 import java.util.Collections;
@@ -388,7 +390,49 @@
         new OnEventWorker().transform(ct, model);
 
         verify();
+    }
+
+    @Test
+    public void method_include_context()
+    {
+        ClassTransformation ct = newClassTransformation();
+        MutableComponentModel model = newMutableComponentModel();
+        OnEvent annotation = newOnEvent();
+
+        MethodSignature signature = new MethodSignature(Modifier.PRIVATE, "void", "foo",
+                new String[]
+                { "java.lang.String", OnEventWorker.OBJECT_ARRAY_TYPE, "java.lang.Integer" }, null);
+
+        List<MethodSignature> methods = Collections.singletonList(signature);
+        train_findMethodsWithAnnotation(ct, OnEvent.class, methods);
+
+        train_getMethodAnnotation(ct, signature, OnEvent.class, annotation);
+
+        train_value(annotation, new String[0]);
+        train_component(annotation, new String[0]);
 
+        train_getClassName(ct, "foo.Bar");
+
+        // Notice that the context doesn't affect the indexing of the other parameters. Though it is
+        // unlikely that a method would use both a context array and explicit context parameters.
+
+        train_extendMethod(
+                ct,
+                TransformConstants.HANDLE_COMPONENT_EVENT,
+                "{",
+                BOILERPLATE_1,
+                BOILERPLATE_2,
+                format("$1.setSource(this, \"foo.Bar.%s\");", signature.getMediumDescription()),
+                "foo($1.coerceContext(0, \"java.lang.String\"), ",
+                "$1.getContext(), ",
+                "$1.coerceContext(1, \"java.lang.Integer\"));",
+                "}");
+
+        replay();
+
+        new OnEventWorker().transform(ct, model);
+
+        verify();
     }
 
     protected final OnEvent newOnEvent()