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 2006/12/22 21:13:17 UTC
svn commit: r489746 - in
/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry:
ValidationTracker.java ValidationTrackerImpl.java
ValidationTrackerImplTest.java
Author: hlship
Date: Fri Dec 22 12:13:16 2006
New Revision: 489746
URL: http://svn.apache.org/viewvc?view=rev&rev=489746
Log:
Base implementation of the ValidationTracker
Added:
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTracker.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTrackerImpl.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTrackerImplTest.java
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTracker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTracker.java?view=auto&rev=489746
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTracker.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTracker.java Fri Dec 22 12:13:16 2006
@@ -0,0 +1,86 @@
+package org.apache.tapestry;
+
+import java.util.List;
+
+import org.apache.tapestry.corelib.components.Loop;
+
+/**
+ * Tracks information related to user input validations. This information is:
+ * <ul>
+ * <li>The input values provided by the user.
+ * <li>Any validation exceptions associated with input fields.
+ * </ul>
+ * <p>
+ * The tracker must differentiate between components (which will implement the {@link Field}
+ * interfaces) and fields. It is a one to many relationship, because each field may be called upon
+ * to render itself multiple times within a request, because of {@link Loop} or other similar
+ * components.
+ * <p>
+ * Internally, the tracker indexes its information in terms of the {@link Field#getElementName()}
+ * for each rendering of the component (the mechanics of Tapestry ensures that this is unique within
+ * the form).
+ * <p>
+ * Validation trackers must be serializable, as they will almost always be stored into the
+ * HttpSession.
+ * <p>
+ * Trackers are used by only a single form within a single page; they are not threadsafe.
+ */
+public interface ValidationTracker
+{
+ /**
+ * Called by a field to record the exact input from the user, prior to any validation. If the
+ * form is redisplayed (to present errors), the input value will be sent back to the user for
+ * correction.
+ *
+ * @param field
+ * the field recording the input
+ * @param input
+ * the value obtained from the forms submission
+ */
+ void recordInput(Field field, String input);
+
+ /** Returns a previously recorded input value. */
+ String getInput(Field field);
+
+ /**
+ * Records an error message for a field. The error message is primarily derived from a
+ * {@link ValidationException} thrown by a {@link Validator} or {@link Translator}.
+ *
+ * @param field
+ * @param errorMessage
+ */
+ void recordError(Field field, String errorMessage);
+
+ /**
+ * Records an error message that is not associated with any specific field. This often reflects
+ * some amount of cross-form validation.
+ *
+ * @param errorMessage
+ */
+ void recordError(String errorMessage);
+
+ /**
+ * For a given field, determines if the field is "in error", meaning that an error message has
+ * been previously recorded for the field.
+ *
+ * @param field
+ * @return true if an error message is present
+ */
+ boolean inError(Field field);
+
+ /** Returns a previously recorded error message. */
+ String getError(Field field);
+
+ /** Returns true if any field contains an error. */
+ boolean getHasErrors();
+
+ /**
+ * Returns a list of all error messages. The messages are stored in the order that they were
+ * added to the tracker, except that unassociated errors (unassociated with any field) are
+ * listed first.
+ */
+ List<String> getErrors();
+
+ /** Clears all information stored by the tracker. */
+ void clear();
+}
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTrackerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTrackerImpl.java?view=auto&rev=489746
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTrackerImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTrackerImpl.java Fri Dec 22 12:13:16 2006
@@ -0,0 +1,186 @@
+package org.apache.tapestry;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tapestry.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry.ioc.internal.util.InternalUtils;
+
+/**
+ * Standard implmentation of {@link ValidationTracker}. Works pretty hard to ensure a minimum
+ * amount of data is stored in the HttpSession.
+ */
+public final class ValidationTrackerImpl implements ValidationTracker, Serializable
+{
+ private static final long serialVersionUID = -8029192726659275677L;
+
+ private static class FieldTracker implements Serializable
+ {
+ private static final long serialVersionUID = -3653306147088451811L;
+
+ private final String _fieldName;
+
+ private String _input;
+
+ private String _errorMessage;
+
+ FieldTracker(String fieldName)
+ {
+ _fieldName = fieldName;
+ }
+
+ public String getFieldName()
+ {
+ return _fieldName;
+ }
+
+ public void setErrorMessage(String errorMessage)
+ {
+ _errorMessage = errorMessage;
+ }
+
+ public String getErrorMessage()
+ {
+ return _errorMessage;
+ }
+
+ public String getInput()
+ {
+ return _input;
+ }
+
+ public void setInput(String input)
+ {
+ _input = input;
+ }
+
+ }
+
+ private List<String> _extraErrors;
+
+ private List<FieldTracker> _fieldTrackers;
+
+ // Rebuilt on-demand
+
+ private transient Map<String, FieldTracker> _fieldToTracker;
+
+ private void refreshFieldToTracker()
+ {
+ if (_fieldToTracker != null)
+ return;
+
+ if (_fieldTrackers == null)
+ return;
+
+ _fieldToTracker = CollectionFactory.newMap();
+
+ for (FieldTracker ft : _fieldTrackers)
+ _fieldToTracker.put(ft.getFieldName(), ft);
+ }
+
+ private FieldTracker get(Field field)
+ {
+ String key = field.getElementName();
+
+ refreshFieldToTracker();
+
+ FieldTracker result = InternalUtils.get(_fieldToTracker, key);
+
+ if (result == null)
+ result = new FieldTracker(key);
+
+ return result;
+ }
+
+ private void store(FieldTracker fieldTracker)
+ {
+ if (_fieldTrackers == null)
+ _fieldTrackers = CollectionFactory.newList();
+
+ refreshFieldToTracker();
+
+ String key = fieldTracker.getFieldName();
+
+ if (!_fieldToTracker.containsKey(key))
+ {
+ _fieldTrackers.add(fieldTracker);
+ _fieldToTracker.put(key, fieldTracker);
+ }
+ }
+
+ public void clear()
+ {
+ _extraErrors = null;
+ _fieldTrackers = null;
+ _fieldToTracker = null;
+ }
+
+ public String getError(Field field)
+ {
+ return get(field).getErrorMessage();
+ }
+
+ public List<String> getErrors()
+ {
+ List<String> result = CollectionFactory.newList();
+
+ if (_extraErrors != null)
+ result.addAll(_extraErrors);
+
+ if (_fieldTrackers != null)
+ {
+ for (FieldTracker ft : _fieldTrackers)
+ {
+ String errorMessage = ft.getErrorMessage();
+
+ if (errorMessage != null)
+ result.add(errorMessage);
+ }
+ }
+
+ return result;
+ }
+
+ public boolean getHasErrors()
+ {
+ return !getErrors().isEmpty();
+ }
+
+ public String getInput(Field field)
+ {
+ return get(field).getInput();
+ }
+
+ public boolean inError(Field field)
+ {
+ return InternalUtils.isNonBlank(get(field).getErrorMessage());
+ }
+
+ public void recordError(Field field, String errorMessage)
+ {
+ FieldTracker ft = get(field);
+
+ ft.setErrorMessage(errorMessage);
+
+ store(ft);
+ }
+
+ public void recordError(String errorMessage)
+ {
+ if (_extraErrors == null)
+ _extraErrors = CollectionFactory.newList();
+
+ _extraErrors.add(errorMessage);
+ }
+
+ public void recordInput(Field field, String input)
+ {
+ FieldTracker ft = get(field);
+
+ ft.setInput(input);
+
+ store(ft);
+ }
+
+}
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTrackerImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTrackerImplTest.java?view=auto&rev=489746
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTrackerImplTest.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ValidationTrackerImplTest.java Fri Dec 22 12:13:16 2006
@@ -0,0 +1,205 @@
+package org.apache.tapestry;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Arrays;
+
+import org.apache.tapestry.test.TapestryTestCase;
+import org.testng.annotations.Test;
+
+public class ValidationTrackerImplTest extends TapestryTestCase
+{
+ @Test
+ public void empty_tracker_has_no_errors()
+ {
+ ValidationTracker tracker = new ValidationTrackerImpl();
+
+ assertTrue(tracker.getErrors().isEmpty());
+ assertFalse(tracker.getHasErrors());
+ }
+
+ @Test
+ public void order_added_is_maintained()
+ {
+ Field fielda = newField("fieldA");
+ Field fieldb = newField("fieldB");
+
+ replay();
+
+ ValidationTracker tracker = new ValidationTrackerImpl();
+
+ tracker.recordError("one");
+ tracker.recordError(fieldb, "fieldb: two");
+ tracker.recordError("three");
+ tracker.recordError(fielda, "fielda: four");
+
+ assertEquals(tracker.getErrors(), Arrays.asList(
+ "one",
+ "three",
+ "fieldb: two",
+ "fielda: four"));
+
+ verify();
+ }
+
+ @Test
+ public void record_input()
+ {
+ Field field = newField("field");
+
+ replay();
+
+ ValidationTracker tracker = new ValidationTrackerImpl();
+
+ assertNull(tracker.getInput(field));
+
+ tracker.recordInput(field, "one");
+
+ assertEquals(tracker.getInput(field), "one");
+
+ tracker.recordInput(field, "two");
+
+ assertEquals(tracker.getInput(field), "two");
+
+ verify();
+ }
+
+ @Test
+ public void record_error_for_field()
+ {
+ Field field = newField("field");
+
+ replay();
+
+ ValidationTracker tracker = new ValidationTrackerImpl();
+
+ assertFalse(tracker.getHasErrors());
+ assertFalse(tracker.inError(field));
+ assertNull(tracker.getError(field));
+
+ tracker.recordError(field, "one");
+
+ assertTrue(tracker.getHasErrors());
+ assertTrue(tracker.inError(field));
+ assertEquals(tracker.getError(field), "one");
+
+ tracker.recordError(field, "two");
+ assertEquals(tracker.getError(field), "two");
+
+ verify();
+ }
+
+ @Test
+ public void record_error_for_form()
+ {
+ ValidationTracker tracker = new ValidationTrackerImpl();
+
+ assertFalse(tracker.getHasErrors());
+
+ assertTrue(tracker.getErrors().isEmpty());
+
+ tracker.recordError("one");
+
+ assertEquals(tracker.getErrors(), Arrays.asList("one"));
+
+ tracker.recordError("two");
+
+ assertEquals(tracker.getErrors(), Arrays.asList("one", "two"));
+ }
+
+ @Test
+ public void data_survives_serialization() throws Exception
+ {
+ Field fielda = newField("fieldA");
+ Field fieldb = newField("fieldB");
+ Field fieldc = newField("fieldC");
+
+ replay();
+
+ ValidationTracker tracker = new ValidationTrackerImpl();
+
+ tracker.recordError("one");
+ tracker.recordError(fieldb, "fieldb: two");
+ tracker.recordError("three");
+ tracker.recordError(fielda, "fielda: four");
+
+ ValidationTracker copy = cloneBySerialiation(tracker);
+
+ copy.recordError(fieldc, "fieldc: five");
+
+ assertEquals(copy.getErrors(), Arrays.asList(
+ "one",
+ "three",
+ "fieldb: two",
+ "fielda: four",
+ "fieldc: five"));
+
+ verify();
+ }
+
+ @Test
+ public void clear_removes_all()
+ {
+ Field fielda = newField("fieldA");
+ Field fieldb = newField("fieldB");
+
+ replay();
+
+ ValidationTracker tracker = new ValidationTrackerImpl();
+
+ tracker.recordError("one");
+ tracker.recordInput(fieldb, "input b");
+ tracker.recordError(fieldb, "fieldb: two");
+ tracker.recordError("three");
+ tracker.recordInput(fielda, "input a");
+ tracker.recordError(fielda, "fielda: four");
+
+ tracker.clear();
+
+ assertFalse(tracker.getHasErrors());
+ assertTrue(tracker.getErrors().isEmpty());
+ assertNull(tracker.getInput(fielda));
+ assertNull(tracker.getInput(fieldb));
+
+ verify();
+ }
+
+ private final Field newField(String elementName)
+ {
+ Field field = newField();
+
+ // Fields generated this way, for the purposes of this test, do not
+ // ever change their elementName. In real life, elementNames can change.
+
+ expect(field.getElementName()).andReturn(elementName).atLeastOnce();
+
+ return field;
+ }
+
+ protected final Field newField()
+ {
+ return newMock(Field.class);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected final <T> T cloneBySerialiation(T input) throws Exception
+ {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(bos);
+
+ oos.writeObject(input);
+
+ oos.close();
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ ObjectInputStream ois = new ObjectInputStream(bis);
+
+ T result = (T) ois.readObject();
+
+ ois.close();
+
+ return result;
+ }
+}