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/01/19 16:39:47 UTC

svn commit: r497845 - in /tapestry/tapestry5/tapestry-core/trunk/src: main/java/org/apache/tapestry/ main/java/org/apache/tapestry/corelib/components/ main/java/org/apache/tapestry/internal/services/ main/java/org/apache/tapestry/test/ main/java/org/ap...

Author: hlship
Date: Fri Jan 19 07:39:41 2007
New Revision: 497845

URL: http://svn.apache.org/viewvc?view=rev&rev=497845
Log:
Add support for overriding validation messages on a per-component/per-validator basis.
Start documentation about Forms and Validation.

Added:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/MinLength.java
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/validation.apt
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ValidForm.properties
Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Validator.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Form.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/FieldValidatorSourceImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/Required.java
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt
    tapestry/tapestry5/tapestry-core/trunk/src/site/site.xml
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/FieldValidatorSourceImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/validator/RequiredTest.java

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Validator.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Validator.java?view=diff&rev=497845&r1=497844&r2=497845
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Validator.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Validator.java Fri Jan 19 07:39:41 2007
@@ -14,7 +14,8 @@
 
 package org.apache.tapestry;
 
-import org.apache.tapestry.ioc.Messages;
+import org.apache.tapestry.ioc.MessageFormatter;
+import org.apache.tapestry.services.ValidationMessagesSource;
 
 /**
  * Used by a {@link Field} to enforce a <strong>constraint</strong> related to a form submission.
@@ -30,7 +31,17 @@
      * type int (the maximum length allowed). For constraints that do not have a constraint value,
      * this method returns null.
      */
-    public Class<C> getConstraintType();
+    Class<C> getConstraintType();
+
+    /**
+     * Returns the message key, within the validiation messages, normally used by this validator.
+     * This is used to provide the {@link MessageFormatter} passed to
+     * {@link #check(Field, Object, MessageFormatter, Object)} (unless overridden).
+     * 
+     * @see ValidationMessagesSource
+     * @return a message key
+     */
+    String getMessageKey();
 
     /**
      * Invoked after the client-submitted value has been {@link Translator translated} to check that
@@ -41,13 +52,13 @@
      *            the field for which a client submitted value is being validated
      * @param constraintValue
      *            the value used to constrain
-     * @param messages
+     * @param formatter
      *            Validation messages, in the appropriate locale
      * @param value
      *            the translated value supplied by the user
      * @throws ValidationException
      *             if the value violates the constraint
      */
-    void check(Field field, C constraintValue, Messages messages, T value)
+    void check(Field field, C constraintValue, MessageFormatter formatter, T value)
             throws ValidationException;
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Form.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Form.java?view=diff&rev=497845&r1=497844&r2=497845
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Form.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Form.java Fri Jan 19 07:39:41 2007
@@ -59,7 +59,7 @@
  * An HTML form, which will enclose other components to render out the various types of fields.
  * <p>
  * A Form emits several notification events; when it renders it sends a
- * {@link #PREPARE_EVENT#prepare} notification event, to allow any listeners to set up the state of
+ * {@link #PREPARE_EVENT prepare} notification event, to allow any listeners to set up the state of
  * the page prior to rendering out the form's content.
  * <p>
  * When the form is submitted, the component emits four notifications: first another prepare event
@@ -379,7 +379,7 @@
     }
 
     /**
-     * A convienience for invoking {@link ValidationTracker#recordError(String). 
+     * A convienience for invoking {@link ValidationTracker#recordError(String)}. 
      */
     public void recordError(String errorMessage)
     {
@@ -391,7 +391,7 @@
     }
 
     /**
-     * A convienience for invoking {@link ValidationTracker#recordError(Field, String).
+     * A convienience for invoking {@link ValidationTracker#recordError(Field, String)}.
      */
     public void recordError(Field field, String errorMessage)
     {

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/FieldValidatorSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/FieldValidatorSourceImpl.java?view=diff&rev=497845&r1=497844&r2=497845
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/FieldValidatorSourceImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/FieldValidatorSourceImpl.java Fri Jan 19 07:39:41 2007
@@ -22,10 +22,12 @@
 import java.util.Locale;
 import java.util.Map;
 
+import org.apache.tapestry.ComponentResources;
 import org.apache.tapestry.Field;
 import org.apache.tapestry.FieldValidator;
 import org.apache.tapestry.ValidationException;
 import org.apache.tapestry.Validator;
+import org.apache.tapestry.ioc.MessageFormatter;
 import org.apache.tapestry.ioc.Messages;
 import org.apache.tapestry.ioc.internal.util.InternalUtils;
 import org.apache.tapestry.ioc.services.TypeCoercer;
@@ -68,18 +70,40 @@
         final Object coercedConstraintValue = coerceConstraintValue(constraintValue, validator
                 .getConstraintType());
 
-        Locale locale = component.getComponentResources().getLocale();
-
-        final Messages messages = _messagesSource.getValidationMessages(locale);
+        final MessageFormatter formatter = findMessageFormatter(component, validatorType, validator);
 
         return new FieldValidator()
         {
             @SuppressWarnings("unchecked")
             public void check(Object value) throws ValidationException
             {
-                validator.check(field, coercedConstraintValue, messages, value);
+                validator.check(field, coercedConstraintValue, formatter, value);
             }
         };
+    }
+
+    private MessageFormatter findMessageFormatter(Component component, String validatorType,
+            Validator validator)
+    {
+        ComponentResources resources = component.getComponentResources();
+
+        String overrideKey = resources.getId() + "-" + validatorType;
+
+        // So, if you use a TextField on your EditUser page, we want to search the messages
+        // of the EditUser page (the container), not the TextField (which will always be the same).
+        
+        Messages messages = resources.getContainerResources().getMessages();
+
+        if (messages.contains(overrideKey))
+            return messages.getFormatter(overrideKey);
+
+        Locale locale = resources.getLocale();
+
+        messages = _messagesSource.getValidationMessages(locale);
+
+        String key = validator.getMessageKey();
+
+        return messages.getFormatter(key);
     }
 
     public FieldValidator createValidators(Field field, String specification)

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java?view=diff&rev=497845&r1=497844&r2=497845
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java Fri Jan 19 07:39:41 2007
@@ -48,6 +48,7 @@
 import org.apache.tapestry.annotations.Inject;
 import org.apache.tapestry.annotations.Parameter;
 import org.apache.tapestry.ioc.Location;
+import org.apache.tapestry.ioc.MessageFormatter;
 import org.apache.tapestry.ioc.Messages;
 import org.apache.tapestry.ioc.Resource;
 import org.apache.tapestry.ioc.ServiceLocator;
@@ -670,5 +671,20 @@
     protected final Heartbeat newHeartbeat()
     {
         return newMock(Heartbeat.class);
+    }
+
+    protected void train_getMessageKey(Validator validator, String messageKey)
+    {
+        expect(validator.getMessageKey()).andReturn(messageKey).atLeastOnce();
+    }
+
+    protected final void train_getMessageFormatter(Messages messages, String key, MessageFormatter formatter)
+    {
+        expect(messages.getFormatter(key)).andReturn(formatter).atLeastOnce();
+    }
+
+    protected final MessageFormatter newMessageFormatter()
+    {
+        return newMock(MessageFormatter.class);
     }
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/MinLength.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/MinLength.java?view=auto&rev=497845
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/MinLength.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/MinLength.java Fri Jan 19 07:39:41 2007
@@ -0,0 +1,29 @@
+package org.apache.tapestry.validator;
+
+import org.apache.tapestry.Field;
+import org.apache.tapestry.ValidationException;
+import org.apache.tapestry.Validator;
+import org.apache.tapestry.ioc.MessageFormatter;
+import org.apache.tapestry.ioc.internal.util.InternalUtils;
+
+public class MinLength implements Validator<Integer, String>
+{
+    public String getMessageKey()
+    {
+        return "min-length";
+    }
+
+    public void check(Field field, Integer constraintValue, MessageFormatter formatter, String value)
+            throws ValidationException
+    {
+        if (InternalUtils.isBlank(value))
+            return;
+
+    }
+
+    public Class<Integer> getConstraintType()
+    {
+        return Integer.class;
+    }
+
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/Required.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/Required.java?view=diff&rev=497845&r1=497844&r2=497845
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/Required.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/validator/Required.java Fri Jan 19 07:39:41 2007
@@ -17,7 +17,7 @@
 import org.apache.tapestry.Field;
 import org.apache.tapestry.ValidationException;
 import org.apache.tapestry.Validator;
-import org.apache.tapestry.ioc.Messages;
+import org.apache.tapestry.ioc.MessageFormatter;
 
 /**
  * A validator that enforces that the value is not null and not the empty string. This validator is
@@ -25,11 +25,16 @@
  */
 public class Required implements Validator<Void, Object>
 {
-    public void check(Field field, Void constraintValue, Messages messages, Object value)
+    public String getMessageKey()
+    {
+        return "required";
+    }
+
+    public void check(Field field, Void constraintValue, MessageFormatter formatter, Object value)
             throws ValidationException
     {
         if (value == null || value.toString().equals(""))
-            throw new ValidationException(messages.format("required", field.getLabel()));
+            throw new ValidationException(formatter.format(field.getLabel()));
     }
 
     public Class<Void> getConstraintType()

Added: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/validation.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/validation.apt?view=auto&rev=497845
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/validation.apt (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/validation.apt Fri Jan 19 07:39:41 2007
@@ -0,0 +1,130 @@
+ ---
+ Form Input and Validation
+ ---
+ 
+Form Input and Validation
+
+  The life's blood of any application is form input; this is the most effective way to gather significant information from the user.
+  Whether it's a search form, a login screen or a multi-page registration wizard, forms are how the user really expresses themselves to the
+  application. 
+  
+  Tapestry excels at creating forms and validating input.  Input validation is declarative, meaning you simply tell Tapestry what validations
+  to apply to a given field, and it takes care of it on the server and (once implemented) on the client as well.
+  
+Form component
+
+  The core of Tapestry's form support is the
+  {{{../apidocs/org/apache/tapestry/corelib/components/Form.html}Form}} component.  The Form component encloses (wraps around) all the
+  other <field components> (components that create HTML form element for the client web browser) such as
+  {{{../apidocs/org/apache/tapestry/corelib/components/TextField.html}TextField}},
+  {{{../apidocs/org/apache/tapestry/corelib/components/TextArea.html}TextArea}},
+  {{{../apidocs/org/apache/tapestry/corelib/components/Checkbox.html}Checkbox}}, etc.
+  
+  The Form component generates a number of {{{event.html}component events}} that
+  you may provide event handler methods for.
+  
+  When rendering, the Form component emits a "prepare" notification, to allow the 
+  Form's container to setup any fields or properties that will be referenced in the form.
+  For example, this is a good chance to create a temporary entity object to be rendered, or
+  to load an entity from a database to be editted.
+  
+  When user submits the form on the client, a series of steps occur on the server.
+  
+  First, the Form emits a "prepare" notification, as it did when the Form was rendered.
+  
+  Next, all the fields inside the form are activated to pull values out of the
+  incoming request, validate them and (if valid) store the changes.
+  
+  <For Tapestry 4 Users: > Tapestry 5 does not use the fragile "form rewind" method
+  from Tapestry 4.  Instead, a hidden field generated during the render stores
+  the information needed to process the form submission.
+  
+  After the fields have done their processing, the Form emits a "validate" event.
+  This is a chance to perform cross-form validation that can't be described declaratively.
+  
+  Next, the Form determines if there have been any validation errors.  If there have been,
+  then the submission is considered a failure, and a "failure" event is emitted.
+  If there have been no validation errors, then a "success" event is emitted.
+  
+  Last, the Form emits a "submit" event (for logic that doesn't care about success or
+  failure).
+  
+Tracking Validation Errors
+
+  Associated with the Form is an
+  {{{../apidocs/org/apache/tapestry/ValidationTracker.html}ValidationTracker}}
+  that tracks all the provided user input and validation errors for every field in the
+  form.  The tracker can be provided to the Form via the Form's tracker parameter,
+  but this is rarely necessary.
+  
+  The Form includes  methods isValid() and getHasErrors(), which are used to
+  see if the Form's validation tracker contains any errors.
+  
+  In your own logic, it is possible to record your own errors.  Form includes
+  two different versions of recordError(), one of which specifies a 
+  {{{../apidocs/org/apache/tapestry/Field.html}Field}} (an interface implemented by
+  all form element components), and one of which is for "global" errors, unassociated
+  with any particular field.
+  
+Storing Data Between Requests
+
+  As with other action requests, the result of a form submission is to send a redirect
+  to the client which re-renders the page.  The ValidationTracker must be
+  stored {{{persist.html}persistently}} between requests, or all the validation
+  information will be lost (the default ValidationTracker provided by the Form is persistent).
+  
+  Likewise, the individual fields updated by the components should also be persistent.
+  
+  For example, a Login page, which collects a user name and a password, might look like:
+  
++---+
+@ComponentClass
+public class Login
+{
+    @Retain
+    private String _userName;
+
+    private String _password;
+
+    @Inject
+    private UserAuthenticator _authenticator;
+
+    @Component(id = "password")
+    private PasswordField _passwordField;
+
+    @Component
+    private Form _form;
+
+    String onSuccess()
+    {
+        if (!_authenticator.isValid(_userName, _password))
+        {
+            _form.recordError(_passwordField, "Invalid user name or password.");
+            return null;
+        }
+
+        return "PostLogin";
+    }
+
+    public String getPassword()
+    {
+        return _password;
+    }
+
+    public void setPassword(String password)
+    {
+        _password = password;
+    }
+
+    public String getUserName()
+    {
+        return _userName;
+    }
+
+    public void setUserName(String userName)
+    {
+        _userName = userName;
+    }
+}
++---+  
+  
\ No newline at end of file

Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt?view=diff&rev=497845&r1=497844&r2=497845
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt Fri Jan 19 07:39:41 2007
@@ -38,6 +38,8 @@
   Progress on Tapestry 5 is really taking off. This space lists some cool new features that have been added
   recently.
   
+  * Input validation messages may not be overriden by providing a particular message key in the containing component's message catalog.
+  
   * Property expressions may now reference public methods (with no parameters) in addition to traditional property names.
   
   * Page templates are now allowed to be stored in the web application root, as well as on the classpath.

Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/site.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/site.xml?view=diff&rev=497845&r1=497844&r2=497845
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/site.xml (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/site.xml Fri Jan 19 07:39:41 2007
@@ -54,6 +54,7 @@
             <item name="Component Classes" href="guide/component-classes.html"/>
             <item name="Component Templates" href="guide/templates.html"/>
             <item name="Component Parameters" href="guide/parameters.html"/>
+            <item name="Input Validation" href="guide/validation.html"/>
             <item name="Component Events" href="guide/event.html"/>
             <item name="Component Mixins" href="guide/mixins.html"/>
             <item name="Localization" href="guide/localization.html"/>

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java?view=diff&rev=497845&r1=497844&r2=497845
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java Fri Jan 19 07:39:41 2007
@@ -456,7 +456,8 @@
         clickAndWait("link=ValidForm");
         clickAndWait("//input[@type='submit']");
         assertTextPresent("You must provide a value for Email.");
-        assertTextPresent("You must provide a value for Incident Message.");
+        // This is an overrdden validation error message:
+        assertTextPresent("Please provide a detailed description of the incident.");
 
         // Check on decorations via the default validation decorator:
 
@@ -509,7 +510,7 @@
     @Test
     public void volatile_loop_inside_a_form()
     {
-        test_loop_inside_form("ToDo List (Volatile)");        
+        test_loop_inside_form("ToDo List (Volatile)");
     }
 
     @Test

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/FieldValidatorSourceImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/FieldValidatorSourceImplTest.java?view=diff&rev=497845&r1=497844&r2=497845
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/FieldValidatorSourceImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/FieldValidatorSourceImplTest.java Fri Jan 19 07:39:41 2007
@@ -27,6 +27,7 @@
 import org.apache.tapestry.FieldValidator;
 import org.apache.tapestry.Validator;
 import org.apache.tapestry.internal.test.InternalBaseTestCase;
+import org.apache.tapestry.ioc.MessageFormatter;
 import org.apache.tapestry.ioc.Messages;
 import org.apache.tapestry.ioc.services.TypeCoercer;
 import org.apache.tapestry.runtime.Component;
@@ -81,20 +82,77 @@
         Validator validator = newValidator();
         TypeCoercer coercer = newTypeCoercer();
         FieldComponent field = newFieldComponent();
-        Messages messages = newMock(Messages.class);
+        Messages messages = newMessages();
+        MessageFormatter formatter = newMessageFormatter();
         Object inputValue = new Object();
         ComponentResources resources = newComponentResources();
+        ComponentResources containerResources = newComponentResources();
+        Messages componentMessages = newMessages();
 
         Map<String, Validator> map = singletonMap("required", validator);
 
         train_getConstraintType(validator, null);
 
         train_getComponentResources(field, resources);
+
+        train_getId(resources, "fred");
+        train_getContainerResources(resources, containerResources);
+        train_getMessages(containerResources, componentMessages);
+        train_contains(componentMessages, "fred-required", false);
+
         train_getLocale(resources, Locale.FRENCH);
 
         train_getValidationMessages(messagesSource, Locale.FRENCH, messages);
 
-        validator.check(field, null, messages, inputValue);
+        train_getMessageKey(validator, "key");
+        train_getMessageFormatter(messages, "key", formatter);
+
+        validator.check(field, null, formatter, inputValue);
+
+        replay();
+
+        FieldValidatorSource source = new FieldValidatorSourceImpl(messagesSource, coercer, map);
+
+        FieldValidator fieldValidator = source.createValidator(field, "required", null);
+
+        fieldValidator.check(inputValue);
+
+        verify();
+    }
+
+    protected final void train_getContainerResources(ComponentResources resources,
+            ComponentResources containerResources)
+    {
+        expect(resources.getContainerResources()).andReturn(containerResources).atLeastOnce();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void component_messages_overrides_validator_messages() throws Exception
+    {
+        ValidationMessagesSource messagesSource = newValidationMessagesSource();
+        Validator validator = newValidator();
+        TypeCoercer coercer = newTypeCoercer();
+        FieldComponent field = newFieldComponent();
+        MessageFormatter formatter = newMessageFormatter();
+        Object inputValue = new Object();
+        ComponentResources resources = newComponentResources();
+        ComponentResources containerResources = newComponentResources();
+        Messages componentMessages = newMessages();
+
+        Map<String, Validator> map = singletonMap("required", validator);
+
+        train_getConstraintType(validator, null);
+
+        train_getComponentResources(field, resources);
+        train_getId(resources, "fred");
+        train_getContainerResources(resources, containerResources);
+        train_getMessages(containerResources, componentMessages);
+        train_contains(componentMessages, "fred-required", true);
+
+        train_getMessageFormatter(componentMessages, "fred-required", formatter);
+
+        validator.check(field, null, formatter, inputValue);
 
         replay();
 
@@ -115,20 +173,31 @@
         Validator validator = newValidator();
         TypeCoercer coercer = newTypeCoercer();
         FieldComponent field = newFieldComponent();
-        Messages messages = newMock(Messages.class);
+        Messages messages = newMessages();
+        MessageFormatter formatter = newMessageFormatter();
         Object inputValue = new Object();
         ComponentResources resources = newComponentResources();
+        ComponentResources containerResources = newComponentResources();
+        Messages componentMessages = newMessages();
 
         Map<String, Validator> map = singletonMap("required", validator);
 
         train_getConstraintType(validator, null);
 
         train_getComponentResources(field, resources);
+        train_getId(resources, "fred");
+        train_getContainerResources(resources, containerResources);
+        train_getMessages(containerResources, componentMessages);
+        train_contains(componentMessages, "fred-required", false);
+
         train_getLocale(resources, Locale.FRENCH);
 
         train_getValidationMessages(messagesSource, Locale.FRENCH, messages);
 
-        validator.check(field, null, messages, inputValue);
+        train_getMessageKey(validator, "key");
+        train_getMessageFormatter(messages, "key", formatter);
+
+        validator.check(field, null, formatter, inputValue);
 
         replay();
 
@@ -141,6 +210,21 @@
         verify();
     }
 
+    private void train_contains(Messages messages, String key, boolean result)
+    {
+        expect(messages.contains(key)).andReturn(result).atLeastOnce();
+    }
+
+    protected void train_getId(ComponentResources resources, String id)
+    {
+        expect(resources.getId()).andReturn(id).atLeastOnce();
+    }
+
+    protected final void train_getMessages(ComponentResources resources, Messages messages)
+    {
+        expect(resources.getMessages()).andReturn(messages).atLeastOnce();
+    }
+
     @SuppressWarnings("unchecked")
     @Test
     public void multiple_validators_via_specification() throws Exception
@@ -150,9 +234,13 @@
         Validator minLength = newValidator();
         TypeCoercer coercer = newTypeCoercer();
         FieldComponent field = newFieldComponent();
-        Messages messages = newMock(Messages.class);
+        Messages messages = newMessages();
+        MessageFormatter requiredFormatter = newMessageFormatter();
+        MessageFormatter minLengthFormatter = newMessageFormatter();
         Object inputValue = new Object();
         ComponentResources resources = newComponentResources();
+        ComponentResources containerResources = newComponentResources();
+        Messages componentMessages = newMessages();
         Integer fifteen = 15;
 
         Map<String, Validator> map = newMap();
@@ -164,14 +252,27 @@
         train_getConstraintType(minLength, Integer.class);
 
         train_getComponentResources(field, resources);
+        train_getId(resources, "fred");
+        train_getContainerResources(resources, containerResources);
+        train_getMessages(containerResources, componentMessages);
+        train_contains(componentMessages, "fred-required", false);
+
         train_getLocale(resources, Locale.FRENCH);
 
         train_getValidationMessages(messagesSource, Locale.FRENCH, messages);
 
+        train_getMessageKey(required, "required");
+        train_getMessageFormatter(messages, "required", requiredFormatter);
+
+        train_contains(componentMessages, "fred-minLength", false);
+
+        train_getMessageKey(minLength, "min-length");
+        train_getMessageFormatter(messages, "min-length", minLengthFormatter);
+
         train_coerce(coercer, "15", Integer.class, fifteen);
 
-        required.check(field, null, messages, inputValue);
-        minLength.check(field, fifteen, messages, inputValue);
+        required.check(field, null, requiredFormatter, inputValue);
+        minLength.check(field, fifteen, minLengthFormatter, inputValue);
 
         replay();
 
@@ -192,9 +293,12 @@
         Validator validator = newValidator();
         TypeCoercer coercer = newTypeCoercer();
         FieldComponent field = newFieldComponent();
-        Messages messages = newMock(Messages.class);
+        Messages messages = newMessages();
+        MessageFormatter formatter = newMessageFormatter();
         Object inputValue = new Object();
         ComponentResources resources = newComponentResources();
+        ComponentResources containerResources = newComponentResources();
+        Messages componentMessages = newMessages();
         Integer five = 5;
 
         Map<String, Validator> map = singletonMap("minLength", validator);
@@ -204,11 +308,19 @@
         train_coerce(coercer, "5", Integer.class, five);
 
         train_getComponentResources(field, resources);
+        train_getId(resources, "fred");
+        train_getContainerResources(resources, containerResources);
+        train_getMessages(containerResources, componentMessages);
+        train_contains(componentMessages, "fred-minLength", false);
+
         train_getLocale(resources, Locale.FRENCH);
 
         train_getValidationMessages(messagesSource, Locale.FRENCH, messages);
 
-        validator.check(field, five, messages, inputValue);
+        train_getMessageKey(validator, "key");
+        train_getMessageFormatter(messages, "key", formatter);
+
+        validator.check(field, five, formatter, inputValue);
 
         replay();
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/validator/RequiredTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/validator/RequiredTest.java?view=diff&rev=497845&r1=497844&r2=497845
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/validator/RequiredTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/validator/RequiredTest.java Fri Jan 19 07:39:41 2007
@@ -16,7 +16,7 @@
 
 import org.apache.tapestry.Field;
 import org.apache.tapestry.ValidationException;
-import org.apache.tapestry.ioc.Messages;
+import org.apache.tapestry.ioc.MessageFormatter;
 import org.apache.tapestry.test.TapestryTestCase;
 import org.testng.annotations.Test;
 
@@ -25,16 +25,16 @@
     @Test
     public void null_value()
     {
-        Messages messages = newMessages();
         Field field = newField("My Field");
+        MessageFormatter formatter = newMessageFormatter();
 
-        train_format(messages, "required", "{message}", "My Field");
+        train_format(formatter, "{message}", "My Field");
 
         replay();
 
         try
         {
-            new Required().check(field, null, messages, null);
+            new Required().check(field, null, formatter, null);
             unreachable();
         }
         catch (ValidationException ex)
@@ -48,16 +48,16 @@
     @Test
     public void blank_value()
     {
-        Messages messages = newMessages();
+        MessageFormatter formatter = newMessageFormatter();
         Field field = newField("My Field");
 
-        train_format(messages, "required", "{message}", "My Field");
+        train_format(formatter, "{message}", "My Field");
 
         replay();
 
         try
         {
-            new Required().check(field, null, messages, "");
+            new Required().check(field, null, formatter, "");
             unreachable();
         }
         catch (ValidationException ex)
@@ -71,19 +71,20 @@
     @Test
     public void non_blank_value() throws Exception
     {
-        Messages messages = newMessages();
+        MessageFormatter formatter = newMessageFormatter();
         Field field = newField();
 
         replay();
 
-        new Required().check(field, null, messages, "not null");
+        new Required().check(field, null, formatter, "not null");
 
         verify();
     }
 
-    protected void train_format(Messages messages, String key, String result, Object... arguments)
+    /** Have to put the result before the varargs. */
+    protected void train_format(MessageFormatter formatter, String result, Object... arguments)
     {
-        expect(messages.format(key, arguments)).andReturn(result);
+        expect(formatter.format(arguments)).andReturn(result);
     }
 
     protected final Field newField(String label)

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ValidForm.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ValidForm.properties?view=auto&rev=497845
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ValidForm.properties (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/ValidForm.properties Fri Jan 19 07:39:41 2007
@@ -0,0 +1 @@
+message-required=Please provide a detailed description of the incident.
\ No newline at end of file