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 2007/11/15 18:30:40 UTC

svn commit: r595372 - in /tapestry/tapestry5/trunk: ./ tapestry-core/src/main/java/org/apache/tapestry/ tapestry-core/src/main/java/org/apache/tapestry/corelib/base/ tapestry-core/src/main/java/org/apache/tapestry/corelib/components/ tapestry-core/src/...

Author: hlship
Date: Thu Nov 15 09:30:35 2007
New Revision: 595372

URL: http://svn.apache.org/viewvc?rev=595372&view=rev
Log:
TAPESTRY-1476: Component events for input translation and validation

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/internal/ComponentTranslatorWrapper.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/runtime/ComponentEventException.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/app1/EventMethodTranslate.tml
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/corelib/internal/ComponentTranslatorWrapperTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/EventMethodTranslate.java
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/base/AbstractTextField.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/DateField.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/PasswordField.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/internal/InternalMessages.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/OnEventWorker.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/runtime/Component.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/runtime/ComponentEvent.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/runtime/Event.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/internal/InternalStrings.properties
    tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/event.apt
    tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/validation.apt
    tapestry/tapestry5/trunk/tapestry-core/src/site/apt/index.apt
    tapestry/tapestry5/trunk/tapestry-core/src/test/app1/Start.tml
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/NewIntegrationTests.java
    tapestry/tapestry5/trunk/tapestry5.ipr

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java?rev=595372&r1=595371&r2=595372&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ComponentResourcesCommon.java Thu Nov 15 09:30:35 2007
@@ -94,6 +94,9 @@
      *                  allowed even if the handler is null).
      * @return true if any event handler was invoked (even if no event handler method returns a
      *         non-null value)
+     * @throws org.apache.tapestry.runtime.ComponentEventException
+     *          if an event handler method throws a checked
+     *          or unchecked exception
      * @see OnEventWorker
      * @see OnEvent
      */

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/base/AbstractTextField.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/base/AbstractTextField.java?rev=595372&r1=595371&r2=595372&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/base/AbstractTextField.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/base/AbstractTextField.java Thu Nov 15 09:30:35 2007
@@ -19,6 +19,7 @@
 import org.apache.tapestry.annotations.BeginRender;
 import org.apache.tapestry.annotations.Environmental;
 import org.apache.tapestry.annotations.Parameter;
+import org.apache.tapestry.corelib.internal.ComponentTranslatorWrapper;
 import org.apache.tapestry.ioc.Messages;
 import org.apache.tapestry.ioc.annotations.Inject;
 import org.apache.tapestry.services.*;
@@ -28,6 +29,18 @@
 /**
  * Abstract class for a variety of components that render some variation of a text field. Most of
  * the hooks for user input validation are in this class.
+ * <p/>
+ * In particular, all subclasses support the "toclient" and "parseclient" events.  These two events
+ * allow the normal {@link Translator} (specified by the translate parameter, but often automatically
+ * derived by Tapestry) to be augmented.
+ * <p/>
+ * If the component container (i.e., the page) provides an event handler method for the "toclient" event,
+ * and that handler returns a non-null string, that will be the string value sent to the client. The context
+ * passed to the event handler method is the current value of the value parameter.
+ * <p/>
+ * Likewise, on a form submit, the "parseclient" event handler method will be passed the string provided
+ * by the client, and may provide a non-null value as the parsed value.  Returning null allows the normal
+ * translator to operate.  The event handler may also throw {@link org.apache.tapestry.ValidationException}.
  */
 public abstract class AbstractTextField extends AbstractField
 {
@@ -103,13 +116,9 @@
 
         if (type == null) return null;
 
-        return _fieldValidatorDefaultSource.createDefaultValidator(
-                this,
-                _resources.getId(),
-                _resources.getContainerMessages(),
-                _locale,
-                type,
-                _resources.getAnnotationProvider("value"));
+        return _fieldValidatorDefaultSource.createDefaultValidator(this, _resources.getId(),
+                                                                   _resources.getContainerMessages(), _locale, type,
+                                                                   _resources.getAnnotationProvider("value"));
     }
 
     /**
@@ -121,12 +130,19 @@
         return createDefaultParameterBinding("value");
     }
 
+    @SuppressWarnings({"unchecked"})
     @BeginRender
     final void begin(MarkupWriter writer)
     {
         String value = _tracker.getInput(this);
 
-        if (value == null) value = _translate.toClient(_value);
+        if (value == null)
+        {
+
+            Translator wrapper = new ComponentTranslatorWrapper(_resources, _translate);
+
+            value = wrapper.toClient(_value);
+        }
 
         writeFieldTag(writer, value);
 
@@ -164,7 +180,11 @@
 
         try
         {
-            Object translated = _translate.parseClient(rawValue, messages);
+            Translator wrapper = new ComponentTranslatorWrapper(_resources, _translate);
+
+            Object translated = wrapper.parseClient(rawValue, messages);
+
+            // TODO: A wrapper for validation as well?
 
             _validate.validate(translated);
 
@@ -173,7 +193,6 @@
         catch (ValidationException ex)
         {
             _tracker.recordError(this, ex.getMessage());
-            return;
         }
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/DateField.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/DateField.java?rev=595372&r1=595371&r2=595372&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/DateField.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/DateField.java Thu Nov 15 09:30:35 2007
@@ -122,13 +122,10 @@
     final FieldValidator defaultValidate()
     {
 
-        return _fieldValidatorDefaultSource.createDefaultValidator(
-                this,
-                _resources.getId(),
-                _resources.getContainerMessages(),
-                _locale,
-                Date.class,
-                _resources.getAnnotationProvider("value"));
+        return _fieldValidatorDefaultSource.createDefaultValidator(this, _resources.getId(),
+                                                                   _resources.getContainerMessages(), _locale,
+                                                                   Date.class,
+                                                                   _resources.getAnnotationProvider("value"));
     }
 
     void beginRender(MarkupWriter writer)
@@ -145,7 +142,9 @@
 
         // TODO: Support a disabled parameter
 
-        writer.element("input", "type", "text",
+        writer.element("input",
+
+                       "type", "text",
 
                        "name", getElementName(),
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/PasswordField.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/PasswordField.java?rev=595372&r1=595371&r2=595372&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/PasswordField.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/PasswordField.java Thu Nov 15 09:30:35 2007
@@ -27,16 +27,15 @@
     @Override
     protected final void writeFieldTag(MarkupWriter writer, String value)
     {
-        writer.element(
-                "input",
-                "type",
-                "password",
-                "name",
-                getElementName(),
-                "id",
-                getClientId(),
-                "value",
-                "");
+        writer.element("input",
+
+                       "type", "password",
+
+                       "name", getElementName(),
+
+                       "id", getClientId(),
+
+                       "value", "");
     }
 
     final void afterRender(MarkupWriter writer)

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/internal/ComponentTranslatorWrapper.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/internal/ComponentTranslatorWrapper.java?rev=595372&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/internal/ComponentTranslatorWrapper.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/internal/ComponentTranslatorWrapper.java Thu Nov 15 09:30:35 2007
@@ -0,0 +1,115 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.corelib.internal;
+
+import org.apache.tapestry.ComponentEventHandler;
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.Translator;
+import org.apache.tapestry.ValidationException;
+import org.apache.tapestry.internal.util.Holder;
+import org.apache.tapestry.ioc.Messages;
+import org.apache.tapestry.runtime.Component;
+import org.apache.tapestry.runtime.ComponentEventException;
+
+/**
+ * A wrapoer around a component and a {@link org.apache.tapestry.Translator} that allows the component
+ * priority in terms of the behaviors of the translator.  Events fired upon the component
+ * may bypass corresponding methods of the translator, allowing code in the component to
+ * customize translator-like behavior.
+ */
+@SuppressWarnings("unchecked")
+public class ComponentTranslatorWrapper implements Translator
+{
+    static final String PARSE_CLIENT_EVENT = "parseClient";
+    static final String TO_CLIENT_EVENT = "toClient";
+
+    private final ComponentResources _resources;
+    private final Translator _delegate;
+
+    /**
+     * @param resources resources of component, used to trigger events
+     * @param delegate  translator used when the component does not provide an event handler
+     */
+    public ComponentTranslatorWrapper(ComponentResources resources, Translator delegate)
+    {
+        _delegate = delegate;
+        _resources = resources;
+    }
+
+
+    public Object parseClient(String clientValue, Messages messages) throws ValidationException
+    {
+        final Holder<Object> resultHolder = Holder.create();
+
+        ComponentEventHandler handler = new ComponentEventHandler()
+        {
+            public boolean handleResult(Object result, Component component, String methodDescription)
+            {
+                resultHolder.put(result);
+                return true;
+            }
+        };
+
+        try
+        {
+            _resources.triggerEvent(PARSE_CLIENT_EVENT, new Object[]{clientValue}, handler);
+        }
+        catch (ComponentEventException ex)
+        {
+            Throwable cause = ex.getCause();
+
+            if (cause instanceof ValidationException) throw (ValidationException) cause;
+
+            // Rethrow the event exception for reporting higher up.
+
+            throw ex;
+        }
+
+        if (resultHolder.hasValue()) return resultHolder.get();
+
+        // Otherwise, let the normal translator do the job.
+
+        return _delegate.parseClient(clientValue, messages);
+    }
+
+    public String toClient(Object value)
+    {
+        final Holder<String> resultHolder = Holder.create();
+
+        ComponentEventHandler handler = new ComponentEventHandler()
+        {
+            public boolean handleResult(Object result, Component component, String methodDescription)
+            {
+                // What's nice is that the ComponentEventException will automatically identify
+                // the method description.
+
+                if (!(result instanceof String))
+                    throw new RuntimeException(InternalMessages.toClientShouldReturnString());
+
+                resultHolder.put((String) result);
+
+                return true;
+            }
+        };
+
+        _resources.triggerEvent(TO_CLIENT_EVENT, new Object[]{value}, handler);
+
+        if (resultHolder.hasValue()) return resultHolder.get();
+
+        return _delegate.toClient(value);
+    }
+
+
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/internal/InternalMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/internal/InternalMessages.java?rev=595372&r1=595371&r2=595372&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/internal/InternalMessages.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/internal/InternalMessages.java Thu Nov 15 09:30:35 2007
@@ -32,8 +32,7 @@
         return MESSAGES.get("enclose-errors-in-form");
     }
 
-    public static String failureInstantiatingObject(Class objectType, String componentId,
-                                                    Throwable cause)
+    public static String failureInstantiatingObject(Class objectType, String componentId, Throwable cause)
     {
         return MESSAGES.format("failure-instantitating-object", ClassFabUtils
                 .toJavaClassName(objectType), componentId, cause);
@@ -42,5 +41,10 @@
     public static String conflictingEncodingType(String existing, String conflicting)
     {
         return MESSAGES.format("conflicting-encoding-type", existing, conflicting);
+    }
+
+    public static String toClientShouldReturnString()
+    {
+        return MESSAGES.format("to-client-should-return-string");
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/OnEventWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/OnEventWorker.java?rev=595372&r1=595371&r2=595372&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/OnEventWorker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/OnEventWorker.java Thu Nov 15 09:30:35 2007
@@ -19,6 +19,7 @@
 import org.apache.tapestry.ioc.util.BodyBuilder;
 import org.apache.tapestry.model.MutableComponentModel;
 import org.apache.tapestry.runtime.Component;
+import org.apache.tapestry.runtime.ComponentEventException;
 import org.apache.tapestry.services.*;
 
 import java.util.List;
@@ -40,11 +41,9 @@
         {
             public boolean accept(TransformMethodSignature signature)
             {
-                return signature.getMethodName().startsWith("on")
-                        || transformation.getMethodAnnotation(signature, OnEvent.class) != null;
+                return signature.getMethodName().startsWith("on") || transformation.getMethodAnnotation(signature,
+                                                                                                        OnEvent.class) != null;
             }
-
-            ;
         };
 
         List<TransformMethodSignature> methods = transformation.findMethods(filter);
@@ -58,9 +57,24 @@
 
         builder.addln("if ($1.isAborted()) return $_;");
 
+        builder.addln("String methodDescription = null;");
+
+        // Because we track the methodDescription inside a local variable, we can have a single
+        // encompassing try..catch for all possible events for this component.
+
+        builder.addln("try");
+        builder.begin();
+
         for (TransformMethodSignature method : methods)
             addCodeForMethod(builder, method, transformation);
 
+        builder.end(); // try
+
+        builder.addln("catch (Exception ex)");
+        builder.begin();
+        builder.addln("throw new %s(ex.getMessage(), methodDescription, ex);", ComponentEventException.class.getName());
+        builder.end();
+
         builder.end();
 
         transformation.extendMethod(TransformConstants.HANDLE_COMPONENT_EVENT, builder.toString());
@@ -112,7 +126,9 @@
 
         // Several subsequent calls need to know the method name.
 
-        builder.addln("$1.setSource(this, \"%s\");", transformation.getMethodIdentifier(method));
+        builder.addln("methodDescription = \"%s\";", transformation.getMethodIdentifier(method));
+        builder.addln("$1.setSource(this, methodDescription);");
+
 
         boolean isNonVoid = !method.getReturnType().equals("void");
 
@@ -124,10 +140,8 @@
 
         buildMethodParameters(builder, method);
 
-        if (isNonVoid)
-            builder.addln("))) return true;");
-        else
-            builder.addln(");");
+        if (isNonVoid) builder.addln("))) return true;");
+        else builder.addln(");");
 
         for (int i = 0; i < closeCount; i++)
             builder.end();

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/runtime/Component.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/runtime/Component.java?rev=595372&r1=595371&r2=595372&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/runtime/Component.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/runtime/Component.java Thu Nov 15 09:30:35 2007
@@ -18,7 +18,7 @@
 import org.apache.tapestry.annotations.OnEvent;
 
 /**
- * Interface that defining the lifecycle of a component, within a page, allowing for callbacks into
+ * Interface that defines the lifecycle of a component, within a page, allowing for callbacks into
  * the component for many different events. This interface is part of the public API for Tapestry,
  * but is <em>not</em> expected to be directly implemented by component classes; it should only be
  * implemented as part of the component class transformation process.
@@ -81,11 +81,12 @@
     void cleanupRender(MarkupWriter writer, Event event);
 
     /**
-     * Invoked to handle a component event. Methods with the {@link OnEvent} annotation will be
-     * invoked until one returns a non-null value.
+     * Invoked to handle a component event. Methods with the {@link OnEvent} annotation (or the matching
+     * naming convention) will be invoked until one returns a non-null value.
      *
      * @param event
      * @return true if any handler was found (and invoked), false otherwise
+     * @throws ComponentEventException if an event handler throws an exception
      */
     boolean handleComponentEvent(ComponentEvent event);
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/runtime/ComponentEvent.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/runtime/ComponentEvent.java?rev=595372&r1=595371&r2=595372&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/runtime/ComponentEvent.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/runtime/ComponentEvent.java Thu Nov 15 09:30:35 2007
@@ -55,10 +55,8 @@
      * 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)
+     * @param index           the index of the context value
+     * @param desiredTypeName the desired type
      * @return the coerced value (a wrapper type if the desired type is a primitive)
      */
     Object coerceContext(int index, String desiredTypeName);

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/runtime/ComponentEventException.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/runtime/ComponentEventException.java?rev=595372&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/runtime/ComponentEventException.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/runtime/ComponentEventException.java Thu Nov 15 09:30:35 2007
@@ -0,0 +1,40 @@
+// Copyright 2007 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.runtime;
+
+/**
+ * An exception thrown while invoking a component event handler method.
+ */
+public class ComponentEventException extends RuntimeException
+{
+    private final String _methodDescription;
+
+    /**
+     * @param message           message describing the problem, usually obtained from the underly cause exception
+     * @param methodDescription describes the method being invoke that threw the cause exception
+     * @param cause             the underlying exception actually thrown by the event handler method
+     */
+    public ComponentEventException(String message, String methodDescription, Throwable cause)
+    {
+        super(message, cause);
+
+        _methodDescription = methodDescription;
+    }
+
+    public String getMethodDescription()
+    {
+        return _methodDescription;
+    }
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/runtime/Event.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/runtime/Event.java?rev=595372&r1=595371&r2=595372&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/runtime/Event.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/runtime/Event.java Thu Nov 15 09:30:35 2007
@@ -38,7 +38,7 @@
      * some kinds of exception reporting).
      *
      * @param component         the component instance from which the result was obtained
-     * @param methodDescription
+     * @param methodDescription describes the location (i.e. file name, method name and line number) of the method
      */
     void setSource(Component component, String methodDescription);
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/internal/InternalStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/internal/InternalStrings.properties?rev=595372&r1=595371&r2=595372&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/internal/InternalStrings.properties (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/internal/InternalStrings.properties Thu Nov 15 09:30:35 2007
@@ -12,7 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-component-action-not-serializable=Error serializing component action for component %s: %s
+component-action-not-serializable=Error serializing component action for component %s: %s
 enclose-errors-in-form=The Errors component must be enclosed by a Form component.
 failure-instantitating-object=Exception instantiating instance of %s (for component '%s'): %s
-conflicting-encoding-type=Encoding type of form has already been set to '%s' and may not be changed to '%s'.
\ No newline at end of file
+conflicting-encoding-type=Encoding type of form has already been set to '%s' and may not be changed to '%s'.
+to-client-should-return-string=Return value from 'parseClient' event handler method must be a string.

Modified: tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/event.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/event.apt?rev=595372&r1=595371&r2=595372&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/event.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/event.apt Thu Nov 15 09:30:35 2007
@@ -27,7 +27,9 @@
   In Tapestry 4, you would configure a parameter of a component with the name of a method to invoke
   when a certain event occured, usually a request from the client.
   
-  This has some limitations, including the fact that only a single method could be invoked.
+  This has some limitations, including the fact that only a single method could be invoked, and that
+  it tied together a component and a method based on a method name, which required careful coordination
+  between the template and the Java code.
   
   Tapestry 5 introduces the concept of <event handler methods>, identified via a naming convention, or
   via the 
@@ -64,7 +66,8 @@
   This URL doesn't say what happens when the link is clicked, it identifies <which component is responsible>.
 
   There's no simple mapping from URL to a piece of code; instead the component sends notifications, in the form
-  of invocations of event handler methods,   
+  of invocations of event handler methods, and Tapestry ensures that the correct bit of code, that you supply,
+  gts invoked.
   
    
   A Java method can be invoked when the link for the component is clicked by the user:
@@ -104,6 +107,8 @@
   "action" is the name of the default event type; the ActionLink and Form components each use this event type.
   If you omit the component part of the OnEvent annotation, then you'll recieve notifications from
   <all> contained components, possibly including nested components (due to event bubbling).
+
+  As elsewhere, the comparison of event type and component id is caseless.
   
   You should qualify exactly which component(s) you wish to recieve events from.
   
@@ -112,7 +117,7 @@
   As an alternative to the use of annotations, you may name your events in a specific fashion, and Tapestry will invoke your methods just as if
   they were annotated.  
   
-  This style of event handler methods start with the prefix "on", followed by the name of the action (capitalized).  You may then continue by adding "From" and
+  This style of event handler methods start with the prefix "on", followed by the name of the action.  You may then continue by adding "From" and
   a capitalized component id.
   
   The previous example may be rewritten as:
@@ -124,14 +129,30 @@
   }
 +---+  
   
-  If you method is named "onAnyEvent", it will be invoked for all types of events. This is rarely what you want!
+  If your method is named "onAnyEvent", it will be invoked for all types of events. This is rarely what you want!
     
   Note from Howard: I've found that I prefer the naming convention approach, and reserve the annotation just for situations that don't otherwise fit.  
   
 Event Handler Method Return Values
   
   For page navigation events, the value returned from an event handler method {{{pagenav.html}determines how Tapestry will render a response}}.
-  
+
+Multiple Method Matches
+
+ In some cases, you may have multiple event methods match a single event.
+
+ The order is as follows:
+
+ * Base class methods before sub-class methods.
+
+ * Matching methods within a class in alphabetical order.
+
+ * For a single method name with multiple overrides, by number of parameters, descending.
+
+ []
+
+ There's only rare cases where it makes sense for more than one method to handle an event.
+
 Event Context
 
   The context values (the context parameter to the ActionLink component) can be any object.
@@ -158,4 +179,38 @@
 
   The event will bubble up the hierarchy, until it is aborted. The event is aborted
   when an event handler method returns a non-null value.
+
+Event Method Exceptions
+
+  Event methods are allow to throw any exception (not just runtime exceptions). If an event method does
+  throw an exception, Tapestry will catch the thrown exception and ultimately display the exception report
+  page.
+
+  In other words, there's no need to do this:
+
++---+
+  void onActionFromRunQuery()
+  {
+    try
+    {
+      _dao.executeQuery();
+    }
+    catch (JDBCException ex)
+    {
+      throw new RuntimeException(ex);
+    }
+  }
++---+
+
+  Instead, you may simply say:
+
++---+
+  void onActionFromRunQuery() throws JDBCException
+  {
+    _dao.executeQuery();
+  }
++----+
+
+
+  Your event handler method may even declare that it "throws Exception" if that is more convienient.
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/validation.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/validation.apt?rev=595372&r1=595371&r2=595372&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/validation.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/validation.apt Thu Nov 15 09:30:35 2007
@@ -237,5 +237,65 @@
   This is nice and seamless; the same look and feel and behavior for both the built-in validators, and for errors generated based on
   application logic.
 
-  
-  
\ No newline at end of file
+Overriding the Translator with Events
+
+  The TextField, PasswordField and TextArea components all have a translate parameter, a
+  {{{../../apidocs/org/apache/tapestry/Translator.html}Translator}} object that is used to convert values on the server
+  side to strings on the client side.
+
+  In most cases, the translate parameter is not set explicitly; Tapestry derives an appropriate value
+  based on the type of property being editted by the field.
+
+  In certain cases, you may want to override the translator.  This can be accomplished using two events
+  triggered on the component, "toclient" and "parseclient".
+
+  The "toclient" event is passed the current object value and returns a string, which will be the default
+  value for the field. When there is no event handler, or when the event handler returns null, the default
+  Translator is used to convert the server side value to a string.
+
+  For example, you may have a quantity field that you wish to display as blank, rather than zero,
+  initially:
+
++---+
+  <t:textfield t:id="quantity" size="10"/>
+
+  . . .
+
+  private int _quantity;
+
+  String onToClientFromQuantity()
+  {
+    if (_quantity == 0) return "";
+
+    return null;
+  }
++---+
+
+  This is good so far, but if the field is optional and the user submits the form,
+  you'll get a validation error, because the empty string is not valid as an integer.
+
+  That's where the "parseclient" event comes in:
+
++---+
+  Object onParseClientFromQuantity(String input)
+  {
+    if ("".equals(input)) return 0;
+
+    return null;
+  }
++---+
+
+  The event handler has precendence over the translator.  Here it checks for the empty string
+  (and note that the input may be null!) and evaluates that as zero.
+
+  Again, returning null lets the normal translator do its work.
+
+  The event handler may also throw
+  {{{../../apidocs/org/apache/tapestry/ValidationException.html}ValidationException}} to indicate a value
+  that can't be parsed.
+
+  <<Caution:>> These two events are exclusively on the <server side>. Client side validations, in particular,
+  will not have access to the "parseclient" event.  This means that, in certain circumstances,
+  an input value will be rejected on the client side even though it is valid on the server side.
+
+  <Coming soon: an additional event for validating the parsed value.>
\ No newline at end of file

Modified: tapestry/tapestry5/trunk/tapestry-core/src/site/apt/index.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/site/apt/index.apt?rev=595372&r1=595371&r2=595372&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/site/apt/index.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/site/apt/index.apt Thu Nov 15 09:30:35 2007
@@ -12,7 +12,10 @@
 
   Progress on Tapestry 5 is really taking off. This space lists some cool new features that have been added
   recently.
-  
+
+  * You may now customize how the TextField, PasswordField and TextArea component converts between server side values
+    and client side strings, using specialized events.
+
   * We finally have a DateField component, built on top of the {{{http://www.dynarch.com/projects/calendar/}dynarch.com DHTML/JavaScript Calendar}}.
   
   * The Grid component may now be used inside a Form.

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/app1/EventMethodTranslate.tml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/app1/EventMethodTranslate.tml?rev=595372&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/app1/EventMethodTranslate.tml (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/app1/EventMethodTranslate.tml Thu Nov 15 09:30:35 2007
@@ -0,0 +1,28 @@
+<html t:type="Border" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+    <h1>Event Handler Method Translate</h1>
+
+    <t:form>
+
+        <t:errors/>
+
+        <div class="t-beaneditor">
+
+            <div class="t-beaneditor-row">
+                <t:label for="count"/>
+                <t:textfield t:id="count"/>
+
+            </div>
+
+            <div class="t-beaneditor-row">
+                <input type="submit" value="Update"/>
+            </div>
+        </div>
+    </t:form>
+
+    <hr/>
+
+    <p>
+        Count: [${count}]
+    </p>
+
+</html>

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/app1/Start.tml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/app1/Start.tml?rev=595372&r1=595371&r2=595372&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/app1/Start.tml (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/app1/Start.tml Thu Nov 15 09:30:35 2007
@@ -1,280 +1,285 @@
 <html t:type="Border"
-  xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+      xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
 
-  <h1>Tapestry 5 Integration Application 1</h1>
+    <h1>Tapestry 5 Integration Application 1</h1>
 
-  <table>
-    <tr>
-      <td>
-        <ul>
-          <li>
-            <a t:type="PageLink" page="MerryChristmas">Count Page</a>
-          </li>
-          <li>
-            <a t:type="PageLink" page="InjectDemo">Inject Demo</a>
-          </li>
-          <li>
-            <a t:type="PageLink" page="Countdown">Countdown Page</a>
-          </li>
-          <li>
-            <a t:type="PageLink" page="ParameterConflict">
-              Template Overridden by Class Page
-            </a>
-          </li>
-          <li>
-            <a t:type="PageLink" page="EnvironmentalDemo">
-              Environmental Annotation Usage
-            </a>
-          </li>
-          <li>
-            <a t:type="PageLink" page="expansion">Expansion Page</a>
-          </li>
-          <li>
-            <a href="BadTemplate">BadTemplate Page</a>
-            -- More exception reporting
-          </li>
-          <li>
-            <a t:type="PageLink" page="ActionPage">Action Page</a>
-            -- tests fixture for ActionLink component
-          </li>
-          <li>
-            <a t:type="PageLink" page="InstanceMixin">InstanceMixin</a>
-            -- Mixin added to particular component instance
-          </li>
-          <li>
-            <a t:type="PageLink" page="RenderPhaseOrder">
-              RenderPhaseOrder
-            </a>
-            -- Order of operations when invoking render phase methods
-          </li>
-          <li>
-            <a t:type="PageLink" page="SimpleForm">SimpleForm</a>
-            -- first pass at writing Form and TextField components
-          </li>
-          <li>
-            <a t:type="PageLink" page="NumberSelect">NumberSelect</a>
-            -- passivate/activate page context demo
-          </li>
-          <li>
-            <a t:type="PageLink" page="Localization">Localization</a>
-            -- accessing localized messages from the component catalog
-          </li>
-          <li>
-            <a t:type="PageLink" page="AssetDemo">AssetDemo</a>
-            -- declaring an using Assets
-          </li>
-          <li>
-            <a t:type="PageLink" page="ExpansionSubclass">
-              ExpansionSubclass
-            </a>
-            -- components can inherit templates from base classes
-          </li>
-          <li>
-            <a href="InjectContainerMismatch">
-              InjectContainerMismatch
-            </a>
-            -- check error reporting when @InjectContainer doesn't match
-            the actual field type
-          </li>
+    <table>
+        <tr>
+            <td>
+                <ul>
+                    <li>
+                        <a t:type="PageLink" page="MerryChristmas">Count Page</a>
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="InjectDemo">Inject Demo</a>
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="Countdown">Countdown Page</a>
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="ParameterConflict">
+                            Template Overridden by Class Page
+                        </a>
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="EnvironmentalDemo">
+                            Environmental Annotation Usage
+                        </a>
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="expansion">Expansion Page</a>
+                    </li>
+                    <li>
+                        <a href="BadTemplate">BadTemplate Page</a>
+                        -- More exception reporting
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="ActionPage">Action Page</a>
+                        -- tests fixture for ActionLink component
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="InstanceMixin">InstanceMixin</a>
+                        -- Mixin added to particular component instance
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="RenderPhaseOrder">
+                            RenderPhaseOrder
+                        </a>
+                        -- Order of operations when invoking render phase methods
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="SimpleForm">SimpleForm</a>
+                        -- first pass at writing Form and TextField components
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="NumberSelect">NumberSelect</a>
+                        -- passivate/activate page context demo
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="Localization">Localization</a>
+                        -- accessing localized messages from the component catalog
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="AssetDemo">AssetDemo</a>
+                        -- declaring an using Assets
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="ExpansionSubclass">
+                            ExpansionSubclass
+                        </a>
+                        -- components can inherit templates from base classes
+                    </li>
+                    <li>
+                        <a href="InjectContainerMismatch">
+                            InjectContainerMismatch
+                        </a>
+                        -- check error reporting when @InjectContainer doesn't match
+                        the actual field type
+                    </li>
 
-          <li>
-            <a t:type="PageLink" page="ParameterDefault">
-              ParameterDefault
-            </a>
-            -- defaulter methods for component parameters
-          </li>
-          <li>
-            <a t:type="PageLink" page="ValidForm">ValidForm</a>
-            -- server-side input validation
-          </li>
-          <li>
-            <a t:type="PageLink" page="PasswordFieldDemo">
-              PasswordFieldDemo
-            </a>
-            -- test for the PasswordField component
-          </li>
-          <li>
-            <a t:type="PageLink" page="RenderComponentDemo">
-              RenderComponentDemo
-            </a>
-            -- components that "nominate" other components to render
-          </li>
-          <li>
-            <a t:type="PageLink" page="BlockDemo">BlockDemo</a>
-            -- use of blocks to control rendering
-          </li>
-          <li>
-            <a t:type="PageLink" page="ToDoListVolatile">
-              ToDo List (Volatile)
-            </a>
-            -- Loops and Submit inside Form, volatile mode
-          </li>
-          <li>
-            <t:pagelink page="MissingTemplate">
-              Missing Template Demo
-            </t:pagelink>
-            -- Demo for what happens when a template is not found for a
-            page.
-          </li>
-        </ul>
-      </td>
-      <td>
-        <ul>
+                    <li>
+                        <a t:type="PageLink" page="ParameterDefault">
+                            ParameterDefault
+                        </a>
+                        -- defaulter methods for component parameters
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="ValidForm">ValidForm</a>
+                        -- server-side input validation
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="PasswordFieldDemo">
+                            PasswordFieldDemo
+                        </a>
+                        -- test for the PasswordField component
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="RenderComponentDemo">
+                            RenderComponentDemo
+                        </a>
+                        -- components that "nominate" other components to render
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="BlockDemo">BlockDemo</a>
+                        -- use of blocks to control rendering
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="ToDoListVolatile">
+                            ToDo List (Volatile)
+                        </a>
+                        -- Loops and Submit inside Form, volatile mode
+                    </li>
+                    <li>
+                        <t:pagelink page="MissingTemplate">
+                            Missing Template Demo
+                        </t:pagelink>
+                        -- Demo for what happens when a template is not found for a
+                        page.
+                    </li>
+                </ul>
+            </td>
+            <td>
+                <ul>
 
-          <li>
-            <a t:type="PageLink" page="ToDoList">ToDo List</a>
-            -- Loops and Submit inside Form using a primary key encoder
-          </li>
-          <li>
-            <a t:type="PageLink" page="FlashDemo">FlashDemo</a>
-            -- demonstrate "flash" persistence
-          </li>
-          <li>
-            <a t:type="PageLink" page="beaneditordemo">
-              BeanEditor Demo
-            </a>
-            -- demonstrate the BeanEditor mega-component
-          </li>
-          <li>
-            <a t:type="PageLink" page="pageloadeddemo">
-              PageLoaded Demo
-            </a>
-            -- shows that page lifecycle methods are invoked
-          </li>
-          <li>
-            <a t:type="PageLink" page="griddemo">Grid Demo</a>
-            -- default Grid component
-          </li>
-          <li>
-            <a t:type="PageLink" page="nullgrid">Null Grid</a>
-            -- handling of null source for Grid
-          </li>
-          <li>
-            <a t:type="PageLink" page="gridenumdemo">Grid Enum Demo</a>
-            -- handling of enum types in the Grid
-          </li>
-          <li>
-            <t:pageLink page="GridRemoveReorderDemo">
-              Grid Remove/Reorder Demo
-            </t:pageLink>
-            -- handling of remove and reorder parameters
-          </li>
-          <li>
-            <a t:type="PageLink" page="protected">Protected Page</a>
-            -- Demonstrate result of non-void return from a page's
-            activate method.
-          </li>
-          <li>
-            <a t:type="PageLink" page="kicker">Kicker</a>
-            -- demos complex page and component context in links
-          </li>
-          <li>
-            <a t:type="PageLink" page="simpletrackgriddemo">
-              SimpleTrack Grid Demo
-            </a>
-            -- customizing the model for a Grid around an interface
-          </li>
-          <li>
-            <a t:type="PageLink" page="pagelinkcontext">
-              PageLink Context Demo
-            </a>
-            -- passing explicit context in a page render link
-          </li>
-          <li>
-            <a t:type="pagelink" page="ValidBeanEditorDemo">
-              Client Validation Demo
-            </a>
-            --BeanEditor with validation enabled
-          </li>
-          <li>
-            <a href="recursivedemo">Recursive Demo</a>
-            -- check for handling of recursive components
-          </li>
-          <li>
-            <t:pagelink page="renderabledemo">
-              Renderable Demo
-            </t:pagelink>
-            -- Shows that render phase methods can return a Renderable
-            object
-          </li>
-          <li>
-            <t:pagelink page="eventhandlerdemo" context="'clear'">
-              EventHandler Demo
-            </t:pagelink>
-            -- Tests for event handling method order and matching
-          </li>
-          <li>
-            <t:pagelink page="inheritedbindingsdemo">
-              Inherited Bindings Demo
-            </t:pagelink>
-            -- Tests for components that inherit bindings from
-            containing components
-          </li>
-          <li>
-            <t:pagelink page="ClientPersistenceDemo">
-              Client Persistence Demo
-            </t:pagelink>
-            -- component field values persisted on the client side
-          </li>
-          <li>
-            <t:pagelink page="attributeExpansionsDemo">
-              Attribute Expansions Demo
-            </t:pagelink>
-            -- use expansions inside attributes of ordinary elements
-          </li>
-          <li>
-            <t:pagelink page="PaletteDemo">Palette Demo</t:pagelink>
-            -- multiple selection component
-          </li>
-          <li>
-            <t:pagelink page="ReturnTypes">Return Types</t:pagelink>
-            -- Tests various event handler return types
-          </li>
-          <li>
-            <t:pagelink page="FormEncodingType">
-              Form Encoding Type
-            </t:pagelink>
-            -- Test ability to set an encoding type for a Form
-          </li>
-          <li>
-            <t:pagelink page="RadioDemo">RadioDemo</t:pagelink>
-            -- Use of the RadioGroup and Radio components.
-          </li>
-          <li>
-            <t:pagelink page="RegexpDemo">Regexp Demo</t:pagelink>
-            -- Use of the Regexp validator
-          </li>
-          <li>
-            <t:pagelink page="BeanEditRemoveReorder">
-              BeanEdit Remove/Reorder
-            </t:pagelink>
-            -- Use of the remove and reorder parameters with
-            BeanEditForm
-          </li>
-          <li>
-            <t:pagelink page="MultiBeanEditDemo">
-              MultiBeanEdit Demo
-            </t:pagelink>
-            -- Multiple BeanEditor components in a single form
-          </li>
-          <li>
-            <t:pagelink page="GridFormDemo">Grid Form Demo</t:pagelink>
-            -- Grid operating inside a Form
-          </li>
-          <li>
-            <t:pagelink page="DateFieldDemo">DateField Demo</t:pagelink>
-            -- using DateField by itself on a page
-          </li>
-          <li>
-            <t:pagelink page="BeanEditDateDemo">
-              BeanEditor / Date Demo
-            </t:pagelink>
-            -- Use of date properties inside BeanEditor and BeanDisplay
-          </li>
+                    <li>
+                        <a t:type="PageLink" page="ToDoList">ToDo List</a>
+                        -- Loops and Submit inside Form using a primary key encoder
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="FlashDemo">FlashDemo</a>
+                        -- demonstrate "flash" persistence
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="beaneditordemo">
+                            BeanEditor Demo
+                        </a>
+                        -- demonstrate the BeanEditor mega-component
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="pageloadeddemo">
+                            PageLoaded Demo
+                        </a>
+                        -- shows that page lifecycle methods are invoked
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="griddemo">Grid Demo</a>
+                        -- default Grid component
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="nullgrid">Null Grid</a>
+                        -- handling of null source for Grid
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="gridenumdemo">Grid Enum Demo</a>
+                        -- handling of enum types in the Grid
+                    </li>
+                    <li>
+                        <t:pageLink page="GridRemoveReorderDemo">
+                            Grid Remove/Reorder Demo
+                        </t:pageLink>
+                        -- handling of remove and reorder parameters
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="protected">Protected Page</a>
+                        -- Demonstrate result of non-void return from a page's
+                        activate method.
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="kicker">Kicker</a>
+                        -- demos complex page and component context in links
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="simpletrackgriddemo">
+                            SimpleTrack Grid Demo
+                        </a>
+                        -- customizing the model for a Grid around an interface
+                    </li>
+                    <li>
+                        <a t:type="PageLink" page="pagelinkcontext">
+                            PageLink Context Demo
+                        </a>
+                        -- passing explicit context in a page render link
+                    </li>
+                    <li>
+                        <a t:type="pagelink" page="ValidBeanEditorDemo">
+                            Client Validation Demo
+                        </a>
+                        --BeanEditor with validation enabled
+                    </li>
+                    <li>
+                        <a href="recursivedemo">Recursive Demo</a>
+                        -- check for handling of recursive components
+                    </li>
+                    <li>
+                        <t:pagelink page="renderabledemo">
+                            Renderable Demo
+                        </t:pagelink>
+                        -- Shows that render phase methods can return a Renderable
+                        object
+                    </li>
+                    <li>
+                        <t:pagelink page="eventhandlerdemo" context="'clear'">
+                            EventHandler Demo
+                        </t:pagelink>
+                        -- Tests for event handling method order and matching
+                    </li>
+                    <li>
+                        <t:pagelink page="inheritedbindingsdemo">
+                            Inherited Bindings Demo
+                        </t:pagelink>
+                        -- Tests for components that inherit bindings from
+                        containing components
+                    </li>
+                    <li>
+                        <t:pagelink page="ClientPersistenceDemo">
+                            Client Persistence Demo
+                        </t:pagelink>
+                        -- component field values persisted on the client side
+                    </li>
+                    <li>
+                        <t:pagelink page="attributeExpansionsDemo">
+                            Attribute Expansions Demo
+                        </t:pagelink>
+                        -- use expansions inside attributes of ordinary elements
+                    </li>
+                    <li>
+                        <t:pagelink page="PaletteDemo">Palette Demo</t:pagelink>
+                        -- multiple selection component
+                    </li>
+                    <li>
+                        <t:pagelink page="ReturnTypes">Return Types</t:pagelink>
+                        -- Tests various event handler return types
+                    </li>
+                    <li>
+                        <t:pagelink page="FormEncodingType">
+                            Form Encoding Type
+                        </t:pagelink>
+                        -- Test ability to set an encoding type for a Form
+                    </li>
+                    <li>
+                        <t:pagelink page="RadioDemo">RadioDemo</t:pagelink>
+                        -- Use of the RadioGroup and Radio components.
+                    </li>
+                    <li>
+                        <t:pagelink page="RegexpDemo">Regexp Demo</t:pagelink>
+                        -- Use of the Regexp validator
+                    </li>
+                    <li>
+                        <t:pagelink page="BeanEditRemoveReorder">
+                            BeanEdit Remove/Reorder
+                        </t:pagelink>
+                        -- Use of the remove and reorder parameters with
+                        BeanEditForm
+                    </li>
+                    <li>
+                        <t:pagelink page="MultiBeanEditDemo">
+                            MultiBeanEdit Demo
+                        </t:pagelink>
+                        -- Multiple BeanEditor components in a single form
+                    </li>
+                    <li>
+                        <t:pagelink page="GridFormDemo">Grid Form Demo</t:pagelink>
+                        -- Grid operating inside a Form
+                    </li>
+                    <li>
+                        <t:pagelink page="DateFieldDemo">DateField Demo</t:pagelink>
+                        -- using DateField by itself on a page
+                    </li>
+                    <li>
+                        <t:pagelink page="BeanEditDateDemo">
+                            BeanEditor / Date Demo
+                        </t:pagelink>
+                        -- Use of date properties inside BeanEditor and BeanDisplay
+                    </li>
 
-        </ul>
-      </td>
-    </tr>
-  </table>
+                    <li>
+                        <t:pagelink page="eventmethodtranslate">EventMethod Translator</t:pagelink>
+                        -- Demo ability to provide toclient and parseclient event handler methods
+                    </li>
+
+                </ul>
+            </td>
+        </tr>
+    </table>
 
 </html>

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/corelib/internal/ComponentTranslatorWrapperTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/corelib/internal/ComponentTranslatorWrapperTest.java?rev=595372&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/corelib/internal/ComponentTranslatorWrapperTest.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/corelib/internal/ComponentTranslatorWrapperTest.java Thu Nov 15 09:30:35 2007
@@ -0,0 +1,270 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.corelib.internal;
+
+import org.apache.tapestry.ComponentEventHandler;
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.Translator;
+import org.apache.tapestry.ValidationException;
+import org.apache.tapestry.ioc.Messages;
+import org.apache.tapestry.runtime.ComponentEventException;
+import org.apache.tapestry.test.TapestryTestCase;
+import org.easymock.EasyMock;
+import org.easymock.IAnswer;
+import org.testng.annotations.Test;
+
+public class ComponentTranslatorWrapperTest extends TapestryTestCase
+{
+    @SuppressWarnings({"unchecked"})
+    @Test
+    public void parse_client_via_event() throws ValidationException
+    {
+        Messages messages = mockMessages();
+        ComponentResources resources = mockComponentResources();
+        Translator translator = mockTranslator();
+
+        String clientValue = "abracadabra";
+
+        IAnswer answer = new IAnswer()
+        {
+            @SuppressWarnings({"unchecked"})
+            public Object answer() throws Throwable
+            {
+                Object[] args = EasyMock.getCurrentArguments();
+                Object[] context = (Object[]) args[1];
+                ComponentEventHandler handler = (ComponentEventHandler) args[2];
+
+                // Pretend that the parser event handler converted it to upper case.
+
+                return handler.handleResult(context[0].toString().toUpperCase(), null, null);
+            }
+        };
+
+        EasyMock.expect(resources.triggerEvent(EasyMock.eq(ComponentTranslatorWrapper.PARSE_CLIENT_EVENT),
+                                               EasyMock.isA(Object[].class),
+                                               EasyMock.isA(ComponentEventHandler.class))).andAnswer(answer);
+
+
+        replay();
+
+        Object actual = new ComponentTranslatorWrapper(resources, translator).parseClient(clientValue, messages);
+
+        assertEquals(actual, clientValue.toUpperCase());
+
+        verify();
+    }
+
+    @SuppressWarnings({"ThrowableInstanceNeverThrown"})
+    @Test
+    public void parse_client_event_handler_throws_validation_exception() throws Exception
+    {
+        Messages messages = mockMessages();
+        ComponentResources resources = mockComponentResources();
+        Translator translator = mockTranslator();
+        ValidationException ve = new ValidationException("Just didn't feel right.");
+
+        String clientValue = "abracadabra";
+
+
+        EasyMock.expect(resources.triggerEvent(EasyMock.eq(ComponentTranslatorWrapper.PARSE_CLIENT_EVENT),
+                                               EasyMock.isA(Object[].class),
+                                               EasyMock.isA(ComponentEventHandler.class))).andThrow(
+                new ComponentEventException(ve.getMessage(), null, ve));
+
+
+        replay();
+
+        try
+        {
+            new ComponentTranslatorWrapper(resources, translator).parseClient(clientValue, messages);
+
+            unreachable();
+        }
+        catch (ValidationException ex)
+        {
+            assertSame(ex, ve);
+        }
+
+
+        verify();
+    }
+
+    @SuppressWarnings({"ThrowableInstanceNeverThrown"})
+    @Test
+    public void parse_client_event_handler_fails_with_other_exception() throws Exception
+    {
+        Messages messages = mockMessages();
+        ComponentResources resources = mockComponentResources();
+        Translator translator = mockTranslator();
+        RuntimeException re = new RuntimeException("Just didn't feel right.");
+        ComponentEventException cee = new ComponentEventException(re.getMessage(), null, re);
+
+        String clientValue = "abracadabra";
+
+
+        EasyMock.expect(resources.triggerEvent(EasyMock.eq(ComponentTranslatorWrapper.PARSE_CLIENT_EVENT),
+                                               EasyMock.isA(Object[].class),
+                                               EasyMock.isA(ComponentEventHandler.class))).andThrow(cee);
+
+
+        replay();
+
+        try
+        {
+            new ComponentTranslatorWrapper(resources, translator).parseClient(clientValue, messages);
+
+            unreachable();
+        }
+        catch (ComponentEventException ex)
+        {
+            assertSame(ex, cee);
+            assertSame(ex.getCause(), re);
+        }
+
+
+        verify();
+    }
+
+    @Test
+    public void parse_client_via_translator() throws ValidationException
+    {
+        Messages messages = mockMessages();
+        ComponentResources resources = mockComponentResources();
+        Translator translator = mockTranslator();
+
+        String clientValue = "abracadabra";
+
+
+        EasyMock.expect(resources.triggerEvent(EasyMock.eq(ComponentTranslatorWrapper.PARSE_CLIENT_EVENT),
+                                               EasyMock.isA(Object[].class),
+                                               EasyMock.isA(ComponentEventHandler.class))).andReturn(false);
+
+        expect(translator.parseClient(clientValue, messages)).andReturn("foobar");
+
+        replay();
+
+        Object actual = new ComponentTranslatorWrapper(resources, translator).parseClient(clientValue, messages);
+
+        assertEquals(actual, "foobar");
+
+        verify();
+    }
+
+    @SuppressWarnings({"unchecked"})
+    @Test
+    public void to_client_via_translator()
+    {
+        Object value = new Object();
+        ComponentResources resources = mockComponentResources();
+        Translator translator = mockTranslator();
+
+        String clientValue = "abracadabra";
+
+        EasyMock.expect(resources.triggerEvent(EasyMock.eq(ComponentTranslatorWrapper.TO_CLIENT_EVENT),
+                                               EasyMock.aryEq(new Object[]{value}),
+                                               EasyMock.isA(ComponentEventHandler.class))).andReturn(false);
+
+        expect(translator.toClient(value)).andReturn(clientValue);
+
+        replay();
+
+        Translator t = new ComponentTranslatorWrapper(resources, translator);
+
+        assertEquals(t.toClient(value), clientValue);
+
+        verify();
+    }
+
+    @SuppressWarnings({"unchecked"})
+    @Test
+    public void to_client_via_event_handler() throws Exception
+    {
+        Object value = new Object();
+        ComponentResources resources = mockComponentResources();
+        Translator translator = mockTranslator();
+
+        final String clientValue = "abracadabra";
+
+        IAnswer answer = new IAnswer()
+        {
+            @SuppressWarnings({"unchecked"})
+            public Object answer() throws Throwable
+            {
+                Object[] args = EasyMock.getCurrentArguments();
+                ComponentEventHandler handler = (ComponentEventHandler) args[2];
+
+                return handler.handleResult(clientValue, null, null);
+            }
+        };
+
+        EasyMock.expect(resources.triggerEvent(EasyMock.eq(ComponentTranslatorWrapper.TO_CLIENT_EVENT),
+                                               EasyMock.aryEq(new Object[]{value}),
+                                               EasyMock.isA(ComponentEventHandler.class))).andAnswer(answer);
+
+
+        replay();
+
+        Translator t = new ComponentTranslatorWrapper(resources, translator);
+
+        assertEquals(t.toClient(value), clientValue);
+
+        verify();
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public void to_client_via_event_handler_returns_non_string() throws Exception
+    {
+        Object value = new Object();
+        ComponentResources resources = mockComponentResources();
+        Translator translator = mockTranslator();
+
+
+        IAnswer answer = new IAnswer()
+        {
+            @SuppressWarnings({"unchecked"})
+            public Object answer() throws Throwable
+            {
+                Object[] args = EasyMock.getCurrentArguments();
+                ComponentEventHandler handler = (ComponentEventHandler) args[2];
+
+                // Return an innappropriate value.
+
+                return handler.handleResult(this, null, null);
+            }
+        };
+
+        EasyMock.expect(resources.triggerEvent(EasyMock.eq(ComponentTranslatorWrapper.TO_CLIENT_EVENT),
+                                               EasyMock.aryEq(new Object[]{value}),
+                                               EasyMock.isA(ComponentEventHandler.class))).andAnswer(answer);
+
+
+        replay();
+
+        Translator t = new ComponentTranslatorWrapper(resources, translator);
+
+        try
+        {
+            t.toClient(value);
+            unreachable();
+        }
+        catch (RuntimeException ex)
+        {
+            assertEquals(ex.getMessage(), InternalMessages.toClientShouldReturnString());
+        }
+
+        verify();
+    }
+
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java?rev=595372&r1=595371&r2=595372&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java Thu Nov 15 09:30:35 2007
@@ -30,8 +30,7 @@
  * Note: If these tests fail with BindException when starting Jetty, it could be Skype. At least on
  * my system, Skype is listening on localhost:80.
  */
-@Test(timeOut = 50000, sequential = true, groups =
-        {"integration"})
+@Test(timeOut = 50000, sequential = true, groups = {"integration"})
 public class IntegrationTests extends AbstractIntegrationTestSuite
 {
     public IntegrationTests()
@@ -141,11 +140,10 @@
     {
         start("BadTemplate Page");
 
-        assertTextPresent(
-                "org.apache.tapestry.ioc.internal.util.TapestryException",
-                "Failure parsing template classpath:org/apache/tapestry/integration/app1/pages/BadTemplate.tml, line 7, column 15",
-                "<t:foobar>content from template</t:foobar>",
-                "Element <t:foobar> is in the Tapestry namespace, but is not a recognized Tapestry template element.");
+        assertTextPresent("org.apache.tapestry.ioc.internal.util.TapestryException",
+                          "Failure parsing template classpath:org/apache/tapestry/integration/app1/pages/BadTemplate.tml, line 7, column 15",
+                          "<t:foobar>content from template</t:foobar>",
+                          "Element <t:foobar> is in the Tapestry namespace, but is not a recognized Tapestry template element.");
     }
 
     @Test
@@ -196,8 +194,7 @@
     {
         start("InstanceMixin");
 
-        final String[] dates =
-                {"Jun 13, 1999", "Jul 15, 2001", "Dec 4, 2005"};
+        final String[] dates = {"Jun 13, 1999", "Jul 15, 2001", "Dec 4, 2005"};
 
         for (String date : dates)
         {
@@ -414,12 +411,7 @@
 
         // Tried to use "email:" and "exact:email:" but Selenium 0.8.1 doesn't seem to accept that.
 
-        assertTextPresent(
-                "[foo@bar.baz]",
-                "[Message for you, sir!]",
-                "[false]",
-                "[winnt]",
-                "[RESEARCH_AND_DESIGN]");
+        assertTextPresent("[foo@bar.baz]", "[Message for you, sir!]", "[false]", "[winnt]", "[RESEARCH_AND_DESIGN]");
 
         // Haven't figured out how to get selenium to check that fields are disabled.
     }
@@ -530,11 +522,8 @@
         start("BeanEditor Demo", "Clear Data");
         clickAndWait(SUBMIT);
 
-        assertTextPresent(
-                "(First Name is Required)",
-                "You must provide a value for First Name.",
-                "Everyone has to have a last name!",
-                "Year of Birth requires a value of at least 1900.");
+        assertTextPresent("(First Name is Required)", "You must provide a value for First Name.",
+                          "Everyone has to have a last name!", "Year of Birth requires a value of at least 1900.");
 
         // Part of the override for the firstName property
 
@@ -552,10 +541,9 @@
 
         clickAndWait(SUBMIT);
 
-        assertTextPresent(
-                "You must provide at least 3 characters for First Name.",
-                "You must provide at least 5 characters for Last Name.",
-                "You must provide a value for Year of Birth.");
+        assertTextPresent("You must provide at least 3 characters for First Name.",
+                          "You must provide at least 5 characters for Last Name.",
+                          "You must provide a value for Year of Birth.");
 
         type("firstName", "Howard");
         type("lastName", "Lewis Ship");
@@ -605,15 +593,7 @@
 
         // Strange: I thought tr[1] was the header row ???
 
-        assertTextSeries(
-                "//tr[1]/td[%d]",
-                1,
-                "Bug Juice",
-                "Late Lounge (2 of 2)",
-                "45 Dip",
-                "Electronica",
-                "4",
-                "-");
+        assertTextSeries("//tr[1]/td[%d]", 1, "Bug Juice", "Late Lounge (2 of 2)", "45 Dip", "Electronica", "4", "-");
 
         // Here were checking that the page splits are correct
 
@@ -630,15 +610,8 @@
 
         // Here's one with a customized rating cell
 
-        assertTextSeries(
-                "//tr[25]/td[%d]",
-                1,
-                "Smoked",
-                "London (Original Motion Picture Soundtrack)",
-                "The Crystal Method",
-                "Soundtrack",
-                "30",
-                "****");
+        assertTextSeries("//tr[25]/td[%d]", 1, "Smoked", "London (Original Motion Picture Soundtrack)",
+                         "The Crystal Method", "Soundtrack", "30", "****");
 
         clickAndWait("link=69");
 
@@ -648,37 +621,24 @@
 
         clickAndWait("link=Rating");
 
-        assertText(
-                "//img[@id='rating:sort']/@src",
-                "/assets/tapestry/corelib/components/sort-asc.png");
+        assertText("//img[@id='rating:sort']/@src", "/assets/tapestry/corelib/components/sort-asc.png");
         assertText("//img[@id='rating:sort']/@alt", "[Asc]");
 
-        assertTextSeries(
-                "//tr[22]/td[%d]",
-                1,
-                "Mona Lisa Overdrive",
-                "Labyrinth",
-                "Juno Reactor",
-                "Dance",
-                "31",
-                "*****");
+        assertTextSeries("//tr[22]/td[%d]", 1, "Mona Lisa Overdrive", "Labyrinth", "Juno Reactor", "Dance", "31",
+                         "*****");
 
         // Toggle to sort descending
 
         clickAndWait("link=Rating");
 
-        assertText(
-                "//img[@id='rating:sort']/@src",
-                "/assets/tapestry/corelib/components/sort-desc.png");
+        assertText("//img[@id='rating:sort']/@src", "/assets/tapestry/corelib/components/sort-desc.png");
         assertText("//img[@id='rating:sort']/@alt", "[Desc]");
 
         assertTextSeries("//tr[1]/td[%d]", 1, "Hey Blondie", "Out from Out Where");
 
         clickAndWait("link=Title");
 
-        assertText(
-                "//img[@id='title:sort']/@src",
-                "/assets/tapestry/corelib/components/sort-asc.png");
+        assertText("//img[@id='title:sort']/@src", "/assets/tapestry/corelib/components/sort-asc.png");
         assertText("//img[@id='title:sort']/@alt", "[Asc]");
 
         clickAndWait("link=1");
@@ -789,11 +749,8 @@
     {
         start("Client Validation Demo");
 
-        assertTextSeries(
-                "//script[%d]/@src",
-                1,
-                "/assets/scriptaculous/prototype.js",
-                "/assets/scriptaculous/scriptaculous.js");
+        assertTextSeries("//script[%d]/@src", 1, "/assets/scriptaculous/prototype.js",
+                         "/assets/scriptaculous/scriptaculous.js");
 
         clickAndWait("link=Clear Data");
 
@@ -801,12 +758,8 @@
 
         click(SUBMIT);
 
-        assertTextSeries(
-                "//li[%d]",
-                1,
-                "You must provide a value for First Name.",
-                "Everyone has to have a last name!",
-                "Year of Birth requires a value of at least 1900.");
+        assertTextSeries("//li[%d]", 1, "You must provide a value for First Name.", "Everyone has to have a last name!",
+                         "Year of Birth requires a value of at least 1900.");
 
         type("firstName", "Howard");
         type("lastName", "Lewis Ship");
@@ -828,10 +781,9 @@
     {
         start("Recursive Demo");
 
-        assertTextPresent(
-                "An unexpected application exception has occurred.",
-                "The template for component org.apache.tapestry.integration.app1.components.Recursive is recursive (contains another direct or indirect reference to component org.apache.tapestry.integration.app1.components.Recursive). is not supported (components may not contain themselves).",
-                "component is <t:recursive>recursive</t:recursive>, so we\'ll see a failure.");
+        assertTextPresent("An unexpected application exception has occurred.",
+                          "The template for component org.apache.tapestry.integration.app1.components.Recursive is recursive (contains another direct or indirect reference to component org.apache.tapestry.integration.app1.components.Recursive). is not supported (components may not contain themselves).",
+                          "component is <t:recursive>recursive</t:recursive>, so we\'ll see a failure.");
     }
 
     @Test
@@ -878,9 +830,7 @@
     {
         start("Inherited Bindings Demo");
 
-        assertTextPresent(
-                "Bound: [ value: the-bound-value, bound: true ]",
-                "Unbound: [ value: null, bound: false ]");
+        assertTextPresent("Bound: [ value: the-bound-value, bound: true ]", "Unbound: [ value: null, bound: false ]");
     }
 
     @Test
@@ -904,9 +854,7 @@
         assertText("//div[@id='single']/@class", "red");
         assertText("//div[@id='consecutive']/@class", "goober-red");
         assertText("//div[@id='trailer']/@class", "goober-green");
-        assertText(
-                "//div[@id='formal']/text()",
-                "ALERT-expansions work inside formal component parameters as well");
+        assertText("//div[@id='formal']/text()", "ALERT-expansions work inside formal component parameters as well");
 
         // An unrelated test, but fills in a bunch of minor gaps.
 
@@ -1001,9 +949,8 @@
         waitForPageToLoad();
 
         clickAndWait("link=bad");
-        assertTextPresent(
-                "An unexpected application exception has occurred.",
-                "An event handler for component org.apache.tapestry.integration.app1.pages.Start returned the value 20 (from method org.apache.tapestry.integration.app1.pages.Start.onActionFromBadReturnType() (at Start.java:34)). Return type java.lang.Integer can not be handled.");
+        assertTextPresent("An unexpected application exception has occurred.",
+                          "An event handler for component org.apache.tapestry.integration.app1.pages.Start returned the value 20 (from method org.apache.tapestry.integration.app1.pages.Start.onActionFromBadReturnType() (at Start.java:34)). Return type java.lang.Integer can not be handled.");
 
     }
 
@@ -1091,13 +1038,8 @@
 
         waitForPageToLoad("30000");
 
-        assertTextSeries(
-                "//li[%d]",
-                1,
-                "First Name: [Howard]",
-                "Last Name: [Lewis Ship]",
-                "Path: [/var/www]",
-                "Role: [GRANT]");
+        assertTextSeries("//li[%d]", 1, "First Name: [Howard]", "Last Name: [Lewis Ship]", "Path: [/var/www]",
+                         "Role: [GRANT]");
     }
 
     @Test
@@ -1177,5 +1119,37 @@
         open(BASE_URL + "servicestatus");
 
         assertTextPresent("Tapestry IoC Services Status");
+    }
+
+    @Test
+    public void event_based_translate() throws Exception
+    {
+        start("EventMethod Translator");
+
+        type("count", "123");
+        clickAndWait(SUBMIT);
+
+        assertTextPresent("Count: [123]");
+
+        type("count", "0");
+        clickAndWait(SUBMIT);
+
+        assertTextPresent("Count: [0]");
+
+        assertFieldValue("count", "zero");
+
+        type("count", "456");
+        clickAndWait(SUBMIT);
+
+        assertTextPresent("Count: [456]");
+
+        assertFieldValue("count", "456");
+
+        type("count", "ZERO");
+        clickAndWait(SUBMIT);
+
+        assertTextPresent("Count: [0]");
+
+        assertFieldValue("count", "zero");
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/NewIntegrationTests.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/NewIntegrationTests.java?rev=595372&r1=595371&r2=595372&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/NewIntegrationTests.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/NewIntegrationTests.java Thu Nov 15 09:30:35 2007
@@ -23,13 +23,13 @@
  * disable all of those when adding a new test, the new test is added and debugged here. Once it is
  * totally ready, it is moved up to IntegrationTests.
  */
-@Test(timeOut = 50000, sequential = true, enabled = false, groups =
-        {"integration"})
+@Test(timeOut = 50000, sequential = true, enabled = false, groups = {"integration"})
 public class NewIntegrationTests extends AbstractIntegrationTestSuite
 {
     public NewIntegrationTests()
     {
         super("src/test/app1");
     }
+
 
 }

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/EventMethodTranslate.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/EventMethodTranslate.java?rev=595372&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/EventMethodTranslate.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/EventMethodTranslate.java Thu Nov 15 09:30:35 2007
@@ -0,0 +1,55 @@
+// Copyright 2007 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.ValidationException;
+import org.apache.tapestry.annotations.Persist;
+
+public class EventMethodTranslate
+{
+    @Persist
+    private int _count;
+
+    public int getCount()
+    {
+        return _count;
+    }
+
+    public void setCount(int count)
+    {
+        _count = count;
+    }
+
+    String onToClientFromCount()
+    {
+        if (_count == 0) return "zero";
+
+        // Get the default behavior
+        return null;
+    }
+
+    Object onParseClientFromCount(String input) throws ValidationException
+    {
+        if (input == null || input.equals("")) return null;
+
+        // And it gets tricky because we probably should trim spaces!
+
+        if (input.equalsIgnoreCase("zero")) return 0;
+
+        // Get the default behavior.
+
+        return null;
+    }
+}

Modified: tapestry/tapestry5/trunk/tapestry5.ipr
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry5.ipr?rev=595372&r1=595371&r2=595372&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry5.ipr (original)
+++ tapestry/tapestry5/trunk/tapestry5.ipr Thu Nov 15 09:30:35 2007
@@ -58,18 +58,7 @@
       <entry name=".+\.(gif|png|jpeg|jpg)" />
     </resourceExtensions>
     <wildcardResourcePatterns>
-      <entry name="?*.properties" />
-      <entry name="?*.xml" />
-      <entry name="?*.html" />
-      <entry name="?*.dtd" />
-      <entry name="?*.tld" />
-      <entry name="?*.gif" />
-      <entry name="?*.png" />
-      <entry name="?*.jpeg" />
-      <entry name="?*.jpg" />
-      <entry name="?*.tml" />
-      <entry name="?*.css" />
-      <entry name="?*.js" />
+      <entry name="!?*.java" />
     </wildcardResourcePatterns>
   </component>
   <component name="DependenciesAnalyzeManager">