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/16 18:07:49 UTC

svn commit: r595742 - 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: Fri Nov 16 09:07:48 2007
New Revision: 595742

URL: http://svn.apache.org/viewvc?rev=595742&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/ComponentValidatorWrapper.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/corelib/internal/ComponentTranslatorWrapper.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/runtime/ComponentEventException.java
    tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/validation.apt
    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/app1/pages/EventMethodTranslate.java
    tapestry/tapestry5/trunk/tapestry-upload/src/main/java/org/apache/tapestry/upload/components/Upload.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=595742&r1=595741&r2=595742&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 Fri Nov 16 09:07:48 2007
@@ -20,6 +20,7 @@
 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.*;
@@ -169,6 +170,7 @@
      */
     protected abstract void writeFieldTag(MarkupWriter writer, String value);
 
+    @SuppressWarnings({"unchecked"})
     @Override
     protected final void processSubmission(FormSupport formSupport, String elementName)
     {
@@ -180,13 +182,13 @@
 
         try
         {
-            Translator wrapper = new ComponentTranslatorWrapper(_resources, _translate);
+            Translator translatorWrapper = new ComponentTranslatorWrapper(_resources, _translate);
 
-            Object translated = wrapper.parseClient(rawValue, messages);
+            Object translated = translatorWrapper.parseClient(rawValue, messages);
 
-            // TODO: A wrapper for validation as well?
+            FieldValidator validatorWrapper = new ComponentValidatorWrapper(_resources, _validate);
 
-            _validate.validate(translated);
+            validatorWrapper.validate(translated);
 
             _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=595742&r1=595741&r2=595742&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 Fri Nov 16 09:07:48 2007
@@ -19,6 +19,7 @@
 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.*;
@@ -104,6 +105,7 @@
     @Inject
     private ValueEncoderSource _valueEncoderSource;
 
+    @SuppressWarnings({"unchecked"})
     @Override
     protected void processSubmission(FormSupport formSupport, String elementName)
     {
@@ -111,16 +113,17 @@
 
         Object selectedValue = _encoder.toValue(primaryKey);
 
+        FieldValidator wrappedValidator = new ComponentValidatorWrapper(_resources, _validate);
+
         try
         {
-            _validate.validate(selectedValue);
+            wrappedValidator.validate(selectedValue);
 
             _value = selectedValue;
         }
         catch (ValidationException ex)
         {
             _tracker.recordError(this, ex.getMessage());
-            return;
         }
     }
 
@@ -167,13 +170,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"));
     }
 
     Binding defaultValue()

Modified: 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=595742&r1=595741&r2=595742&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/internal/ComponentTranslatorWrapper.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/internal/ComponentTranslatorWrapper.java Fri Nov 16 09:07:48 2007
@@ -68,11 +68,9 @@
         }
         catch (ComponentEventException ex)
         {
-            Throwable cause = ex.getCause();
+            ValidationException ve = ex.get(ValidationException.class);
 
-            if (cause instanceof ValidationException) throw (ValidationException) cause;
-
-            // Rethrow the event exception for reporting higher up.
+            if (ve != null) throw ve;
 
             throw ex;
         }

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/internal/ComponentValidatorWrapper.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/internal/ComponentValidatorWrapper.java?rev=595742&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/internal/ComponentValidatorWrapper.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/internal/ComponentValidatorWrapper.java Fri Nov 16 09:07:48 2007
@@ -0,0 +1,55 @@
+package org.apache.tapestry.corelib.internal;
+
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.FieldValidator;
+import org.apache.tapestry.MarkupWriter;
+import org.apache.tapestry.ValidationException;
+import org.apache.tapestry.runtime.ComponentEventException;
+
+/**
+ * Wraps around a component, allowing it to perform input validation using
+ * an event.
+ */
+@SuppressWarnings("unchecked")
+public class ComponentValidatorWrapper implements FieldValidator
+{
+    static final String VALIDATE_EVENT = "validate";
+
+    private final ComponentResources _resources;
+    private final FieldValidator _validator;
+
+    public ComponentValidatorWrapper(ComponentResources resources, FieldValidator delegate)
+    {
+        _resources = resources;
+        _validator = delegate;
+    }
+
+    /**
+     * Invokes a "validate" event on the component, passing the value as context.
+     *
+     * @param value
+     * @throws ValidationException
+     */
+    public void validate(Object value) throws ValidationException
+    {
+        try
+        {
+            _resources.triggerEvent(VALIDATE_EVENT, new Object[]{value}, null);
+        }
+        catch (ComponentEventException ex)
+        {
+            ValidationException ve = ex.get(ValidationException.class);
+
+            if (ve != null) throw ve;
+
+            throw ex;
+        }
+
+        _validator.validate(value);
+    }
+
+    public void render(MarkupWriter writer)
+    {
+        _validator.render(writer);
+    }
+}

Modified: 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=595742&r1=595741&r2=595742&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/runtime/ComponentEventException.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/runtime/ComponentEventException.java Fri Nov 16 09:07:48 2007
@@ -37,4 +37,21 @@
     {
         return _methodDescription;
     }
+
+    /**
+     * Checks to see if the root cause of this exception is assignable to the provided type. Returns the
+     * root cause, cast to the given value if so, or null otherwise.
+     *
+     * @param exceptionType type of exception to check for
+     * @return the root cause, or null if the root cause is null or not assignable to the provided exception type
+     */
+    public <T extends Exception> T get(Class<T> exceptionType)
+    {
+        Throwable cause = getCause();
+
+        if (cause != null && exceptionType.isAssignableFrom(cause.getClass())) return exceptionType.cast(cause);
+
+        return null;
+    }
+
 }

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=595742&r1=595741&r2=595742&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 Fri Nov 16 09:07:48 2007
@@ -294,8 +294,25 @@
   {{{../../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,
+  Now, what if you want to perform your own custom validation?  That's another event: "validate":
+
++---+
+  void onValidateFromCount(Integer value) throws ValidationException
+  {
+    if (value == null) return;
+
+    if (value.equals(13)) throw new ValidationException("Thirteen is an unlucky number.");
+  }
++---+
+
+  This event gets fired <<before>> the normal validators, therefore you have to watch out
+  for nulls, even if the field is marked as required.  It gets the <parsed> value (not the string from
+  the client, but the object value from the translator, or from the "parseclient" event handler).
+
+  The method may not return value, but may throw a ValidationException to indicate a problem with
+  the value.
+
+  <<Caution:>> These events are exclusively on the <server side>.
+  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

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/corelib/internal/ComponentValidatorWrapperTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/corelib/internal/ComponentValidatorWrapperTest.java?rev=595742&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/corelib/internal/ComponentValidatorWrapperTest.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/corelib/internal/ComponentValidatorWrapperTest.java Fri Nov 16 09:07:48 2007
@@ -0,0 +1,90 @@
+package org.apache.tapestry.corelib.internal;
+
+import org.apache.tapestry.*;
+import org.apache.tapestry.runtime.ComponentEventException;
+import org.apache.tapestry.test.TapestryTestCase;
+import org.easymock.EasyMock;
+import org.testng.annotations.Test;
+
+public class ComponentValidatorWrapperTest extends TapestryTestCase
+{
+    @Test
+    public void render_is_a_pass_thru()
+    {
+        ComponentResources resources = mockComponentResources();
+        FieldValidator fv = mockFieldValidator();
+        MarkupWriter writer = mockMarkupWriter();
+
+        fv.render(writer);
+
+        replay();
+
+        FieldValidator wrapper = new ComponentValidatorWrapper(resources, fv);
+
+        wrapper.render(writer);
+
+        verify();
+    }
+
+    @SuppressWarnings({"unchecked"})
+    @Test
+    public void event_triggered_before_delegate_invoked() throws Exception
+    {
+        getMocksControl().checkOrder(true);
+
+        ComponentResources resources = mockComponentResources();
+        FieldValidator fv = mockFieldValidator();
+
+        Object value = new Object();
+
+        ComponentEventHandler handler = null;
+
+        expect(resources.triggerEvent(EasyMock.eq(ComponentValidatorWrapper.VALIDATE_EVENT),
+                                      EasyMock.aryEq(new Object[]{value}), EasyMock.eq(handler))).andReturn(true);
+
+        fv.validate(value);
+
+        replay();
+
+        FieldValidator wrapper = new ComponentValidatorWrapper(resources, fv);
+
+        wrapper.validate(value);
+
+        verify();
+    }
+
+    @SuppressWarnings({"unchecked", "ThrowableInstanceNeverThrown"})
+    @Test
+    public void event_trigger_throws_validation_exception() throws Exception
+    {
+        ComponentResources resources = mockComponentResources();
+        FieldValidator fv = mockFieldValidator();
+
+        Object value = new Object();
+
+        ValidationException ve = new ValidationException("Bah!");
+        ComponentEventException cee = new ComponentEventException(ve.getMessage(), null, ve);
+
+        ComponentEventHandler handler = null;
+
+        expect(resources.triggerEvent(EasyMock.eq(ComponentValidatorWrapper.VALIDATE_EVENT),
+                                      EasyMock.aryEq(new Object[]{value}), EasyMock.eq(handler))).andThrow(cee);
+
+
+        replay();
+
+        FieldValidator wrapper = new ComponentValidatorWrapper(resources, fv);
+
+
+        try
+        {
+            wrapper.validate(value);
+            unreachable();
+        }
+        catch (ValidationException ex)
+        {
+            assertSame(ex, ve);
+        }
+        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=595742&r1=595741&r2=595742&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 Fri Nov 16 09:07:48 2007
@@ -1151,5 +1151,12 @@
         assertTextPresent("Count: [0]");
 
         assertFieldValue("count", "zero");
+
+        // Try the server-side custom exception reporting.
+
+        type("count", "13");
+        clickAndWait(SUBMIT);
+
+        assertTextPresent("Thirteen is an unlucky number.");
     }
 }

Modified: 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=595742&r1=595741&r2=595742&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/EventMethodTranslate.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/EventMethodTranslate.java Fri Nov 16 09:07:48 2007
@@ -52,4 +52,12 @@
 
         return null;
     }
+
+    void onValidateFromCount(Integer count) throws ValidationException
+    {
+        // count may be null
+        if (count == null) return;
+
+        if (count.equals(13)) throw new ValidationException("Thirteen is an unlucky number.");
+    }
 }

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=595742&r1=595741&r2=595742&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 Fri Nov 16 09:07:48 2007
@@ -18,6 +18,7 @@
 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.FieldValidatorDefaultSource;
 import org.apache.tapestry.services.FormSupport;
@@ -76,13 +77,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"));
     }
 
     public Upload()
@@ -90,8 +87,8 @@
     }
 
     // For testing
-    Upload(UploadedFile value, FieldValidator<Object> validate, MultipartDecoder decoder,
-           ValidationTracker tracker, ComponentResources resources)
+    Upload(UploadedFile value, FieldValidator<Object> validate, MultipartDecoder decoder, ValidationTracker tracker,
+           ComponentResources resources)
     {
         _value = value;
         if (validate != null) _validate = validate;
@@ -100,6 +97,7 @@
         _resources = resources;
     }
 
+    @SuppressWarnings({"unchecked"})
     @Override
     protected void processSubmission(FormSupport formSupport, String elementName)
     {
@@ -107,13 +105,14 @@
 
         if (uploaded != null)
         {
-            if (uploaded.getFileName() == null || uploaded.getFileName().length() == 0)
-                uploaded = null;
+            if (uploaded.getFileName() == null || uploaded.getFileName().length() == 0) uploaded = null;
         }
 
+        FieldValidator wrappedValidator = new ComponentValidatorWrapper(_resources, _validate);
+
         try
         {
-            _validate.validate(uploaded);
+            wrappedValidator.validate(uploaded);
         }
         catch (ValidationException ex)
         {