You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2003/02/28 18:28:40 UTC

cvs commit: jakarta-tapestry/examples/Tutorial/src/tutorial/workbench/upload Upload.java

hlship      2003/02/28 09:28:40

  Modified:    examples/Tutorial/src/tutorial/portal Stocks.java
               junit/src/net/sf/tapestry/junit/valid TestingField.java
                        TestStringValidator.java TestNumberValidator.java
                        TestValidationDelegate.java
               web      new.html
               examples/Vlib/src/net/sf/tapestry/vlib/pages EditBook.java
                        EditProfile.java NewBook.java
               framework/src/net/sf/tapestry/form Hidden.java
               framework/src/net/sf/tapestry/valid DateValidator.java
                        ValidField.java IFieldTracking.java
                        EmailValidator.java BaseValidator.java
                        NumberValidator.java StringValidator.java
                        ValidatorException.java ValidationConstraint.java
                        IValidationDelegate.java FieldTracking.java
                        RenderString.java ValidationDelegate.java
               examples/Vlib/src/net/sf/tapestry/vlib Protected.java
               examples/Tutorial/src/tutorial/workbench/upload Upload.java
  Log:
  Rework IValidationDelegate to always track submitted input values, just in case a field is marked in error after it rewinds.
  
  Revision  Changes    Path
  1.14      +2 -2      jakarta-tapestry/examples/Tutorial/src/tutorial/portal/Stocks.java
  
  Index: Stocks.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/examples/Tutorial/src/tutorial/portal/Stocks.java,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- Stocks.java	17 Jan 2003 17:45:28 -0000	1.13
  +++ Stocks.java	28 Feb 2003 17:28:35 -0000	1.14
  @@ -136,7 +136,7 @@
           IFormComponent component = (IFormComponent) getComponent(id);
   
           delegate.setFormComponent(component);
  -        delegate.record(new RenderString(message), null, null);
  +        delegate.record(message, null);
       }
   
       public void addStock(IRequestCycle cycle)
  
  
  
  1.15      +5 -1      jakarta-tapestry/junit/src/net/sf/tapestry/junit/valid/TestingField.java
  
  Index: TestingField.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/junit/src/net/sf/tapestry/junit/valid/TestingField.java,v
  retrieving revision 1.14
  retrieving revision 1.15
  diff -u -r1.14 -r1.15
  --- TestingField.java	25 Feb 2003 22:53:36 -0000	1.14
  +++ TestingField.java	28 Feb 2003 17:28:35 -0000	1.15
  @@ -125,4 +125,8 @@
           return false;
       }
   
  +	public String toString()
  +	{
  +		return "TestingField[" + _displayName + "]";
  +	}
   }
  
  
  
  1.8       +148 -91   jakarta-tapestry/junit/src/net/sf/tapestry/junit/valid/TestStringValidator.java
  
  Index: TestStringValidator.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/junit/src/net/sf/tapestry/junit/valid/TestStringValidator.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- TestStringValidator.java	17 Jan 2003 17:41:35 -0000	1.7
  +++ TestStringValidator.java	28 Feb 2003 17:28:35 -0000	1.8
  @@ -55,6 +55,7 @@
   package net.sf.tapestry.junit.valid;
   
   import junit.framework.TestCase;
  +import net.sf.tapestry.junit.TapestryTestCase;
   import net.sf.tapestry.valid.StringValidator;
   import net.sf.tapestry.valid.ValidationConstraint;
   import net.sf.tapestry.valid.ValidatorException;
  @@ -68,96 +69,152 @@
    *
    **/
   
  -public class TestStringValidator extends TestCase
  +public class TestStringValidator extends TapestryTestCase
   {
  -	private StringValidator v = new StringValidator();
  +    private StringValidator v = new StringValidator();
  +
  +    public TestStringValidator(String name)
  +    {
  +        super(name);
  +    }
  +
  +    public void testToString()
  +    {
  +        String in = "Foo";
  +        String out = v.toString(new TestingField("myField"), in);
  +
  +        assertEquals("Result.", in, out);
  +    }
  +
  +    public void testToStringNull()
  +    {
  +        String out = v.toString(new TestingField("nullField"), null);
  +
  +        assertNull("Null expected.", out);
  +    }
  +
  +    public void testToObjectRequiredFail()
  +    {
  +        v.setRequired(true);
  +
  +        try
  +        {
  +            v.toObject(new TestingField("requiredField"), "");
  +
  +            fail("Exception expected.");
  +        }
  +        catch (ValidatorException ex)
  +        {
  +            assertEquals(ValidationConstraint.REQUIRED, ex.getConstraint());
  +        }
  +    }
  +
  +    public void testToObjectRequiredPass() throws ValidatorException
  +    {
  +        v.setRequired(true);
  +
  +        Object result = v.toObject(new TestingField("requiredField"), "stuff");
  +
  +        assertEquals("Result.", "stuff", result);
  +    }
  +
  +    public void testToObjectMinimumFail()
  +    {
  +        v.setMinimumLength(10);
  +
  +        try
  +        {
  +            v.toObject(new TestingField("minimum"), "short");
  +
  +            fail("Exception expected.");
  +        }
  +        catch (ValidatorException ex)
  +        {
  +            assertEquals(ValidationConstraint.MINIMUM_WIDTH, ex.getConstraint());
  +        }
  +    }
  +
  +    public void testToObjectMinimumPass() throws ValidatorException
  +    {
  +        v.setMinimumLength(10);
  +
  +        String in = "ambidexterous";
  +
  +        Object out = v.toObject(new TestingField("minimum"), in);
  +
  +        assertEquals("Result", in, out);
  +    }
  +
  +    /**
  +     *  An empty string is not subject to the minimum length constraint.
  +     * 
  +     **/
  +
  +    public void testToObjectMinimumNull() throws ValidatorException
  +    {
  +        v.setMinimumLength(10);
  +
  +        String in = "";
  +
  +        Object out = v.toObject(new TestingField("minimum"), in);
  +
  +        assertNull("Result", out);
  +    }
  +
  +    public void testOptional()
  +    {
  +        assertEquals(0, StringValidator.OPTIONAL.getMinimumLength());
  +        assertEquals(false, StringValidator.OPTIONAL.isClientScriptingEnabled());
  +        assertEquals(false, StringValidator.OPTIONAL.isRequired());
  +    }
  +
  +    public void testRequired()
  +    {
  +        assertEquals(0, StringValidator.REQUIRED.getMinimumLength());
  +        assertEquals(false, StringValidator.REQUIRED.isClientScriptingEnabled());
  +        assertEquals(true, StringValidator.REQUIRED.isRequired());
  +    }
  +
  +    public void testSetStaticMinimumLength()
  +    {
  +        try
  +        {
  +            StringValidator.OPTIONAL.setMinimumLength(1);
  +
  +            unreachable();
  +        }
  +        catch (UnsupportedOperationException ex)
  +        {
  +            checkException(ex, "Changes to property values are not allowed.");
  +        }
  +    }
  +
  +    public void testSetStaticClientScripting()
  +    {
  +        try
  +        {
  +            StringValidator.OPTIONAL.setClientScriptingEnabled(true);
  +
  +            unreachable();
  +        }
  +        catch (UnsupportedOperationException ex)
  +        {
  +            checkException(ex, "Changes to property values are not allowed.");
  +        }
  +    }
  +
  +    public void testSetRequired()
  +    {
  +        try
  +        {
  +            StringValidator.OPTIONAL.setRequired(true);
  +
  +            unreachable();
  +        }
  +        catch (UnsupportedOperationException ex)
  +        {
  +            checkException(ex, "Changes to property values are not allowed.");
  +        }
  +    }
   
  -	public TestStringValidator(String name)
  -	{
  -		super(name);
  -	}
  -
  -	public void testToString()
  -	{
  -		String in = "Foo";
  -		String out = v.toString(new TestingField("myField"), in);
  -
  -		assertEquals("Result.", in, out);
  -	}
  -
  -	public void testToStringNull()
  -	{
  -		String out = v.toString(new TestingField("nullField"), null);
  -
  -		assertNull("Null expected.", out);
  -	}
  -
  -	public void testToObjectRequiredFail()
  -	{
  -		v.setRequired(true);
  -
  -		try
  -		{
  -			v.toObject(new TestingField("requiredField"), "");
  -
  -			fail("Exception expected.");
  -		}
  -		catch (ValidatorException ex)
  -		{
  -			assertEquals(ValidationConstraint.REQUIRED, ex.getConstraint());
  -		}
  -	}
  -
  -	public void testToObjectRequiredPass() throws ValidatorException
  -	{
  -		v.setRequired(true);
  -
  -		Object result = v.toObject(new TestingField("requiredField"), "stuff");
  -
  -		assertEquals("Result.", "stuff", result);
  -	}
  -
  -	public void testToObjectMinimumFail()
  -	{
  -		v.setMinimumLength(10);
  -		
  -		try
  -		{
  -			v.toObject(new TestingField("minimum"), "short");
  -			
  -			fail("Exception expected.");
  -		}
  -		catch (ValidatorException ex)
  -		{
  -			assertEquals(ValidationConstraint.MINIMUM_WIDTH, ex.getConstraint());
  -		}
  -	}
  -	
  -	public void testToObjectMinimumPass()
  -	throws ValidatorException
  -	{
  -		v.setMinimumLength(10);
  -		
  -		String in = "ambidexterous";
  -		
  -		Object out = v.toObject(new TestingField("minimum"), in);
  -		
  -		assertEquals("Result", in, out);
  -	}
  -	
  -	/**
  -	 *  An empty string is not subject to the minimum length constraint.
  -	 * 
  -	 **/
  -	
  -	public void testToObjectMinimumNull()	throws ValidatorException
  -	{
  -		v.setMinimumLength(10);
  -		
  -		String in = "";
  -		
  -		Object out = v.toObject(new TestingField("minimum"), in);
  -		
  -		assertNull("Result", out);
  -	}	
   }
  
  
  
  1.7       +50 -4     jakarta-tapestry/junit/src/net/sf/tapestry/junit/valid/TestNumberValidator.java
  
  Index: TestNumberValidator.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/junit/src/net/sf/tapestry/junit/valid/TestNumberValidator.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- TestNumberValidator.java	25 Feb 2003 22:53:35 -0000	1.6
  +++ TestNumberValidator.java	28 Feb 2003 17:28:35 -0000	1.7
  @@ -57,9 +57,8 @@
   import java.math.BigDecimal;
   import java.math.BigInteger;
   
  -import junit.framework.TestCase;
  -
   import net.sf.tapestry.form.IFormComponent;
  +import net.sf.tapestry.junit.TapestryTestCase;
   import net.sf.tapestry.valid.NumberValidator;
   import net.sf.tapestry.valid.ValidatorException;
   
  @@ -72,7 +71,7 @@
    *
    **/
   
  -public class TestNumberValidator extends TestCase
  +public class TestNumberValidator extends TapestryTestCase
   {
       private NumberValidator v = new NumberValidator();
   
  @@ -122,6 +121,53 @@
       public void testDouble() throws ValidatorException
       {
           testPassThru("testDouble", Double.class, new Double(348348.484854848));
  +    }
  +
  +    public void testLong() throws ValidatorException
  +    {
  +        testPassThru("testLong", Long.class, new Long(37373218723l));
  +    }
  +
  +    public void testInRange() throws ValidatorException
  +    {
  +        v.setMinimum(new Integer(100));
  +        v.setMaximum(new Integer(200));
  +
  +        testPassThru("testInRange", Integer.class, new Integer(150));
  +    }
  +
  +    public void testUnderMinimum()
  +    {
  +        v.setMinimum(new Integer(100));
  +        v.setMaximum(new Integer(200));
  +
  +        try
  +        {
  +            testPassThru("testUnderMinimum", Integer.class, new Integer(50));
  +
  +            unreachable();
  +        }
  +        catch (ValidatorException ex)
  +        {
  +            checkException(ex, "testUnderMinimum must not be smaller than 100.");
  +        }
  +    }
  +
  +    public void testOverMaximum()
  +    {
  +        v.setMinimum(new Integer(100));
  +        v.setMaximum(new Integer(200));
  +
  +        try
  +        {
  +            testPassThru("testUnderMinimum", Integer.class, new Integer(250));
  +
  +            unreachable();
  +        }
  +        catch (ValidatorException ex)
  +        {
  +            checkException(ex, "testUnderMinimum must not be larger than 200.");
  +        }
       }
   
       public void testBigInteger() throws ValidatorException
  
  
  
  1.9       +191 -99   jakarta-tapestry/junit/src/net/sf/tapestry/junit/valid/TestValidationDelegate.java
  
  Index: TestValidationDelegate.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/junit/src/net/sf/tapestry/junit/valid/TestValidationDelegate.java,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- TestValidationDelegate.java	25 Feb 2003 22:53:36 -0000	1.8
  +++ TestValidationDelegate.java	28 Feb 2003 17:28:35 -0000	1.9
  @@ -77,135 +77,227 @@
   
   public class TestValidationDelegate extends TestCase
   {
  -	private ValidationDelegate d = new ValidationDelegate();
  +    private ValidationDelegate d = new ValidationDelegate();
   
  -	public TestValidationDelegate(String name)
  -	{
  -		super(name);
  -	}
  +    public TestValidationDelegate(String name)
  +    {
  +        super(name);
  +    }
   
  -	public void testHasErrorsEmpty()
  -	{
  -		assertEquals(false, d.getHasErrors());
  -	}
  +    public void testHasErrorsEmpty()
  +    {
  +        assertEquals(false, d.getHasErrors());
  +    }
   
  -	public void testFirstErrorEmpty()
  -	{
  -		assertNull(d.getFirstError());
  -	}
  +    public void testFirstErrorEmpty()
  +    {
  +        assertNull(d.getFirstError());
  +    }
   
  -	public void testInvalidInput()
  -	{
  -		IFormComponent f = new TestingField("testAdd");
  -		String errorMessage = "Need a bigger one.";
  +    public void testInvalidInput()
  +    {
  +        IFormComponent f = new TestingField("testAdd");
  +        String errorMessage = "Need a bigger one.";
   
  -		d.setFormComponent(f);
  -		d.record(
  -			new ValidatorException(
  -				errorMessage,
  -				ValidationConstraint.TOO_LARGE,
  -				"Bad Stuff"));
  +        d.setFormComponent(f);
  +        d.recordFieldInputValue("Bad Stuff");
  +        d.record(new ValidatorException(errorMessage, ValidationConstraint.TOO_LARGE));
   
  -		List fieldTracking = d.getFieldTracking();
  +        List fieldTracking = d.getFieldTracking();
   
  -		assertEquals(1, fieldTracking.size());
  +        assertEquals(1, fieldTracking.size());
   
  -		IFieldTracking t = (IFieldTracking) fieldTracking.get(0);
  +        IFieldTracking t = (IFieldTracking) fieldTracking.get(0);
   
  -		assertEquals(f, t.getFormComponent());
  -		checkRender(errorMessage, t);
  -		assertEquals("testAdd", t.getFieldName());
  -		assertEquals("Bad Stuff", t.getInvalidInput());
  -		assertEquals(ValidationConstraint.TOO_LARGE, t.getConstraint());
  +        assertEquals(f, t.getComponent());
  +        checkRender(errorMessage, t);
  +        assertEquals("testAdd", t.getFieldName());
  +        assertEquals("Bad Stuff", t.getInput());
  +        assertEquals(ValidationConstraint.TOO_LARGE, t.getConstraint());
   
  -		assertTrue(d.getHasErrors());
  -		assertEquals(errorMessage, ((RenderString)(d.getFirstError())).getString());
  -	}
  +        assertTrue(d.getHasErrors());
  +        assertEquals(errorMessage, ((RenderString) (d.getFirstError())).getString());
  +    }
   
  -	private void checkRender(String errorMessage, IFieldTracking tracking)
  -	{
  -		IRender render = tracking.getRenderer();
  +    public void testValidatorErrorRenderer()
  +    {
  +        IFormComponent f = new TestingField("testValidatorErrorRenderer");
   
  -		assertEquals(errorMessage, ((RenderString) render).getString());
  -	}
  +        IRender errorRenderer = new RenderString("Just don't like it.");
   
  -	public void testMultipleInvalidInput()
  -	{
  -		IFormComponent f1 = new TestingField("input1");
  -		String e1 = "And now for something completely different.";
  -		IFormComponent f2 = new TestingField("input2");
  -		String e2 = "A man with three buttocks.";
  +        d.setFormComponent(f);
  +        d.recordFieldInputValue("Bad Stuff");
  +        d.record(
  +            new ValidatorException(
  +                "Just don't like it.",
  +                errorRenderer,
  +                ValidationConstraint.CONSISTENCY));
   
  -		d.setFormComponent(f1);
  -		d.record(new ValidatorException(e1, null, "Monty"));
  +        List fieldTracking = d.getFieldTracking();
   
  -		d.setFormComponent(f2);
  -		d.record(new ValidatorException(e2, null, "Python"));
  +        assertEquals(1, fieldTracking.size());
   
  -		List fieldTracking = d.getFieldTracking();
  -		assertEquals(2, fieldTracking.size());
  +        IFieldTracking t = (IFieldTracking) fieldTracking.get(0);
   
  -		IFieldTracking t = (IFieldTracking) fieldTracking.get(0);
  +        assertEquals(f, t.getComponent());
  +        assertEquals(errorRenderer, t.getErrorRenderer());
  +        assertEquals("testValidatorErrorRenderer", t.getFieldName());
  +        assertEquals("Bad Stuff", t.getInput());
  +        assertEquals(ValidationConstraint.CONSISTENCY, t.getConstraint());
   
  -		assertEquals(f1, t.getFormComponent());
  -		checkRender(e1, t);
  +        assertTrue(d.getHasErrors());
  +        assertEquals(errorRenderer, d.getFirstError());
  +    }
   
  -		t = (IFieldTracking) fieldTracking.get(1);
  -		assertEquals("Python", t.getInvalidInput());
  -		checkRender(e2, t);
  -		assertEquals(f2, t.getFormComponent());
  -	}
  +    public void testNoError()
  +    {
  +        IFormComponent f = new TestingField("testNoError");
   
  -	public void testReset()
  -	{
  -		IFormComponent f1 = new TestingField("input1");
  -		String e1 = "And now for something completely different.";
  -		IFormComponent f2 = new TestingField("input2");
  -		String e2 = "A man with three buttocks.";
  +        d.setFormComponent(f);
  +        d.recordFieldInputValue("Futurama");
   
  -		d.setFormComponent(f1);
  -		d.record(new ValidatorException(e1, null, "Monty"));
  +        List fieldTracking = d.getFieldTracking();
  +        assertEquals(1, fieldTracking.size());
   
  -		d.setFormComponent(f2);
  -		d.record(new ValidatorException(e2, null, "Python"));
  +        IFieldTracking t = (IFieldTracking) fieldTracking.get(0);
   
  -		// Now, wipe out info on f1
  +        assertEquals(f, t.getComponent());
  +        assertEquals(null, t.getErrorRenderer());
  +        assertEquals(false, t.isInError());
  +        assertEquals("Futurama", t.getInput());
  +        assertEquals(null, t.getConstraint());
   
  -		d.setFormComponent(f1);
  -		d.reset();
  +        assertEquals(false, d.getHasErrors());
  +        assertNull(d.getFirstError());
  +    }
   
  -		List fieldTracking = d.getFieldTracking();
  -		assertEquals(1, fieldTracking.size());
  +    public void testUnassociatedErrors()
  +    {
  +        IFormComponent f = new TestingField("testUnassociatedErrors");
   
  -		IFieldTracking t = (IFieldTracking) fieldTracking.get(0);
  -		assertEquals("Python", t.getInvalidInput());
  -		checkRender(e2, t);
  -		assertEquals(f2, t.getFormComponent());
  -	}
  +        d.setFormComponent(f);
  +        d.recordFieldInputValue("Bender");
   
  -	public void testResetAll()
  -	{
  -		IFormComponent f1 = new TestingField("input1");
  -		String e1 = "And now for something completely different.";
  -		IFormComponent f2 = new TestingField("input2");
  -		String e2 = "A man with three buttocks.";
  +        d.setFormComponent(null);
  +        d.record("Overload!", ValidationConstraint.CONSISTENCY);
   
  -		d.setFormComponent(f1);
  -		d.record(new ValidatorException(e1, null, "Monty"));
  +        assertEquals(true, d.getHasErrors());
   
  -		d.setFormComponent(f2);
  -		d.record(new ValidatorException(e2, null, "Python"));
  +        List fieldTracking = d.getFieldTracking();
  +        assertEquals(2, fieldTracking.size());
   
  -		d.setFormComponent(f1);
  -		d.reset();
  +        IFieldTracking t0 = (IFieldTracking) fieldTracking.get(0);
  +        assertEquals(false, t0.isInError());
  +        assertEquals(f, t0.getComponent());
   
  -		d.setFormComponent(f2);
  -		d.reset();
  +        IFieldTracking t1 = (IFieldTracking) fieldTracking.get(1);
  +        assertEquals(null, t1.getComponent());
  +        assertEquals(true, t1.isInError());
  +        checkRender("Overload!", t1);
   
  -		List fieldTracking = d.getFieldTracking();
  -		assertEquals(0, fieldTracking.size());
  +        checkRender("Overload!", d.getFirstError());
  +        
  +        List trackings = d.getUnassociatedTrackings();
  +        assertEquals(1, trackings.size());
  +        assertEquals(t1, trackings.get(0));
  +        
  +        trackings = d.getAssociatedTrackings();
  +        assertEquals(1, trackings.size());
  +        assertEquals(t0, trackings.get(0));
  +    }
   
  -		assertEquals(false, d.getHasErrors());
  -		assertNull(d.getFirstError());
  -	}
  +    private void checkRender(String errorMessage, IFieldTracking tracking)
  +    {
  +        IRender render = tracking.getErrorRenderer();
  +
  +        checkRender(errorMessage, render);
  +    }
  +
  +    private void checkRender(String errorMessage, IRender render)
  +    {
  +        assertEquals(errorMessage, ((RenderString) render).getString());
  +    }
  +
  +    public void testMultipleInvalidInput()
  +    {
  +        IFormComponent f1 = new TestingField("input1");
  +        String e1 = "And now for something completely different.";
  +        IFormComponent f2 = new TestingField("input2");
  +        String e2 = "A man with three buttocks.";
  +
  +        d.setFormComponent(f1);
  +        d.recordFieldInputValue("Monty");
  +        d.record(new ValidatorException(e1, null));
  +
  +        d.setFormComponent(f2);
  +        d.recordFieldInputValue("Python");
  +        d.record(new ValidatorException(e2, null));
  +
  +        List fieldTracking = d.getFieldTracking();
  +        assertEquals(2, fieldTracking.size());
  +
  +        IFieldTracking t = (IFieldTracking) fieldTracking.get(0);
  +
  +        assertEquals(f1, t.getComponent());
  +        checkRender(e1, t);
  +
  +        t = (IFieldTracking) fieldTracking.get(1);
  +        assertEquals("Python", t.getInput());
  +        checkRender(e2, t);
  +        assertEquals(f2, t.getComponent());
  +    }
  +
  +    public void testReset()
  +    {
  +        IFormComponent f1 = new TestingField("input1");
  +        String e1 = "And now for something completely different.";
  +        IFormComponent f2 = new TestingField("input2");
  +        String e2 = "A man with three buttocks.";
  +
  +        d.setFormComponent(f1);
  +        d.recordFieldInputValue("Monty");
  +        d.record(new ValidatorException(e1, null));
  +
  +        d.setFormComponent(f2);
  +        d.recordFieldInputValue("Python");
  +        d.record(new ValidatorException(e2, null));
  +
  +        // Now, wipe out info on f1
  +
  +        d.setFormComponent(f1);
  +        d.reset();
  +
  +        List fieldTracking = d.getFieldTracking();
  +        assertEquals(1, fieldTracking.size());
  +
  +        IFieldTracking t = (IFieldTracking) fieldTracking.get(0);
  +        assertEquals("Python", t.getInput());
  +        checkRender(e2, t);
  +        assertEquals(f2, t.getComponent());
  +    }
  +
  +    public void testResetAll()
  +    {
  +        IFormComponent f1 = new TestingField("input1");
  +        String e1 = "And now for something completely different.";
  +        IFormComponent f2 = new TestingField("input2");
  +        String e2 = "A man with three buttocks.";
  +
  +        d.setFormComponent(f1);
  +        d.record(new ValidatorException(e1, null));
  +
  +        d.setFormComponent(f2);
  +        d.record(new ValidatorException(e2, null));
  +
  +        d.setFormComponent(f1);
  +        d.reset();
  +
  +        d.setFormComponent(f2);
  +        d.reset();
  +
  +        assertEquals(null, d.getFieldTracking());
  +
  +        assertEquals(false, d.getHasErrors());
  +        assertNull(d.getFirstError());
  +    }
   }
  
  
  
  1.128     +6 -3      jakarta-tapestry/web/new.html
  
  Index: new.html
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/web/new.html,v
  retrieving revision 1.127
  retrieving revision 1.128
  diff -u -r1.127 -r1.128
  --- new.html	25 Feb 2003 22:52:53 -0000	1.127
  +++ new.html	28 Feb 2003 17:28:35 -0000	1.128
  @@ -16,8 +16,11 @@
   <ul>
   <li>Allow &lt;set-property&gt; element of specification to specify the expression as an attribute or
   as wrapped character data.</li>
  -<li>The interface for <a href="doc/api/net/sf/tapestry/valid/IValidationDelegate.html">ValidationDelegate</a>
  -changed incompatibly, to pass the component and validator to the rendering methods.</li>
  +<li>The interfaces for <a href="doc/api/net/sf/tapestry/valid/IValidationDelegate.html">IValidationDelegate</a>,
  + <a href="doc/api/net/sf/tapestry/valid/IValidator.html">IValidator</a> and
  +  <a href="doc/api/net/sf/tapestry/valid/ValidatorException.html">ValidatorException</a>
  +changed incompatibly.  This will only be an issue if you have created custom validation delegates or custom
  +validators.</li>
   <li>Added methods to <a href="doc/api/net/sf/tapestry/valid/IComponentStrings.html">IComponentStrings</a> for formatting localized strings
   with arguments.</li>
   </ul>		
  
  
  
  1.8       +7 -6      jakarta-tapestry/examples/Vlib/src/net/sf/tapestry/vlib/pages/EditBook.java
  
  Index: EditBook.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/examples/Vlib/src/net/sf/tapestry/vlib/pages/EditBook.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- EditBook.java	13 Jan 2003 03:33:18 -0000	1.7
  +++ EditBook.java	28 Feb 2003 17:28:36 -0000	1.8
  @@ -167,7 +167,10 @@
               }
               catch (RemoteException ex)
               {
  -                vengine.rmiFailure("Remote exception setting up page for book #" + bookPK + ".", ex, i > 0);
  +                vengine.rmiFailure(
  +                    "Remote exception setting up page for book #" + bookPK + ".",
  +                    ex,
  +                    i > 0);
               }
           }
   
  @@ -187,8 +190,7 @@
           {
               setErrorField(
                   "inputPublisherName",
  -                "Must provide a publisher name if the publisher option is empty.",
  -                null);
  +                "Must provide a publisher name if the publisher option is empty.");
               return;
           }
   
  @@ -196,8 +198,7 @@
           {
               setErrorField(
                   "inputPublisherName",
  -                "Must leave the publisher name blank if selecting a publisher from the list.",
  -                publisherName);
  +                "Must leave the publisher name blank if selecting a publisher from the list.");
               return;
           }
   
  
  
  
  1.7       +3 -3      jakarta-tapestry/examples/Vlib/src/net/sf/tapestry/vlib/pages/EditProfile.java
  
  Index: EditProfile.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/examples/Vlib/src/net/sf/tapestry/vlib/pages/EditProfile.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- EditProfile.java	13 Jan 2003 03:33:18 -0000	1.6
  +++ EditProfile.java	28 Feb 2003 17:28:36 -0000	1.7
  @@ -177,7 +177,7 @@
   
           if (Tapestry.isNull(password1) != Tapestry.isNull(password2))
           {
  -            setErrorField("inputPassword1", "Enter the password, then re-enter it to confirm.", null);
  +            setErrorField("inputPassword1", "Enter the password, then re-enter it to confirm.");
   
               return;
           }
  @@ -186,7 +186,7 @@
           {
               if (!password1.equals(password2))
               {
  -                setErrorField("inputPassword1", "Enter the same password in both fields.", null);
  +                setErrorField("inputPassword1", "Enter the same password in both fields.");
                   return;
               }
   
  
  
  
  1.6       +3 -5      jakarta-tapestry/examples/Vlib/src/net/sf/tapestry/vlib/pages/NewBook.java
  
  Index: NewBook.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/examples/Vlib/src/net/sf/tapestry/vlib/pages/NewBook.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- NewBook.java	13 Jan 2003 03:33:18 -0000	1.5
  +++ NewBook.java	28 Feb 2003 17:28:36 -0000	1.6
  @@ -116,8 +116,7 @@
           {
               setErrorField(
                   "inputPublisherName",
  -                "Must enter a publisher name or select an existing publisher from the list.",
  -                null);
  +                "Must enter a publisher name or select an existing publisher from the list.");
               return;
           }
   
  @@ -125,8 +124,7 @@
           {
               setErrorField(
                   "inputPublisherName",
  -                "Must either select an existing publisher or enter a new publisher name.",
  -                publisherName);
  +                "Must either select an existing publisher or enter a new publisher name.");
               return;
           }
   
  
  
  
  1.15      +4 -5      jakarta-tapestry/framework/src/net/sf/tapestry/form/Hidden.java
  
  Index: Hidden.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/net/sf/tapestry/form/Hidden.java,v
  retrieving revision 1.14
  retrieving revision 1.15
  diff -u -r1.14 -r1.15
  --- Hidden.java	19 Feb 2003 23:22:11 -0000	1.14
  +++ Hidden.java	28 Feb 2003 17:28:36 -0000	1.15
  @@ -83,7 +83,8 @@
           return _name;
       }
   
  -    protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle) throws RequestCycleException
  +    protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
  +        throws RequestCycleException
       {
           IForm form = getForm(cycle);
           boolean formRewound = form.isRewinding();
  @@ -148,7 +149,7 @@
   
           getValueBinding().setObject(value);
   
  -IActionListener listener = getListener();
  +        IActionListener listener = getListener();
   
           if (listener != null)
               listener.actionTriggered(this, cycle);
  @@ -163,9 +164,7 @@
   
       public abstract IActionListener getListener();
   
  -
       public abstract IBinding getValueBinding();
  -
   
       /**
        * 
  
  
  
  1.11      +4 -4      jakarta-tapestry/framework/src/net/sf/tapestry/valid/DateValidator.java
  
  Index: DateValidator.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/net/sf/tapestry/valid/DateValidator.java,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- DateValidator.java	25 Feb 2003 22:53:31 -0000	1.10
  +++ DateValidator.java	28 Feb 2003 17:28:36 -0000	1.11
  @@ -193,7 +193,7 @@
                       field.getDisplayName(),
                       getEffectiveDisplayFormat());
   
  -            throw new ValidatorException(errorMessage, ValidationConstraint.DATE_FORMAT, value);
  +            throw new ValidatorException(errorMessage, ValidationConstraint.DATE_FORMAT);
           }
   
           // OK, check that the date is in range.
  @@ -207,7 +207,7 @@
                       field.getDisplayName(),
                       format.format(_minimum));
   
  -            throw new ValidatorException(errorMessage, ValidationConstraint.TOO_SMALL, value);
  +            throw new ValidatorException(errorMessage, ValidationConstraint.TOO_SMALL);
           }
   
           if (_maximum != null && _maximum.compareTo(result) < 0)
  @@ -219,7 +219,7 @@
                       field.getDisplayName(),
                       format.format(_maximum));
   
  -            throw new ValidatorException(errorMessage, ValidationConstraint.TOO_LARGE, value);
  +            throw new ValidatorException(errorMessage, ValidationConstraint.TOO_LARGE);
           }
   
           return result;
  
  
  
  1.22      +4 -2      jakarta-tapestry/framework/src/net/sf/tapestry/valid/ValidField.java
  
  Index: ValidField.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/net/sf/tapestry/valid/ValidField.java,v
  retrieving revision 1.21
  retrieving revision 1.22
  diff -u -r1.21 -r1.22
  --- ValidField.java	25 Feb 2003 22:53:31 -0000	1.21
  +++ ValidField.java	28 Feb 2003 17:28:36 -0000	1.22
  @@ -217,7 +217,7 @@
           IValidationDelegate delegate = getForm().getDelegate();
   
           if (delegate.isInError())
  -            return delegate.getInvalidInput();
  +            return delegate.getFieldInputValue();
   
           Object value = getValueBinding().getObject();
           String result = getValidator().toString(this, value);
  @@ -232,6 +232,8 @@
       {
           Object objectValue = null;
           IValidationDelegate delegate = getForm().getDelegate();
  +
  +		delegate.recordFieldInputValue(value);
   
           try
           {
  
  
  
  1.7       +20 -28    jakarta-tapestry/framework/src/net/sf/tapestry/valid/IFieldTracking.java
  
  Index: IFieldTracking.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/net/sf/tapestry/valid/IFieldTracking.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- IFieldTracking.java	13 Jan 2003 03:33:37 -0000	1.6
  +++ IFieldTracking.java	28 Feb 2003 17:28:37 -0000	1.7
  @@ -58,19 +58,16 @@
   import net.sf.tapestry.form.IFormComponent;
   
   /**
  - *  Defines the interface for an object that tracks validation errors.  This 
  + *  Defines the interface for an object that tracks input fields.  This 
    *  interface is now poorly named, in that it tracks errors that may <em>not</em>
    *  be associated with a specific field.
    * 
  - *  <p>The initial release (1.0.8) stored an error <em>message</em>.  Starting in
  - *  release 1.0.9, this was changed to an error <em>renderrer</em>.  This increases
  - *  the complexity slightly, but allows for much, much greater flexibility in how
  - *  errors are ultimately presented to the user.  For example, you could devote part
  - *  of a template to a {@link net.sf.tapestry.components.Block} 
  - *  that contained a detail error message and links
  - *  to other parts of the application (for example, perhaps a pop-up help message).
  - * 
  - *  <p>However, in most cases, the renderrer will simply be a wrapper 
  + *  <p>For each field, a flag is stored indicating if the field is, in fact, in error.
  + *  The input supplied by the client is stored so that if the form is re-rendered
  + *  (as is typically done when there are input errors), the value entered by the user
  + *  is displayed back to the user.  An error message renderer is stored; this is
  + *  an object that can render the error message (it is usually
  + *  a {@link net.sf.tapestry.valid.RenderString} wrapper around a simple string).
    *
    *  @author Howard Lewis Ship
    *  @version $Id$
  @@ -80,13 +77,22 @@
   
   public interface IFieldTracking
   {
  +	/**
  +	 *  Returns true if the field is in error (that is,
  +	 *  if it has an error message {@link #getRenderer() renderer}.
  +	 * 
  +	 **/
  +	
  +	public boolean isInError();
  +	
  +	
       /**
        *  Returns the field component.  This may return null if the error
        *  is not associated with any particular field.
        * 
        **/
   
  -    public IFormComponent getFormComponent();
  +    public IFormComponent getComponent();
   
       /**
        *  Returns an object that will render the error message.
  @@ -95,18 +101,8 @@
        * 
        **/
   
  -    public IRender getRenderer();
  +    public IRender getErrorRenderer();
   
  -    /**
  -     *  Sets the error renderrer, the object that will render the error message.
  -     *  Typically, this is just a {@link RenderString}, but it could be a component
  -     *  or virtually anything.
  -     * 
  -     *  @since 1.0.9
  -     * 
  -     **/
  -
  -    public void setRenderer(IRender value);
   
       /**
        *  Returns the invalid input recorded for the field.  This is stored
  @@ -115,9 +111,7 @@
        * 
        **/
   
  -    public String getInvalidInput();
  -
  -    public void setInvalidInput(String value);
  +    public String getInput();
   
       /**
        *  Returns the name of the field, that is, the name assigned by the form
  @@ -135,6 +129,4 @@
        **/
   
       public ValidationConstraint getConstraint();
  -
  -    public void setConstraint(ValidationConstraint value);
   }
  
  
  
  1.5       +3 -3      jakarta-tapestry/framework/src/net/sf/tapestry/valid/EmailValidator.java
  
  Index: EmailValidator.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/net/sf/tapestry/valid/EmailValidator.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- EmailValidator.java	25 Feb 2003 22:53:31 -0000	1.4
  +++ EmailValidator.java	28 Feb 2003 17:28:37 -0000	1.5
  @@ -114,7 +114,7 @@
                       Integer.toString(_minimumLength),
                       field.getDisplayName());
   
  -            throw new ValidatorException(errorMessage, ValidationConstraint.MINIMUM_WIDTH, input);
  +            throw new ValidatorException(errorMessage, ValidationConstraint.MINIMUM_WIDTH);
           }
   
           if (input.length() > 0 && !isValidEmail(input))
  @@ -125,7 +125,7 @@
                       field.getPage().getLocale(),
                       field.getDisplayName());
   
  -            throw new ValidatorException(errorMessage, ValidationConstraint.EMAIL_FORMAT, input);
  +            throw new ValidatorException(errorMessage, ValidationConstraint.EMAIL_FORMAT);
           }
   
           return input;
  
  
  
  1.18      +2 -3      jakarta-tapestry/framework/src/net/sf/tapestry/valid/BaseValidator.java
  
  Index: BaseValidator.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/net/sf/tapestry/valid/BaseValidator.java,v
  retrieving revision 1.17
  retrieving revision 1.18
  diff -u -r1.17 -r1.18
  --- BaseValidator.java	25 Feb 2003 22:53:31 -0000	1.17
  +++ BaseValidator.java	28 Feb 2003 17:28:37 -0000	1.18
  @@ -232,8 +232,7 @@
           if (_required && isNull)
               throw new ValidatorException(
                   getString("field-is-required", field.getPage().getLocale(), field.getDisplayName()),
  -                ValidationConstraint.REQUIRED,
  -                null);
  +                ValidationConstraint.REQUIRED);
   
           return isNull;
       }
  
  
  
  1.14      +25 -25    jakarta-tapestry/framework/src/net/sf/tapestry/valid/NumberValidator.java
  
  Index: NumberValidator.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/net/sf/tapestry/valid/NumberValidator.java,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- NumberValidator.java	25 Feb 2003 22:53:31 -0000	1.13
  +++ NumberValidator.java	28 Feb 2003 17:28:37 -0000	1.14
  @@ -112,7 +112,7 @@
       }
   
       private Class _valueTypeClass = int.class;
  -    
  +
       private boolean _zeroIsNull;
       private Number _minimum;
       private Number _maximum;
  @@ -279,7 +279,7 @@
                       field.getPage().getLocale(),
                       field.getDisplayName());
   
  -            throw new ValidatorException(errorMessage, ValidationConstraint.NUMBER_FORMAT, value);
  +            throw new ValidatorException(errorMessage, ValidationConstraint.NUMBER_FORMAT);
           }
   
           if (_minimum != null && adaptor.compare(result, _minimum) < 0)
  @@ -291,7 +291,7 @@
                       field.getDisplayName(),
                       _minimum);
   
  -            throw new ValidatorException(errorMessage, ValidationConstraint.TOO_SMALL, value);
  +            throw new ValidatorException(errorMessage, ValidationConstraint.TOO_SMALL);
           }
   
           if (_maximum != null && adaptor.compare(result, _maximum) > 0)
  @@ -303,7 +303,7 @@
                       field.getDisplayName(),
                       _maximum);
   
  -            throw new ValidatorException(errorMessage, ValidationConstraint.TOO_LARGE, value);
  +            throw new ValidatorException(errorMessage, ValidationConstraint.TOO_LARGE);
           }
   
           return result;
  @@ -446,27 +446,27 @@
               throw new ApplicationRuntimeException(
                   Tapestry.getString("NumberValidator.unknown-type", typeName));
   
  -		_valueTypeClass = typeClass;
  +        _valueTypeClass = typeClass;
  +    }
  +
  +    /** @since 2.4 **/
  +
  +    public void setValueTypeClass(Class valueTypeClass)
  +    {
  +        _valueTypeClass = valueTypeClass;
       }
   
  -	/** @since 2.4 **/
  -	
  -	public void setValueTypeClass(Class valueTypeClass)
  -	{
  -		_valueTypeClass = valueTypeClass;
  -	}
  -	
  -	/** 
  -	 *  
  -	 *  Returns the value type to convert strings back into.  The default is int.
  -	 * 
  -	 *  @since 2.4 
  -	 * 
  -	 **/
  -	
  -	public Class getValueTypeClass()
  -	{
  -		return _valueTypeClass;
  -	}
  +    /** 
  +     *  
  +     *  Returns the value type to convert strings back into.  The default is int.
  +     * 
  +     *  @since 2.4 
  +     * 
  +     **/
  +
  +    public Class getValueTypeClass()
  +    {
  +        return _valueTypeClass;
  +    }
   
   }
  
  
  
  1.12      +21 -13    jakarta-tapestry/framework/src/net/sf/tapestry/valid/StringValidator.java
  
  Index: StringValidator.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/net/sf/tapestry/valid/StringValidator.java,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- StringValidator.java	25 Feb 2003 22:53:31 -0000	1.11
  +++ StringValidator.java	28 Feb 2003 17:28:37 -0000	1.12
  @@ -79,7 +79,8 @@
   {
       private static final class StaticStringValidator extends StringValidator
       {
  -        private static final String UNSUPPORTED_MESSAGE = "Changes to property values are not allowed.";
  +        private static final String UNSUPPORTED_MESSAGE =
  +            "Changes to property values are not allowed.";
   
           private StaticStringValidator(boolean required)
           {
  @@ -99,14 +100,14 @@
           {
               throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
           }
  -        
  +
           /** @throws UnsupportedOperationException **/
  -                
  +
           public void setClientScriptingEnabled(boolean clientScriptingEnabled)
           {
               throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
           }
  -   }
  +    }
   
       /**
        *  Returns a shared instance of a StringValidator with the required flag set.
  @@ -127,7 +128,7 @@
       private int _minimumLength;
   
       /** @since 2.2 **/
  -    
  +
       private String _scriptPath = "/net/sf/tapestry/valid/StringValidator.script";
   
       public StringValidator()
  @@ -161,7 +162,7 @@
                       Integer.toString(_minimumLength),
                       field.getDisplayName());
   
  -            throw new ValidatorException(errorMessage, ValidationConstraint.MINIMUM_WIDTH, input);
  +            throw new ValidatorException(errorMessage, ValidationConstraint.MINIMUM_WIDTH);
           }
   
           return input;
  @@ -183,7 +184,10 @@
        * 
        **/
   
  -    public void renderValidatorContribution(IFormComponent field, IMarkupWriter writer, IRequestCycle cycle)
  +    public void renderValidatorContribution(
  +        IFormComponent field,
  +        IMarkupWriter writer,
  +        IRequestCycle cycle)
           throws RequestCycleException
       {
           if (!isClientScriptingEnabled())
  @@ -203,16 +207,20 @@
           if (_minimumLength > 0)
               symbols.put(
                   "minimumLengthMessage",
  -                getString("field-too-short", locale, Integer.toString(_minimumLength), displayName));
  +                getString(
  +                    "field-too-short",
  +                    locale,
  +                    Integer.toString(_minimumLength),
  +                    displayName));
   
           processValidatorScript(_scriptPath, cycle, field, symbols);
       }
  -    
  +
       /**
        *  @since 2.2
        * 
        **/
  -    
  +
       public String getScriptPath()
       {
           return _scriptPath;
  @@ -227,10 +235,10 @@
        *  @since 2.2
        * 
        **/
  -    
  +
       public void setScriptPath(String scriptPath)
       {
           _scriptPath = scriptPath;
  -    }    
  +    }
   
   }
  
  
  
  1.7       +23 -15    jakarta-tapestry/framework/src/net/sf/tapestry/valid/ValidatorException.java
  
  Index: ValidatorException.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/net/sf/tapestry/valid/ValidatorException.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- ValidatorException.java	13 Jan 2003 03:33:37 -0000	1.6
  +++ ValidatorException.java	28 Feb 2003 17:28:37 -0000	1.7
  @@ -54,6 +54,8 @@
    */
   package net.sf.tapestry.valid;
   
  +import net.sf.tapestry.IRender;
  +
   /**
    *  Thrown by a {@link IValidator} when submitted input is not valid.
    *
  @@ -65,43 +67,49 @@
   
   public class ValidatorException extends Exception
   {
  -    private ValidationConstraint constraint;
  -    private String invalidInput;
  +    private IRender _errorRenderer;
  +    private ValidationConstraint _constraint;
   
       public ValidatorException(String errorMessage)
       {
  -        super(errorMessage);
  +        this(errorMessage, null, null);
  +    }
  +
  +    public ValidatorException(String errorMessage, ValidationConstraint constraint)
  +    {
  +        this(errorMessage, null, constraint);
       }
   
       /**
        *  Creates a new instance.
        *  @param errorMessage the default error message to be used (this may be
  -     *  overriden by the {@link IValidationDelegate}
  +     *  overriden by the {@link IValidationDelegate})
  +     *  @param renderer to use to render the error message (may be null) 
        *  @param constraint a validation constraint that has been compromised, or
        *  null if no constraint is applicable
  -     *  @param invalidInput the input received by the component that was invalid; this
  -     *  is stored during the request cycle and will be used as the default value
  -     *  for the input field during the page render
  +     * 
        **/
   
       public ValidatorException(
           String errorMessage,
  -        ValidationConstraint constraint,
  -        String invalidInput)
  +        IRender errorRenderer,
  +        ValidationConstraint constraint)
       {
           super(errorMessage);
   
  -        this.constraint = constraint;
  -        this.invalidInput = invalidInput;
  +        _errorRenderer = errorRenderer;
  +        _constraint = constraint;
       }
   
       public ValidationConstraint getConstraint()
       {
  -        return constraint;
  +        return _constraint;
       }
   
  -    public String getInvalidInput()
  +    /** @since 2.4 **/
  +
  +    public IRender getErrorRenderer()
       {
  -        return invalidInput;
  +        return _errorRenderer;
       }
   }
  
  
  
  1.9       +14 -9     jakarta-tapestry/framework/src/net/sf/tapestry/valid/ValidationConstraint.java
  
  Index: ValidationConstraint.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/net/sf/tapestry/valid/ValidationConstraint.java,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- ValidationConstraint.java	13 Jan 2003 03:33:37 -0000	1.8
  +++ ValidationConstraint.java	28 Feb 2003 17:28:37 -0000	1.9
  @@ -73,8 +73,7 @@
        *
        **/
   
  -    public static final ValidationConstraint REQUIRED =
  -        new ValidationConstraint("REQUIRED");
  +    public static final ValidationConstraint REQUIRED = new ValidationConstraint("REQUIRED");
   
       /**
        *  Indicates that a non-null value was provided, but that (after removing
  @@ -90,8 +89,7 @@
        *
        **/
   
  -    public static final ValidationConstraint DATE_FORMAT =
  -        new ValidationConstraint("DATE_FORMAT");
  +    public static final ValidationConstraint DATE_FORMAT = new ValidationConstraint("DATE_FORMAT");
   
       /**
        *  Indicates a general error in the format of a string that is
  @@ -116,16 +114,23 @@
        *
        **/
   
  -    public static final ValidationConstraint TOO_SMALL =
  -        new ValidationConstraint("TOO_SMALL");
  +    public static final ValidationConstraint TOO_SMALL = new ValidationConstraint("TOO_SMALL");
   
       /**
        *  Indicates that the value was too large (for a Date, too late).
        *
        **/
   
  -    public static final ValidationConstraint TOO_LARGE =
  -        new ValidationConstraint("TOO_LARGE");
  +    public static final ValidationConstraint TOO_LARGE = new ValidationConstraint("TOO_LARGE");
  +
  +    /**
  +     *  Indicates a consistency error, usually between too different fields.
  +     * 
  +     *  @since 2.4
  +     * 
  +     **/
  +
  +    public static final ValidationConstraint CONSISTENCY = new ValidationConstraint("CONSISTENCY");
   
       /**
        *  Protected constructor, which allows new constraints to be created
  
  
  
  1.9       +44 -20    jakarta-tapestry/framework/src/net/sf/tapestry/valid/IValidationDelegate.java
  
  Index: IValidationDelegate.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/net/sf/tapestry/valid/IValidationDelegate.java,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- IValidationDelegate.java	25 Feb 2003 22:53:31 -0000	1.8
  +++ IValidationDelegate.java	28 Feb 2003 17:28:37 -0000	1.9
  @@ -89,7 +89,7 @@
    *  For most simple forms, these terms are pretty much synonymous.
    *  Your form will render normally, and each form component will render
    *  only once.  Some of your form components will by {@link ValidField}
  - *  components (which implement {@link IField}) and handle most of
  + *  components and handle most of
    *  their validation internally (with the help of {@link IValidator} objects).
    *  In addition, your form listener may do additional validation and notify
    *  the validation delegate of additional errors, some of which
  @@ -108,10 +108,11 @@
    *  <p><b>The Exception</b><br>
    *  The problem is that a component doesn't know its field name until its
    *  <code>render()</code> method is invoked (at which point, it allocates a unique field
  - *  name from the {@link net.sf.tapestry.form.Form}.  This is not a problem for the field or its
  + *  name from the {@link net.sf.tapestry.IForm#getElementId(IComponent)}. 
  + *  This is not a problem for the field or its
    *  {@link IValidator}, but screws things up for the {@link FieldLabel}.
    * 
  - *  <p>Typically, the label is renderred <em>before</em> the corresponding form component.
  + *  <p>Typically, the label is rendered <em>before</em> the corresponding form component.
    *  Form components leave their last assigned field name in their
    *  {@link IFormComponent#getName() name property}.  So if the form component is in any kind of
    *  loop, the {@link FieldLabel} will key its name, 
  @@ -129,10 +130,12 @@
   {
       /**
        *  Invoked before other methods to configure the delegate for the given 
  -     *  form component.
  +     *  form component.  Sets the current field based on
  +     *  the {@link IFormComponent#getName() name} of the form component
  +     *  (which is almost always a {@link ValidField}).
        * 
        *  <p>The caller should invoke this with a parameter of null to record
  -     *  global errors (errors not associated with any particular field).
  +     *  unassociated global errors (errors not associated with any particular field).
        * 
        *  @since 1.0.8
        *
  @@ -151,13 +154,14 @@
       public boolean isInError();
   
       /**
  -     *  Returns the invalid string submitted by the client.
  +     *  Returns the string submitted by the client as the value for
  +     *  the current field.
        * 
  -     * @since 1.0.8
  +     *  @since 1.0.8
        * 
        **/
   
  -    public String getInvalidInput();
  +    public String getFieldInputValue();
   
       /**
        *  Returns a {@link List} of {@link IFieldTracking}, in default order
  @@ -191,41 +195,53 @@
       public void clear();
   
       /**
  +     *  Records the user's input for the current form component.  Input should
  +     *  be recorded even if there isn't an explicit error, since later form-wide
  +     *  validations may discover an error in the field.
  +     * 
  +     *  @since 2.4
  +     * 
  +     **/
  +
  +    public void recordFieldInputValue(String input);
  +
  +    /**
        *  The error notification method, invoked during the rewind phase
        *  (that is, while HTTP parameters are being extracted from the request
        *  and assigned to various object properties).  
        *
        *  <p>Typically, the delegate simply invokes
  -     *  {@link #record(String, ValidationConstraint, String)}, but special
  +     *  {@link #record(String, ValidationConstraint)} or
  +     *  {@link #record(IRender, ValidationConstraint)}, but special
        *  delegates may override this behavior to provide (in some cases)
        *  different error messages or more complicated error renderers.
  +     * 
        **/
   
       public void record(ValidatorException ex);
   
       /**
  -     *  Records an error in the current component, or an unassociated error.
  +     *  Records an error in the current field, or an unassociated error
  +     *  if there is no current field.
        *  
        *  @param message message to display (@see RenderString}
  -     *  @param constraint the constraint that was violated
  -     *  @param invalidInput the input provided by the user
  +     *  @param constraint the constraint that was violated, or null if not known
        * 
        *  @since 1.0.9
        **/
   
  -    public void record(String message, ValidationConstraint constraint, String invalidInput);
  +    public void record(String message, ValidationConstraint constraint);
   
       /**
  -     *  Records an error in the current component, or an unassociated error..
  +     *  Records an error in the current component, or an unassociated error.
        *  The maximum flexibility recorder.
        *  
        *  @param errorRenderer object that will render the error message (@see RenderString}
  -     *  @param constraint the constraint that was violated
  -     *  @param invalidInput the input provided by the user
  +     *  @param constraint the constraint that was violated, or null if not known
        * 
        **/
   
  -    public void record(IRender errorRenderer, ValidationConstraint constraint, String invalidInput);
  +    public void record(IRender errorRenderer, ValidationConstraint constraint);
   
       /**
        *  Invoked before the field is rendered.  If the field is in error,
  @@ -250,7 +266,11 @@
        *  @since 1.0.5
        **/
   
  -    public void writeAttributes(IMarkupWriter writer, IRequestCycle cycle, IFormComponent component, IValidator validator)
  +    public void writeAttributes(
  +        IMarkupWriter writer,
  +        IRequestCycle cycle,
  +        IFormComponent component,
  +        IValidator validator)
           throws RequestCycleException;
   
       /**
  @@ -259,7 +279,11 @@
        *
        **/
   
  -    public void writeSuffix(IMarkupWriter writer, IRequestCycle cycle, IFormComponent component, IValidator validator)
  +    public void writeSuffix(
  +        IMarkupWriter writer,
  +        IRequestCycle cycle,
  +        IFormComponent component,
  +        IValidator validator)
           throws RequestCycleException;
   
       /**
  
  
  
  1.7       +41 -22    jakarta-tapestry/framework/src/net/sf/tapestry/valid/FieldTracking.java
  
  Index: FieldTracking.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/net/sf/tapestry/valid/FieldTracking.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- FieldTracking.java	13 Jan 2003 03:33:37 -0000	1.6
  +++ FieldTracking.java	28 Feb 2003 17:28:37 -0000	1.7
  @@ -68,60 +68,79 @@
   
   public class FieldTracking implements IFieldTracking
   {
  -    private IFormComponent component;
  -    private String invalidInput;
  -    private IRender renderer;
  -    private String fieldName;
  -    private ValidationConstraint constraint;
  -
  +    private IFormComponent _component;
  +    private String _input;
  +    private IRender _renderer;
  +    private String _fieldName;
  +    private ValidationConstraint _constraint;
  +
  +	/**
  +	 *  Constructor used for unassociated errors; errors that are not about any particular
  +	 *  field within the form.
  +	 * 
  +	 **/
  +	
       FieldTracking()
       {
       }
   
  +	/**
  +	 *  Standard constructor for a field (with the given name), rendered
  +	 *  by the specified component.
  +	 * 
  +	 **/
  +	
       FieldTracking(String fieldName, IFormComponent component)
       {
  -        this.fieldName = fieldName;
  -        this.component = component;
  +        _fieldName = fieldName;
  +        _component = component;
       }
   
  -    public IFormComponent getFormComponent()
  +    public IFormComponent getComponent()
       {
  -        return component;
  +        return _component;
       }
   
  -    public IRender getRenderer()
  +    public IRender getErrorRenderer()
       {
  -        return renderer;
  +        return _renderer;
       }
   
  -    public void setRenderer(IRender value)
  +    public void setErrorRenderer(IRender value)
       {
  -        renderer = value;
  +        _renderer = value;
       }
   
  -    public String getInvalidInput()
  +    public String getInput()
       {
  -        return invalidInput;
  +        return _input;
       }
   
  -    public void setInvalidInput(String value)
  +    public void setInput(String value)
       {
  -        invalidInput = value;
  +        _input = value;
       }
   
       public String getFieldName()
       {
  -        return fieldName;
  +        return _fieldName;
       }
   
       public ValidationConstraint getConstraint()
       {
  -        return constraint;
  +        return _constraint;
       }
   
       public void setConstraint(ValidationConstraint constraint)
       {
  -        this.constraint = constraint;
  +        _constraint = constraint;
  +    }
  +
  +    /** @since 2.4 **/
  +
  +    public boolean isInError()
  +    {
  +        return _renderer != null;
       }
   
   }
  
  
  
  1.7       +59 -68    jakarta-tapestry/framework/src/net/sf/tapestry/valid/RenderString.java
  
  Index: RenderString.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/net/sf/tapestry/valid/RenderString.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- RenderString.java	13 Jan 2003 03:33:36 -0000	1.6
  +++ RenderString.java	28 Feb 2003 17:28:37 -0000	1.7
  @@ -58,6 +58,7 @@
   import net.sf.tapestry.IRender;
   import net.sf.tapestry.IRequestCycle;
   import net.sf.tapestry.RequestCycleException;
  +import org.apache.commons.lang.builder.ToStringBuilder;
   
   /**
    *  A wrapper around {@link String} that allows the String to
  @@ -71,72 +72,62 @@
   
   public class RenderString implements IRender
   {
  -	private String string;
  -	private boolean raw = false;
  -	
  -	public RenderString(String string)
  -	{
  -		this.string = string;
  -	}
  -	
  -	/**
  -	 *  @param string the string to render
  -	 *  @param raw if true, the String is rendered as-is, with no filtering.
  -	 *  If false (the default), the String is filtered.
  -	 *
  -	 **/
  -	
  -	public RenderString(String string, boolean raw)
  -	{
  -		this.string = string;
  -		this.raw = raw;
  -	}
  -
  -	/**
  -	 *  Renders the String to the writer.  Does nothing if the string is null.
  -	 *  If raw is true, uses {@link IMarkupWriter#printRaw(String)}, otherwise
  -	 *  {@link IMarkupWriter#print(String)}.
  -	 * 
  -	 *
  -	 **/
  -	
  -	public void render(IMarkupWriter writer, IRequestCycle cycle)
  -		throws RequestCycleException
  -	{
  -		if (string == null)
  -			return;
  -			
  -		if (raw)
  -			writer.printRaw(string);
  -		else
  -			writer.print(string);
  -	}
  -	
  -	public String getString()
  -	{
  -		return string;
  -	}
  -	
  -	public boolean isRaw()
  -	{
  -		return raw;
  -	}
  -	
  -	public String toString()
  -	{
  -		StringBuffer buffer = new StringBuffer("RenderString@");
  -		
  -		buffer.append(Integer.toHexString(hashCode()));
  -		buffer.append('[');
  -		buffer.append(string);
  -		
  -		if (raw)
  -		buffer.append(" (raw)");
  -		
  -		buffer.append(']');
  -		
  -		return buffer.toString();
  -	}
  -		
  +    private String _string;
  +    private boolean _raw = false;
   
  +    public RenderString(String string)
  +    {
  +        _string = string;
  +    }
  +
  +    /**
  +     *  @param string the string to render
  +     *  @param raw if true, the String is rendered as-is, with no filtering.
  +     *  If false (the default), the String is filtered.
  +     *
  +     **/
  +
  +    public RenderString(String string, boolean raw)
  +    {
  +        _string = string;
  +        _raw = raw;
  +    }
  +
  +    /**
  +     *  Renders the String to the writer.  Does nothing if the string is null.
  +     *  If raw is true, uses {@link IMarkupWriter#printRaw(String)}, otherwise
  +     *  {@link IMarkupWriter#print(String)}.
  +     * 
  +     *
  +     **/
  +
  +    public void render(IMarkupWriter writer, IRequestCycle cycle) throws RequestCycleException
  +    {
  +        if (_string == null)
  +            return;
  +
  +        if (_raw)
  +            writer.printRaw(_string);
  +        else
  +            writer.print(_string);
  +    }
  +
  +    public String getString()
  +    {
  +        return _string;
  +    }
  +
  +    public boolean isRaw()
  +    {
  +        return _raw;
  +    }
  +
  +    public String toString()
  +    {
  +        ToStringBuilder builder = new ToStringBuilder(this);
  +        builder.append("string", _string);
  +        builder.append("raw", _raw);
  +
  +        return builder.toString();
  +    }
   }
  
  
  
  1.11      +140 -93   jakarta-tapestry/framework/src/net/sf/tapestry/valid/ValidationDelegate.java
  
  Index: ValidationDelegate.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/net/sf/tapestry/valid/ValidationDelegate.java,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- ValidationDelegate.java	25 Feb 2003 22:53:31 -0000	1.10
  +++ ValidationDelegate.java	28 Feb 2003 17:28:37 -0000	1.11
  @@ -57,6 +57,7 @@
   import java.util.ArrayList;
   import java.util.Collections;
   import java.util.HashMap;
  +import java.util.Iterator;
   import java.util.List;
   import java.util.Map;
   
  @@ -64,8 +65,8 @@
   import net.sf.tapestry.IRender;
   import net.sf.tapestry.IRequestCycle;
   import net.sf.tapestry.RequestCycleException;
  +import net.sf.tapestry.Tapestry;
   import net.sf.tapestry.form.IFormComponent;
  -import net.sf.tapestry.util.pool.IPoolable;
   
   /**
    *  A base implementation of {@link IValidationDelegate} that can be used
  @@ -78,18 +79,18 @@
    * 
    **/
   
  -public class ValidationDelegate implements IValidationDelegate, IPoolable
  +public class ValidationDelegate implements IValidationDelegate
   {
       private IFormComponent _currentComponent;
       private List _trackings;
  -    
  +
       /**
        *  A Map of Maps, keyed on the name of the Form.  Each inner map contains
        *  the trackings for one form, keyed on component name.  Care must
        *  be taken, because the inner Map is not always present.
        * 
        **/
  -    
  +
       private Map _trackingMap;
   
       public void clear()
  @@ -99,17 +100,6 @@
           _trackingMap = null;
       }
   
  -    public void resetForPool()
  -    {
  -        _currentComponent = null;
  -
  -        if (_trackings != null)
  -            _trackings.clear();
  -
  -        if (_trackingMap != null)
  -            _trackingMap.clear();
  -    }
  -
       /**
        *  If the form component is in error, places a &lt;font color="red"&lt; around it.
        *  Note: this will only work on the render phase after a rewind, and will be
  @@ -150,33 +140,34 @@
   
       /**
        *  Returns the {@link IFieldTracking} for the current component, if any.
  -     *  The {@link IFieldTracking} is created in {@link #record(IRender, ValidationConstraint, String)}
  -     *  when an error is recorded for the component.
  +     *  The {@link IFieldTracking} is usually created in 
  +     *  {@link #recordInput(String)} or, failing that,
  +     *  in {@link #record(IRender, ValidationConstraint)}.
        * 
        *  <p>Components may be rendered multiple times, with multiple names (provided
        *  by the {@link net.sf.tapestry.form.Form}, care must be taken that this method is invoked
  -     *  <em>after</em> the Form has provided a unique name for the component.
  +     *  <em>after</em> the Form has provided a unique 
  +     *  {@link IFormComponent#getName()} for the component.
        * 
        *  @see #setFormComponent(IFormComponent)
        * 
  -     *  @return the {@link IFieldTracking}, or null if the field has no tracking
  -     *  (is not in error).
  +     *  @return the {@link FieldTracking}, or null if the field has no tracking.
        * 
        **/
   
  -    protected IFieldTracking getComponentTracking()
  +    protected FieldTracking getComponentTracking()
       {
           if (_trackingMap == null)
               return null;
  -    
  +
           String formName = _currentComponent.getForm().getName();
  -        
  -        Map formMap = (Map)_trackingMap.get(formName);
  -        
  +
  +        Map formMap = (Map) _trackingMap.get(formName);
  +
           if (formMap == null)
               return null;
  -        
  -        return (IFieldTracking) formMap.get(_currentComponent.getName());
  +
  +        return (FieldTracking) formMap.get(_currentComponent.getName());
       }
   
       public void setFormComponent(IFormComponent component)
  @@ -186,12 +177,16 @@
   
       public boolean isInError()
       {
  -        return getComponentTracking() != null;
  +        IFieldTracking tracking = getComponentTracking();
  +
  +        return tracking != null && tracking.isInError();
       }
   
  -    public String getInvalidInput()
  +    public String getFieldInputValue()
       {
  -        return getComponentTracking().getInvalidInput();
  +        IFieldTracking tracking = getComponentTracking();
  +
  +        return tracking == null ? null : tracking.getInput();
       }
   
       /**
  @@ -201,7 +196,7 @@
   
       public List getFieldTracking()
       {
  -        if (_trackings == null)
  +        if (Tapestry.size(_trackings) == 0)
               return null;
   
           return Collections.unmodifiableList(_trackings);
  @@ -214,39 +209,44 @@
           if (tracking != null)
           {
               _trackings.remove(tracking);
  -            
  -            String formName = tracking.getFormComponent().getForm().getName();
  -            
  -            Map formMap = (Map)_trackingMap.get(formName);
  -            
  +
  +            String formName = tracking.getComponent().getForm().getName();
  +
  +            Map formMap = (Map) _trackingMap.get(formName);
  +
               if (formMap != null)
                   formMap.remove(tracking.getFieldName());
           }
       }
   
       /**
  -     *  Invokes {@link #record(String, ValidationConstraint, String)}.
  +     *  Invokes {@link #record(String, ValidationConstraint)}, or
  +     *  {@link #record(IRender, ValidationConstraint)} if the 
  +     *  {@link ValidatorException#getErrorRenderer() error renderer property}
  +     *  is not null.
        * 
        **/
   
       public void record(ValidatorException ex)
       {
  -        record(ex.getMessage(), ex.getConstraint(), ex.getInvalidInput());
  +        IRender errorRenderer = ex.getErrorRenderer();
  +
  +        if (errorRenderer == null)
  +            record(ex.getMessage(), ex.getConstraint());
  +        else
  +            record(errorRenderer, ex.getConstraint());
       }
   
       /**
  -     *  Invokes {@link #record(IRender, ValidationConstraint, String)}, after
  +     *  Invokes {@link #record(IRender, ValidationConstraint)}, after
        *  wrapping the message parameter in a
        *  {@link RenderString}.
        * 
        **/
   
  -    public void record(
  -        String message,
  -        ValidationConstraint constraint,
  -        String invalidInput)
  +    public void record(String message, ValidationConstraint constraint)
       {
  -        record(new RenderString(message), constraint, invalidInput);
  +        record(new RenderString(message), constraint);
       }
   
       /**
  @@ -266,12 +266,37 @@
        *  @since 1.0.9
        **/
   
  -    public void record(
  -        IRender errorRenderer,
  -        ValidationConstraint constraint,
  -        String invalidInput)
  +    public void record(IRender errorRenderer, ValidationConstraint constraint)
  +    {
  +        FieldTracking tracking = findCurrentTracking();
  +
  +        // Note that recording two errors for the same field is not advised; the
  +        // second will override the first.
  +
  +        tracking.setErrorRenderer(errorRenderer);
  +        tracking.setConstraint(constraint);
  +    }
  +
  +    public void recordFieldInputValue(String input)
  +    {
  +        FieldTracking tracking = findCurrentTracking();
  +
  +        tracking.setInput(input);
  +    }
  +
  +    /**
  +     *  Finds or creates the field tracking for the
  +     *  {@link #setFormComponent(IFormComponent) current component.
  +     *  If no current component, an unassociated error is created
  +     *  and returned.
  +     * 
  +     *  @since 2.4
  +     * 
  +     **/
  +
  +    protected FieldTracking findCurrentTracking()
       {
  -        IFieldTracking tracking = null;
  +        FieldTracking result = null;
   
           if (_trackings == null)
               _trackings = new ArrayList();
  @@ -281,57 +306,82 @@
   
           if (_currentComponent == null)
           {
  -            tracking = new FieldTracking();
  +            result = new FieldTracking();
   
  -            // Add it to the *ahem* field trackings, but not to the
  +            // Add it to the field trackings, but not to the
               // map.
   
  -            _trackings.add(tracking);
  +            _trackings.add(result);
           }
           else
           {
  -            tracking = getComponentTracking();
  +            result = getComponentTracking();
   
  -            if (tracking == null)
  +            if (result == null)
               {
                   String formName = _currentComponent.getForm().getName();
  -                
  -                Map formMap = (Map)_trackingMap.get(formName);
  -                
  +
  +                Map formMap = (Map) _trackingMap.get(formName);
  +
                   if (formMap == null)
                   {
                       formMap = new HashMap();
                       _trackingMap.put(formName, formMap);
                   }
  -                
  +
                   String fieldName = _currentComponent.getName();
   
  -                tracking = new FieldTracking(fieldName, _currentComponent);
  +                result = new FieldTracking(fieldName, _currentComponent);
   
  -                _trackings.add(tracking);
  -                formMap.put(fieldName, tracking);
  +                _trackings.add(result);
  +                formMap.put(fieldName, result);
               }
           }
   
  -        // Note that recording two errors for the same field is not advised; the
  -        // second will override the first.
  -
  -        tracking.setInvalidInput(invalidInput);
  -        tracking.setRenderer(errorRenderer);
  -        tracking.setConstraint(constraint);
  +        return result;
       }
   
  -    public void writePrefix(IMarkupWriter writer, IRequestCycle cycle, IFormComponent component, IValidator validator)
  +    /**
  +     *  Does nothing.  Override in a subclass to decoreate
  +     *  fields.
  +     * 
  +     **/
  +
  +    public void writePrefix(
  +        IMarkupWriter writer,
  +        IRequestCycle cycle,
  +        IFormComponent component,
  +        IValidator validator)
           throws RequestCycleException
       {
       }
   
  -    public void writeAttributes(IMarkupWriter writer, IRequestCycle cycle, IFormComponent component, IValidator validator)
  +    /**
  +     *  Does nothing.  Override in a subclass to decorate fields.
  +     * 
  +     **/
  +
  +    public void writeAttributes(
  +        IMarkupWriter writer,
  +        IRequestCycle cycle,
  +        IFormComponent component,
  +        IValidator validator)
           throws RequestCycleException
       {
       }
   
  -    public void writeSuffix(IMarkupWriter writer, IRequestCycle cycle, IFormComponent component, IValidator validator)
  +    /**
  +     *  Default implementation; if the current field is in error,
  +     *  then a suffix is written.  The suffix is:
  +     *  <code>&amp;nbsp;&lt;font color="red"&gt;**&lt;/font&gt;</code>.
  +     * 
  +     **/
  +
  +    public void writeSuffix(
  +        IMarkupWriter writer,
  +        IRequestCycle cycle,
  +        IFormComponent component,
  +        IValidator validator)
           throws RequestCycleException
       {
           if (isInError())
  @@ -346,7 +396,7 @@
   
       public boolean getHasErrors()
       {
  -        return _trackings != null && _trackings.size() > 0;
  +        return getFirstError() != null;
       }
   
       /**
  @@ -358,15 +408,20 @@
   
       public IRender getFirstError()
       {
  -        if (_trackings == null)
  +        if (Tapestry.size(_trackings) == 0)
               return null;
   
  -        if (_trackings.size() == 0)
  -            return null;
  +        Iterator i = _trackings.iterator();
  +
  +        while (i.hasNext())
  +        {
  +            IFieldTracking tracking = (IFieldTracking) i.next();
   
  -        IFieldTracking tracking = (IFieldTracking) _trackings.get(0);
  +            if (tracking.isInError())
  +                return tracking.getErrorRenderer();
  +        }
   
  -        return tracking.getRenderer();
  +        return null;
       }
   
       /**
  @@ -383,12 +438,14 @@
               return false;
   
           String formName = component.getForm().getName();
  -        Map formMap = (Map)_trackingMap.get(formName);
  -        
  +        Map formMap = (Map) _trackingMap.get(formName);
  +
           if (formMap == null)
               return false;
   
  -        return formMap.containsKey(component.getName());
  +        IFieldTracking tracking = (IFieldTracking) formMap.get(component.getName());
  +
  +        return tracking != null && tracking.isInError();
       }
   
       /**
  @@ -403,7 +460,7 @@
   
       public List getAssociatedTrackings()
       {
  -        int count = (_trackings == null) ? 0 : _trackings.size();
  +        int count = Tapestry.size(_trackings);
   
           if (count == 0)
               return null;
  @@ -436,7 +493,7 @@
   
       public List getUnassociatedTrackings()
       {
  -        int count = (_trackings == null) ? 0 : _trackings.size();
  +        int count = Tapestry.size(_trackings);
   
           if (count == 0)
               return null;
  @@ -455,14 +512,4 @@
   
           return result;
       }
  -
  -    /** 
  -     *  @since 2.4
  -     * 
  -     **/
  -    
  -    public void discardFromPool()
  -    {
  -    }
  -
  -}
  \ No newline at end of file
  +};
  \ No newline at end of file
  
  
  
  1.8       +3 -3      jakarta-tapestry/examples/Vlib/src/net/sf/tapestry/vlib/Protected.java
  
  Index: Protected.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/examples/Vlib/src/net/sf/tapestry/vlib/Protected.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- Protected.java	13 Jan 2003 03:33:28 -0000	1.7
  +++ Protected.java	28 Feb 2003 17:28:40 -0000	1.8
  @@ -104,14 +104,14 @@
           return _error;
       }
   
  -    protected void setErrorField(String componentId, String message, String value)
  +    protected void setErrorField(String componentId, String message)
       {
           IFormComponent component = (IFormComponent) getComponent(componentId);
   
           IValidationDelegate delegate = getValidationDelegate();
   
           delegate.setFormComponent(component);
  -        delegate.record(message, null, value);
  +        delegate.record(message, null);
   
       }
   
  
  
  
  1.8       +2 -3      jakarta-tapestry/examples/Tutorial/src/tutorial/workbench/upload/Upload.java
  
  Index: Upload.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/examples/Tutorial/src/tutorial/workbench/upload/Upload.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- Upload.java	17 Jan 2003 17:45:29 -0000	1.7
  +++ Upload.java	28 Feb 2003 17:28:40 -0000	1.8
  @@ -105,8 +105,7 @@
   			delegate.setFormComponent((IFormComponent) getComponent("inputFile"));
   			delegate.record(
   				"You must specify a file to upload.",
  -				ValidationConstraint.REQUIRED,
  -				null);
  +				ValidationConstraint.REQUIRED);
   			return;
   		}