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

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

Author: hlship
Date: Sat Nov 17 11:37:20 2007
New Revision: 595993

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

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/FieldValidationSupportImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/FieldValidationSupport.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/FieldValidationSupportImplTest.java
Removed:
    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/corelib/internal/ComponentValidatorWrapper.java
    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/corelib/internal/ComponentValidatorWrapperTest.java
Modified:
    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/Select.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/test/TapestryTestCase.java
    tapestry/tapestry5/trunk/tapestry-upload/src/main/java/org/apache/tapestry/upload/components/Upload.java
    tapestry/tapestry5/trunk/tapestry-upload/src/test/java/org/apache/tapestry/upload/components/UploadTest.java

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=595993&r1=595992&r2=595993&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 Sat Nov 17 11:37:20 2007
@@ -19,9 +19,6 @@
 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.corelib.internal.ComponentValidatorWrapper;
-import org.apache.tapestry.ioc.Messages;
 import org.apache.tapestry.ioc.annotations.Inject;
 import org.apache.tapestry.services.*;
 
@@ -75,9 +72,6 @@
     private ValidationTracker _tracker;
 
     @Inject
-    private ValidationMessagesSource _messagesSource;
-
-    @Inject
     private TranslatorDefaultSource _translatorDefaultSource;
 
     @Inject
@@ -92,6 +86,9 @@
     @Inject
     private Request _request;
 
+    @Inject
+    private FieldValidationSupport _fieldValidationSupport;
+
     /**
      * Computes a default value for the "translate" parameter using {@link TranslatorDefaultSource}.
      */
@@ -137,13 +134,7 @@
     {
         String value = _tracker.getInput(this);
 
-        if (value == null)
-        {
-
-            Translator wrapper = new ComponentTranslatorWrapper(_resources, _translate);
-
-            value = wrapper.toClient(_value);
-        }
+        if (value == null) value = _fieldValidationSupport.toClient(_value, _resources, _translate);
 
         writeFieldTag(writer, value);
 
@@ -178,17 +169,11 @@
 
         _tracker.recordInput(this, rawValue);
 
-        Messages messages = _messagesSource.getValidationMessages(_locale);
-
         try
         {
-            Translator translatorWrapper = new ComponentTranslatorWrapper(_resources, _translate);
-
-            Object translated = translatorWrapper.parseClient(rawValue, messages);
-
-            FieldValidator validatorWrapper = new ComponentValidatorWrapper(_resources, _validate);
+            Object translated = _fieldValidationSupport.parseClient(rawValue, _resources, _translate);
 
-            validatorWrapper.validate(translated);
+            _fieldValidationSupport.validate(translated, _resources, _validate);
 
             _value = translated;
         }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Select.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Select.java?rev=595993&r1=595992&r2=595993&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Select.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Select.java Sat Nov 17 11:37:20 2007
@@ -19,7 +19,6 @@
 import org.apache.tapestry.annotations.Environmental;
 import org.apache.tapestry.annotations.Parameter;
 import org.apache.tapestry.corelib.base.AbstractField;
-import org.apache.tapestry.corelib.internal.ComponentValidatorWrapper;
 import org.apache.tapestry.internal.util.SelectModelRenderer;
 import org.apache.tapestry.ioc.annotations.Inject;
 import org.apache.tapestry.services.*;
@@ -105,6 +104,9 @@
     @Inject
     private ValueEncoderSource _valueEncoderSource;
 
+    @Inject
+    private FieldValidationSupport _fieldValidationSupport;
+
     @SuppressWarnings({"unchecked"})
     @Override
     protected void processSubmission(FormSupport formSupport, String elementName)
@@ -113,11 +115,9 @@
 
         Object selectedValue = _encoder.toValue(primaryKey);
 
-        FieldValidator wrappedValidator = new ComponentValidatorWrapper(_resources, _validate);
-
         try
         {
-            wrappedValidator.validate(selectedValue);
+            _fieldValidationSupport.validate(selectedValue, _resources, _validate);
 
             _value = selectedValue;
         }

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/FieldValidationSupportImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/FieldValidationSupportImpl.java?rev=595993&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/FieldValidationSupportImpl.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/FieldValidationSupportImpl.java Sat Nov 17 11:37:20 2007
@@ -0,0 +1,108 @@
+package org.apache.tapestry.internal.services;
+
+import org.apache.tapestry.*;
+import org.apache.tapestry.corelib.internal.InternalMessages;
+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;
+import org.apache.tapestry.services.FieldValidationSupport;
+import org.apache.tapestry.services.ValidationMessagesSource;
+
+public class FieldValidationSupportImpl implements FieldValidationSupport
+{
+    static final String PARSE_CLIENT_EVENT = "parseClient";
+    static final String TO_CLIENT_EVENT = "toClient";
+    static final String VALIDATE_EVENT = "validate";
+
+    private final ValidationMessagesSource _messagesSource;
+
+    public FieldValidationSupportImpl(ValidationMessagesSource messagesSource)
+    {
+        _messagesSource = messagesSource;
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public String toClient(Object value, ComponentResources componentResources, Translator translator)
+    {
+        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;
+            }
+        };
+
+        componentResources.triggerEvent(TO_CLIENT_EVENT, new Object[]{value}, handler);
+
+        if (resultHolder.hasValue()) return resultHolder.get();
+
+        return translator.toClient(value);
+
+    }
+
+    public Object parseClient(String clientValue, ComponentResources componentResources, Translator translator)
+            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
+        {
+            componentResources.triggerEvent(PARSE_CLIENT_EVENT, new Object[]{clientValue}, handler);
+        }
+        catch (ComponentEventException ex)
+        {
+            ValidationException ve = ex.get(ValidationException.class);
+
+            if (ve != null) throw ve;
+
+            throw ex;
+        }
+
+        if (resultHolder.hasValue()) return resultHolder.get();
+
+        // Otherwise, let the normal translator do the job.
+
+        Messages messages = _messagesSource.getValidationMessages(componentResources.getLocale());
+
+        return translator.parseClient(clientValue, messages);
+    }
+
+    public void validate(Object value, ComponentResources componentResources, FieldValidator validator)
+            throws ValidationException
+    {
+        validator.validate(value);
+
+        try
+        {
+            componentResources.triggerEvent(VALIDATE_EVENT, new Object[]{value}, null);
+        }
+        catch (ComponentEventException ex)
+        {
+            ValidationException ve = ex.get(ValidationException.class);
+
+            if (ve != null) throw ve;
+
+            throw ex;
+        }
+    }
+}

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/FieldValidationSupport.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/FieldValidationSupport.java?rev=595993&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/FieldValidationSupport.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/FieldValidationSupport.java Sat Nov 17 11:37:20 2007
@@ -0,0 +1,52 @@
+package org.apache.tapestry.services;
+
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.FieldValidator;
+import org.apache.tapestry.Translator;
+import org.apache.tapestry.ValidationException;
+
+/**
+ * Services to help with field {@linkplain org.apache.tapestry.Validator validation} and
+ * {@linkplain org.apache.tapestry.Translator translation}. This service encapsulates
+ * the logic that mixes normal configured/declared validation/translation with
+ * events triggered on the component.
+ */
+public interface FieldValidationSupport
+{
+    /**
+     * A wrapper around {@link org.apache.tapestry.Translator#toClient(Object)} that first
+     * fires a "toclient" event on the component to see if it can perform the conversion.
+     *
+     * @param value              to be converted to a client-side string
+     * @param componentResources used to fire events on the component
+     * @param translator         used if the component does not provide a non-null value
+     * @return the translated value
+     */
+    String toClient(Object value, ComponentResources componentResources, Translator translator);
+
+    /**
+     * A wrapper around {@link org.apache.tapestry.Translator#parseClient(String, org.apache.tapestry.ioc.Messages)}.
+     * First a "parseclient" event is fired; the translator is only invoked if that returns null.
+     *
+     * @param clientValue        the value provided by the client (may be null)
+     * @param componentResources used to trigger events
+     * @param translator         translator that will do the work if the component event returns null
+     * @return the input parsed to an object
+     * @throws org.apache.tapestry.ValidationException
+     *          if the value can't be parsed
+     */
+    Object parseClient(String clientValue, ComponentResources componentResources, Translator translator)
+            throws ValidationException;
+
+    /**
+     * Performs validation on a parsed value from the client.  Normal validations occur first,
+     * then a "validate" event is triggered on the component.
+     *
+     * @param value              parsed value from the client
+     * @param componentResources used to trigger events
+     * @param validator          performs normal validations
+     * @throws ValidationException if the value is not valid
+     */
+    void validate(Object value, ComponentResources componentResources, FieldValidator validator)
+            throws ValidationException;
+}
\ No newline at end of file

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java?rev=595993&r1=595992&r2=595993&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java Sat Nov 17 11:37:20 2007
@@ -91,7 +91,7 @@
         binder.bind(BeanBlockSource.class, BeanBlockSourceImpl.class);
         binder.bind(ComponentDefaultProvider.class, ComponentDefaultProviderImpl.class);
         binder.bind(MarkupWriterFactory.class, MarkupWriterFactoryImpl.class);
-
+        binder.bind(FieldValidationSupport.class, FieldValidationSupportImpl.class);
         binder.bind(TemplateParser.class, TemplateParserImpl.class);
         binder.bind(PageResponseRenderer.class, PageResponseRendererImpl.class);
         binder.bind(PageMarkupRenderer.class, PageMarkupRendererImpl.class);
@@ -107,7 +107,6 @@
         binder.bind(ResourceStreamer.class, ResourceStreamerImpl.class);
         binder.bind(ClientPersistentFieldStorage.class, ClientPersistentFieldStorageImpl.class);
         binder.bind(RequestEncodingInitializer.class, RequestEncodingInitializerImpl.class);
-
     }
 
     public static Alias build(Logger logger,

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/test/TapestryTestCase.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/test/TapestryTestCase.java?rev=595993&r1=595992&r2=595993&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/test/TapestryTestCase.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/test/TapestryTestCase.java Sat Nov 17 11:37:20 2007
@@ -949,4 +949,9 @@
 
         return new MapMessages(Locale.ENGLISH, map);
     }
+
+    protected final FieldValidationSupport mockFieldValidationSupport()
+    {
+        return newMock(FieldValidationSupport.class);
+    }
 }

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/FieldValidationSupportImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/FieldValidationSupportImplTest.java?rev=595993&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/FieldValidationSupportImplTest.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/FieldValidationSupportImplTest.java Sat Nov 17 11:37:20 2007
@@ -0,0 +1,349 @@
+package org.apache.tapestry.internal.services;
+
+import org.apache.tapestry.*;
+import org.apache.tapestry.corelib.internal.InternalMessages;
+import org.apache.tapestry.ioc.Messages;
+import org.apache.tapestry.runtime.ComponentEventException;
+import org.apache.tapestry.services.FieldValidationSupport;
+import org.apache.tapestry.services.ValidationMessagesSource;
+import org.apache.tapestry.test.TapestryTestCase;
+import org.easymock.EasyMock;
+import org.easymock.IAnswer;
+import org.testng.annotations.Test;
+
+import java.util.Locale;
+
+public class FieldValidationSupportImplTest extends TapestryTestCase
+{
+
+    @SuppressWarnings({"unchecked"})
+    @Test
+    public void parse_client_via_event() throws ValidationException
+    {
+        ComponentResources resources = mockComponentResources();
+        Translator translator = mockTranslator();
+        ValidationMessagesSource source = mockValidationMessagesSource();
+
+        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(FieldValidationSupportImpl.PARSE_CLIENT_EVENT),
+                                               EasyMock.isA(Object[].class),
+                                               EasyMock.isA(ComponentEventHandler.class))).andAnswer(answer);
+
+
+        replay();
+
+
+        FieldValidationSupport support = new FieldValidationSupportImpl(source);
+
+        Object actual = support.parseClient(clientValue, resources, translator);
+
+        assertEquals(actual, clientValue.toUpperCase());
+
+        verify();
+    }
+
+    @SuppressWarnings({"ThrowableInstanceNeverThrown"})
+    @Test
+    public void parse_client_event_handler_throws_validation_exception() throws Exception
+    {
+        ComponentResources resources = mockComponentResources();
+        Translator translator = mockTranslator();
+        ValidationException ve = new ValidationException("Just didn't feel right.");
+        ValidationMessagesSource source = mockValidationMessagesSource();
+
+        String clientValue = "abracadabra";
+
+
+        EasyMock.expect(resources.triggerEvent(EasyMock.eq(FieldValidationSupportImpl.PARSE_CLIENT_EVENT),
+                                               EasyMock.isA(Object[].class),
+                                               EasyMock.isA(ComponentEventHandler.class))).andThrow(
+                new ComponentEventException(ve.getMessage(), null, ve));
+
+
+        replay();
+
+        FieldValidationSupport support = new FieldValidationSupportImpl(source);
+
+        try
+        {
+            support.parseClient(clientValue, resources, translator);
+
+            unreachable();
+        }
+        catch (ValidationException ex)
+        {
+            assertSame(ex, ve);
+        }
+
+
+        verify();
+    }
+
+    @SuppressWarnings({"ThrowableInstanceNeverThrown"})
+    @Test
+    public void parse_client_event_handler_fails_with_other_exception() throws Exception
+    {
+        ComponentResources resources = mockComponentResources();
+        Translator translator = mockTranslator();
+        RuntimeException re = new RuntimeException("Just didn't feel right.");
+        ComponentEventException cee = new ComponentEventException(re.getMessage(), null, re);
+        ValidationMessagesSource source = mockValidationMessagesSource();
+
+        String clientValue = "abracadabra";
+
+
+        EasyMock.expect(resources.triggerEvent(EasyMock.eq(FieldValidationSupportImpl.PARSE_CLIENT_EVENT),
+                                               EasyMock.isA(Object[].class),
+                                               EasyMock.isA(ComponentEventHandler.class))).andThrow(cee);
+
+
+        replay();
+
+        FieldValidationSupport support = new FieldValidationSupportImpl(source);
+
+        try
+        {
+            support.parseClient(clientValue, resources, translator);
+
+            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();
+        ValidationMessagesSource source = mockValidationMessagesSource();
+        Locale locale = Locale.GERMAN;
+
+        String clientValue = "abracadabra";
+
+
+        EasyMock.expect(resources.triggerEvent(EasyMock.eq(FieldValidationSupportImpl.PARSE_CLIENT_EVENT),
+                                               EasyMock.isA(Object[].class),
+                                               EasyMock.isA(ComponentEventHandler.class))).andReturn(false);
+
+        train_getLocale(resources, locale);
+
+        train_getValidationMessages(source, locale, messages);
+
+        expect(translator.parseClient(clientValue, messages)).andReturn("foobar");
+
+        replay();
+
+        FieldValidationSupport support = new FieldValidationSupportImpl(source);
+
+        Object actual = support.parseClient(clientValue, resources, translator);
+
+        assertEquals(actual, "foobar");
+
+        verify();
+    }
+
+    @SuppressWarnings({"unchecked"})
+    @Test
+    public void to_client_via_translator()
+    {
+        Object value = new Object();
+        ComponentResources resources = mockComponentResources();
+        Translator translator = mockTranslator();
+        ValidationMessagesSource source = mockValidationMessagesSource();
+
+        String clientValue = "abracadabra";
+
+        EasyMock.expect(resources.triggerEvent(EasyMock.eq(FieldValidationSupportImpl.TO_CLIENT_EVENT),
+                                               EasyMock.aryEq(new Object[]{value}),
+                                               EasyMock.isA(ComponentEventHandler.class))).andReturn(false);
+
+        expect(translator.toClient(value)).andReturn(clientValue);
+
+        replay();
+
+        FieldValidationSupport support = new FieldValidationSupportImpl(source);
+
+        String actual = support.toClient(value, resources, translator);
+
+        assertEquals(actual, clientValue);
+
+        verify();
+    }
+
+    @SuppressWarnings({"unchecked"})
+    @Test
+    public void to_client_via_event_handler() throws Exception
+    {
+        Object value = new Object();
+        ComponentResources resources = mockComponentResources();
+        Translator translator = mockTranslator();
+        ValidationMessagesSource source = mockValidationMessagesSource();
+
+        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(FieldValidationSupportImpl.TO_CLIENT_EVENT),
+                                               EasyMock.aryEq(new Object[]{value}),
+                                               EasyMock.isA(ComponentEventHandler.class))).andAnswer(answer);
+
+
+        replay();
+
+        FieldValidationSupport support = new FieldValidationSupportImpl(source);
+
+        String actual = support.toClient(value, resources, translator);
+
+        assertEquals(actual, 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();
+        ValidationMessagesSource source = mockValidationMessagesSource();
+
+        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(FieldValidationSupportImpl.TO_CLIENT_EVENT),
+                                               EasyMock.aryEq(new Object[]{value}),
+                                               EasyMock.isA(ComponentEventHandler.class))).andAnswer(answer);
+
+
+        replay();
+
+        FieldValidationSupport support = new FieldValidationSupportImpl(source);
+
+        try
+        {
+
+            support.toClient(value, resources, translator);
+
+            unreachable();
+        }
+        catch (RuntimeException ex)
+        {
+            assertEquals(ex.getMessage(), InternalMessages.toClientShouldReturnString());
+        }
+
+        verify();
+    }
+
+    @SuppressWarnings({"unchecked"})
+    @Test
+    public void event_triggered_after_delegate_invoked() throws Exception
+    {
+        getMocksControl().checkOrder(true);
+
+        ComponentResources resources = mockComponentResources();
+        FieldValidator fv = mockFieldValidator();
+        ValidationMessagesSource source = mockValidationMessagesSource();
+
+        Object value = new Object();
+
+        fv.validate(value);
+
+        ComponentEventHandler handler = null;
+
+        expect(resources.triggerEvent(EasyMock.eq(FieldValidationSupportImpl.VALIDATE_EVENT),
+                                      EasyMock.aryEq(new Object[]{value}), EasyMock.eq(handler))).andReturn(true);
+
+
+        replay();
+
+        FieldValidationSupport support = new FieldValidationSupportImpl(source);
+
+        support.validate(value, resources, fv);
+
+        verify();
+    }
+
+    @SuppressWarnings({"unchecked", "ThrowableInstanceNeverThrown"})
+    @Test
+    public void event_trigger_throws_validation_exception() throws Exception
+    {
+        ComponentResources resources = mockComponentResources();
+        FieldValidator fv = mockFieldValidator();
+        ValidationMessagesSource source = mockValidationMessagesSource();
+
+        Object value = new Object();
+
+        ValidationException ve = new ValidationException("Bah!");
+        ComponentEventException cee = new ComponentEventException(ve.getMessage(), null, ve);
+
+        ComponentEventHandler handler = null;
+
+        fv.validate(value);
+
+        expect(resources.triggerEvent(EasyMock.eq(FieldValidationSupportImpl.VALIDATE_EVENT),
+                                      EasyMock.aryEq(new Object[]{value}), EasyMock.eq(handler))).andThrow(cee);
+
+
+        replay();
+
+        FieldValidationSupport support = new FieldValidationSupportImpl(source);
+
+
+        try
+        {
+            support.validate(value, resources, fv);
+            unreachable();
+        }
+        catch (ValidationException ex)
+        {
+            assertSame(ex, ve);
+        }
+
+        verify();
+    }
+}

Modified: tapestry/tapestry5/trunk/tapestry-upload/src/main/java/org/apache/tapestry/upload/components/Upload.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-upload/src/main/java/org/apache/tapestry/upload/components/Upload.java?rev=595993&r1=595992&r2=595993&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-upload/src/main/java/org/apache/tapestry/upload/components/Upload.java (original)
+++ tapestry/tapestry5/trunk/tapestry-upload/src/main/java/org/apache/tapestry/upload/components/Upload.java Sat Nov 17 11:37:20 2007
@@ -18,8 +18,8 @@
 import org.apache.tapestry.annotations.Environmental;
 import org.apache.tapestry.annotations.Parameter;
 import org.apache.tapestry.corelib.base.AbstractField;
-import org.apache.tapestry.corelib.internal.ComponentValidatorWrapper;
 import org.apache.tapestry.ioc.annotations.Inject;
+import org.apache.tapestry.services.FieldValidationSupport;
 import org.apache.tapestry.services.FieldValidatorDefaultSource;
 import org.apache.tapestry.services.FormSupport;
 import org.apache.tapestry.upload.services.MultipartDecoder;
@@ -67,6 +67,9 @@
     @Inject
     private Locale _locale;
 
+    @Inject
+    private FieldValidationSupport _fieldValidationSupport;
+
     /**
      * Computes a default value for the "validate" parameter using
      * {@link FieldValidatorDefaultSource}.
@@ -88,13 +91,14 @@
 
     // For testing
     Upload(UploadedFile value, FieldValidator<Object> validate, MultipartDecoder decoder, ValidationTracker tracker,
-           ComponentResources resources)
+           ComponentResources resources, FieldValidationSupport fieldValidationSupport)
     {
         _value = value;
         if (validate != null) _validate = validate;
         _decoder = decoder;
         _tracker = tracker;
         _resources = resources;
+        _fieldValidationSupport = fieldValidationSupport;
     }
 
     @SuppressWarnings({"unchecked"})
@@ -108,11 +112,9 @@
             if (uploaded.getFileName() == null || uploaded.getFileName().length() == 0) uploaded = null;
         }
 
-        FieldValidator wrappedValidator = new ComponentValidatorWrapper(_resources, _validate);
-
         try
         {
-            wrappedValidator.validate(uploaded);
+            _fieldValidationSupport.validate(uploaded, _resources, _validate);
         }
         catch (ValidationException ex)
         {

Modified: tapestry/tapestry5/trunk/tapestry-upload/src/test/java/org/apache/tapestry/upload/components/UploadTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-upload/src/test/java/org/apache/tapestry/upload/components/UploadTest.java?rev=595993&r1=595992&r2=595993&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-upload/src/test/java/org/apache/tapestry/upload/components/UploadTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-upload/src/test/java/org/apache/tapestry/upload/components/UploadTest.java Sat Nov 17 11:37:20 2007
@@ -16,11 +16,12 @@
 
 import org.apache.tapestry.*;
 import org.apache.tapestry.dom.Element;
+import org.apache.tapestry.services.FieldValidationSupport;
 import org.apache.tapestry.services.FormSupport;
 import org.apache.tapestry.test.TapestryTestCase;
 import org.apache.tapestry.upload.services.MultipartDecoder;
 import org.apache.tapestry.upload.services.UploadedFile;
-import static org.easymock.EasyMock.*;
+import static org.easymock.EasyMock.expectLastCall;
 import org.testng.annotations.Test;
 
 public class UploadTest extends TapestryTestCase
@@ -46,7 +47,7 @@
 
         replay();
 
-        Upload component = new Upload(null, null, null, null, resources);
+        Upload component = new Upload(null, null, null, null, resources, null);
 
         component.injectDecorator(new StubValidationDecorator());
         component.injectFormSupport(formSupport);
@@ -70,7 +71,7 @@
         getMocksControl().checkOrder(true);
 
         ComponentResources resources = mockComponentResources();
-        Upload component = new Upload(null, null, null, null, resources);
+        Upload component = new Upload(null, null, null, null, resources, null);
         MarkupWriter writer = createMarkupWriter();
         writer.element("form");
 
@@ -106,7 +107,7 @@
 
         FieldValidator<Object> validate = mockFieldValidator();
         ComponentResources resources = mockComponentResources();
-        Upload component = new Upload(null, validate, null, null, resources);
+        Upload component = new Upload(null, validate, null, null, resources, null);
         MarkupWriter writer = createMarkupWriter();
         writer.element("form");
 
@@ -145,6 +146,7 @@
 
     }
 
+    @SuppressWarnings({"unchecked"})
     @Test
     public void process_submission_extracts_value_from_decoder() throws Exception
     {
@@ -152,13 +154,15 @@
         MultipartDecoder decoder = mockMultipartDecoder();
         UploadedFile uploadedFile = mockUploadedFile();
         ComponentResources resources = mockComponentResources();
+        FieldValidationSupport support = mockFieldValidationSupport();
+        FieldValidator validate = mockFieldValidator();
 
-        Upload component = new Upload(null, null, decoder, null, resources);
+        Upload component = new Upload(null, validate, decoder, null, resources, support);
 
         expect(decoder.getFileUpload("test")).andReturn(uploadedFile);
         expect(uploadedFile.getFileName()).andReturn("foo").anyTimes();
 
-        train_validate(resources, uploadedFile);
+        support.validate(uploadedFile, resources, validate);
 
         replay();
 
@@ -169,12 +173,7 @@
         assertSame(component.getValue(), uploadedFile);
     }
 
-    protected final void train_validate(ComponentResources resources, Object... context)
-    {
-        ComponentEventHandler handler = null;
-        expect(resources.triggerEvent(eq("validate"), aryEq(context), eq(handler))).andReturn(false);
-    }
-
+    @SuppressWarnings({"unchecked"})
     @Test
     public void process_submission_ignores_null_value() throws Exception
     {
@@ -182,14 +181,16 @@
         MultipartDecoder decoder = mockMultipartDecoder();
         UploadedFile uploadedFile = mockUploadedFile();
         ComponentResources resources = mockComponentResources();
+        FieldValidationSupport support = mockFieldValidationSupport();
+        FieldValidator validate = mockFieldValidator();
 
-        Upload component = new Upload(null, null, decoder, null, resources);
+        Upload component = new Upload(null, validate, decoder, null, resources, support);
 
         expect(decoder.getFileUpload("test")).andReturn(uploadedFile);
         expect(uploadedFile.getFileName()).andReturn("").atLeastOnce();
 
+        support.validate(null, resources, validate);
 
-        train_validate(resources, new Object[]{null});
 
         replay();
 
@@ -209,15 +210,14 @@
         UploadedFile uploadedFile = mockUploadedFile();
         FieldValidator<Object> validate = mockFieldValidator();
         ComponentResources resources = mockComponentResources();
+        FieldValidationSupport support = mockFieldValidationSupport();
 
-        Upload component = new Upload(null, validate, decoder, null, resources);
+        Upload component = new Upload(null, validate, decoder, null, resources, support);
 
         expect(decoder.getFileUpload("test")).andReturn(uploadedFile);
         expect(uploadedFile.getFileName()).andReturn("test").atLeastOnce();
 
-        train_validate(resources, uploadedFile);
-
-        validate.validate(uploadedFile);
+        support.validate(uploadedFile, resources, validate);
 
         replay();
 
@@ -226,7 +226,7 @@
         verify();
     }
 
-    @SuppressWarnings("unchecked")
+    @SuppressWarnings({"unchecked", "ThrowableInstanceNeverThrown"})
     @Test
     public void process_submission_tracks_validator_errors() throws Exception
     {
@@ -236,14 +236,14 @@
         FieldValidator<Object> validate = mockFieldValidator();
         ValidationTracker tracker = mockValidationTracker();
         ComponentResources resources = mockComponentResources();
+        FieldValidationSupport support = mockFieldValidationSupport();
 
-        Upload component = new Upload(null, validate, decoder, tracker, resources);
+        Upload component = new Upload(null, validate, decoder, tracker, resources, support);
 
         expect(decoder.getFileUpload("test")).andReturn(uploadedFile);
         expect(uploadedFile.getFileName()).andReturn("test").atLeastOnce();
 
-        validate.validate(uploadedFile);
-
+        support.validate(uploadedFile, resources, validate);
         expectLastCall().andThrow(new ValidationException("an error"));
 
         tracker.recordError(component, "an error");