aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArtur Signell <artur@vaadin.com>2011-12-14 22:09:43 +0200
committerArtur Signell <artur@vaadin.com>2011-12-14 22:40:57 +0200
commite137e9fdd2c5182777a0419b8cf8e146fc33fd18 (patch)
tree0e7b248aa2e5f7fb612d851375a8ecaa5dfc0ece
parent0bcd80a5ec66ca4e17fd689a5beab8de5c76559a (diff)
downloadvaadin-framework-e137e9fdd2c5182777a0419b8cf8e146fc33fd18.tar.gz
vaadin-framework-e137e9fdd2c5182777a0419b8cf8e146fc33fd18.zip
#8110 Validation is now done on the converted value
Added type to AbstractValidator and automatic type checking so sub classes get the correct type automatically Added a RangeValidator that work with any type that is Comparable CompositeValidator no longer extends AbstractValidator as it does not validate in the same way as AbstractValidator does
-rw-r--r--src/com/vaadin/data/validator/AbstractStringValidator.java41
-rw-r--r--src/com/vaadin/data/validator/AbstractValidator.java33
-rw-r--r--src/com/vaadin/data/validator/CompositeValidator.java36
-rw-r--r--src/com/vaadin/data/validator/DateRangeValidator.java37
-rw-r--r--src/com/vaadin/data/validator/DoubleRangeValidator.java36
-rw-r--r--src/com/vaadin/data/validator/DoubleValidator.java8
-rw-r--r--src/com/vaadin/data/validator/IntegerRangeValidator.java37
-rw-r--r--src/com/vaadin/data/validator/IntegerValidator.java8
-rw-r--r--src/com/vaadin/data/validator/RangeValidator.java172
-rw-r--r--src/com/vaadin/data/validator/RegexpValidator.java5
-rw-r--r--src/com/vaadin/data/validator/StringLengthValidator.java10
-rw-r--r--src/com/vaadin/ui/AbstractField.java97
-rw-r--r--tests/server-side/com/vaadin/tests/server/validation/RangeValidatorTest.java52
-rw-r--r--tests/testbench/com/vaadin/tests/components/datefield/DateFieldRangeValidation.html96
-rw-r--r--tests/testbench/com/vaadin/tests/components/datefield/DateFieldRangeValidation.java142
-rw-r--r--tests/testbench/com/vaadin/tests/components/datefield/RequiredInvalidDateField.java17
-rw-r--r--tests/testbench/com/vaadin/tests/util/AlwaysFailValidator.java7
-rw-r--r--tests/testbench/com/vaadin/tests/validation/TestValidators.java7
18 files changed, 728 insertions, 113 deletions
diff --git a/src/com/vaadin/data/validator/AbstractStringValidator.java b/src/com/vaadin/data/validator/AbstractStringValidator.java
index c47ca15d77..5267cc7b7b 100644
--- a/src/com/vaadin/data/validator/AbstractStringValidator.java
+++ b/src/com/vaadin/data/validator/AbstractStringValidator.java
@@ -4,9 +4,7 @@
package com.vaadin.data.validator;
/**
- * Validator base class for validating strings. See
- * {@link com.vaadin.data.validator.AbstractValidator} for more information.
- *
+ * Validator base class for validating strings.
* <p>
* To include the value that failed validation in the exception message you can
* use "{0}" in the error message. This will be replaced with the failed value
@@ -15,12 +13,11 @@ package com.vaadin.data.validator;
* </p>
*
* @author Vaadin Ltd.
- * @version
- * @VERSION@
+ * @version @VERSION@
* @since 5.4
*/
@SuppressWarnings("serial")
-public abstract class AbstractStringValidator extends AbstractValidator {
+public abstract class AbstractStringValidator extends AbstractValidator<String> {
/**
* Constructs a validator for strings.
@@ -38,36 +35,8 @@ public abstract class AbstractStringValidator extends AbstractValidator {
super(errorMessage);
}
- /**
- * Tests if the given value is a valid string.
- * <p>
- * Null values are always accepted. Values that are not {@link String}s are
- * converted using {@link #toString()}. Then {@link #isValidString(String)}
- * is used to validate the value.
- * </p>
- *
- * @param value
- * the value to check
- * @return true if the value (or its toString()) is a valid string, false
- * otherwise
- */
@Override
- protected boolean isValidValue(Object value) {
- if (value == null) {
- return true;
- }
- if (!(value instanceof String)) {
- value = String.valueOf(value);
- }
- return isValidString((String) value);
+ public Class<String> getType() {
+ return String.class;
}
-
- /**
- * Checks if the given string is valid.
- *
- * @param value
- * String to check. Can never be null.
- * @return true if the string is valid, false otherwise
- */
- protected abstract boolean isValidString(String value);
}
diff --git a/src/com/vaadin/data/validator/AbstractValidator.java b/src/com/vaadin/data/validator/AbstractValidator.java
index 2bf2edbecc..27eaaca485 100644
--- a/src/com/vaadin/data/validator/AbstractValidator.java
+++ b/src/com/vaadin/data/validator/AbstractValidator.java
@@ -27,13 +27,14 @@ import com.vaadin.data.Validator;
* applications. To check validity, {@link #validate(Object)} should be used.
* </p>
*
+ * @param <T>
+ * The type
* @author Vaadin Ltd.
* @version
* @VERSION@
* @since 5.4
*/
-@SuppressWarnings("serial")
-public abstract class AbstractValidator implements Validator {
+public abstract class AbstractValidator<T> implements Validator {
/**
* Error message that is included in an {@link InvalidValueException} if
@@ -81,16 +82,36 @@ public abstract class AbstractValidator implements Validator {
* @param value
* @return
*/
- protected abstract boolean isValidValue(Object value);
+ protected abstract boolean isValidValue(T value);
public void validate(Object value) throws InvalidValueException {
- if (!isValidValue(value)) {
- String message = errorMessage.replace("{0}", String.valueOf(value));
+ // isValidType ensures that value can safely be cast to TYPE
+ if (!isValidType(value) || !isValidValue((T) value)) {
+ String message = getErrorMessage().replace("{0}",
+ String.valueOf(value));
throw new InvalidValueException(message);
}
}
/**
+ * Checks the type of the value to validate to ensure it conforms with
+ * getType. Enables sub classes to handle the specific type instead of
+ * Object.
+ *
+ * @param value
+ * The value to check
+ * @return true if the value can safely be cast to the type specified by
+ * {@link #getType()}
+ */
+ protected boolean isValidType(Object value) {
+ if (value == null) {
+ return true;
+ }
+
+ return getType().isAssignableFrom(value.getClass());
+ }
+
+ /**
* Returns the message to be included in the exception in case the value
* does not validate.
*
@@ -112,4 +133,6 @@ public abstract class AbstractValidator implements Validator {
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
+
+ public abstract Class<T> getType();
}
diff --git a/src/com/vaadin/data/validator/CompositeValidator.java b/src/com/vaadin/data/validator/CompositeValidator.java
index 2dd5950cec..083af70f30 100644
--- a/src/com/vaadin/data/validator/CompositeValidator.java
+++ b/src/com/vaadin/data/validator/CompositeValidator.java
@@ -19,12 +19,11 @@ import com.vaadin.data.Validator;
* <code>AND</code> and <code>OR</code>.
*
* @author Vaadin Ltd.
- * @version
- * @VERSION@
+ * @version @VERSION@
* @since 3.0
*/
@SuppressWarnings("serial")
-public class CompositeValidator extends AbstractValidator {
+public class CompositeValidator implements Validator {
public enum CombinationMode {
/**
@@ -52,6 +51,8 @@ public class CompositeValidator extends AbstractValidator {
@Deprecated
public static final CombinationMode MODE_OR = CombinationMode.OR;
+ private String errorMessage;
+
/**
* Operation mode.
*/
@@ -67,7 +68,7 @@ public class CompositeValidator extends AbstractValidator {
* message.
*/
public CompositeValidator() {
- super("");
+ this(CombinationMode.AND, "");
}
/**
@@ -77,7 +78,7 @@ public class CompositeValidator extends AbstractValidator {
* @param errorMessage
*/
public CompositeValidator(CombinationMode mode, String errorMessage) {
- super(errorMessage);
+ setErrorMessage(errorMessage);
setMode(mode);
}
@@ -100,7 +101,6 @@ public class CompositeValidator extends AbstractValidator {
* @throws Validator.InvalidValueException
* if the value is not valid.
*/
- @Override
public void validate(Object value) throws Validator.InvalidValueException {
switch (mode) {
case AND:
@@ -133,12 +133,6 @@ public class CompositeValidator extends AbstractValidator {
}
}
- @Override
- protected boolean isValidValue(Object value) {
- // not used as validate() overridden
- return false;
- }
-
/**
* Gets the mode of the validator.
*
@@ -171,10 +165,9 @@ public class CompositeValidator extends AbstractValidator {
* Gets the error message for the composite validator. If the error message
* is null, original error messages of the sub-validators are used instead.
*/
- @Override
public String getErrorMessage() {
- if (super.getErrorMessage() != null) {
- return super.getErrorMessage();
+ if (errorMessage != null) {
+ return errorMessage;
}
// TODO Return composite error message
@@ -229,7 +222,7 @@ public class CompositeValidator extends AbstractValidator {
* that must apply or null if none found.
*/
public Collection<Validator> getSubValidators(Class validatorType) {
- if (mode != MODE_AND) {
+ if (mode != CombinationMode.AND) {
return null;
}
@@ -251,4 +244,15 @@ public class CompositeValidator extends AbstractValidator {
return found.isEmpty() ? null : found;
}
+ /**
+ * Sets the message to be included in the exception in case the value does
+ * not validate. The exception message is typically shown to the end user.
+ *
+ * @param errorMessage
+ * the error message.
+ */
+ public void setErrorMessage(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+
}
diff --git a/src/com/vaadin/data/validator/DateRangeValidator.java b/src/com/vaadin/data/validator/DateRangeValidator.java
new file mode 100644
index 0000000000..36a607691e
--- /dev/null
+++ b/src/com/vaadin/data/validator/DateRangeValidator.java
@@ -0,0 +1,37 @@
+package com.vaadin.data.validator;
+
+import java.util.Date;
+
+import com.vaadin.ui.DateField.Resolution;
+
+/**
+ * Validator for validating that a Date is inside a given range.
+ *
+ * @author Vaadin Ltd.
+ * @version
+ * @VERSION@
+ * @since 7.0
+ */
+public class DateRangeValidator extends RangeValidator<Date> {
+
+ /**
+ * Creates a validator for checking that an Date is within a given range.
+ *
+ * By default the range is inclusive i.e. both minValue and maxValue are
+ * valid values. Use {@link #setMinValueIncluded(boolean)} or
+ * {@link #setMaxValueIncluded(boolean)} to change it.
+ *
+ *
+ * @param errorMessage
+ * the message to display in case the value does not validate.
+ * @param minValue
+ * The minimum value to accept or null for no limit
+ * @param maxValue
+ * The maximum value to accept or null for no limit
+ */
+ public DateRangeValidator(String errorMessage, Date minValue,
+ Date maxValue, Resolution resolution) {
+ super(errorMessage, Date.class, minValue, maxValue);
+ }
+
+}
diff --git a/src/com/vaadin/data/validator/DoubleRangeValidator.java b/src/com/vaadin/data/validator/DoubleRangeValidator.java
new file mode 100644
index 0000000000..91fcf004af
--- /dev/null
+++ b/src/com/vaadin/data/validator/DoubleRangeValidator.java
@@ -0,0 +1,36 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.data.validator;
+
+/**
+ * Validator for validating that a {@link Double} is inside a given range.
+ *
+ * @author Vaadin Ltd.
+ * @version
+ * @VERSION@
+ * @since 7.0
+ */
+@SuppressWarnings("serial")
+public class DoubleRangeValidator extends RangeValidator<Double> {
+
+ /**
+ * Creates a validator for checking that an Double is within a given range.
+ *
+ * By default the range is inclusive i.e. both minValue and maxValue are
+ * valid values. Use {@link #setMinValueIncluded(boolean)} or
+ * {@link #setMaxValueIncluded(boolean)} to change it.
+ *
+ *
+ * @param errorMessage
+ * the message to display in case the value does not validate.
+ * @param minValue
+ * The minimum value to accept or null for no limit
+ * @param maxValue
+ * The maximum value to accept or null for no limit
+ */
+ public DoubleRangeValidator(String errorMessage, Double minValue, Double maxValue) {
+ super(errorMessage, Double.class, minValue, maxValue);
+ }
+
+}
diff --git a/src/com/vaadin/data/validator/DoubleValidator.java b/src/com/vaadin/data/validator/DoubleValidator.java
index e90919c17d..f603f57480 100644
--- a/src/com/vaadin/data/validator/DoubleValidator.java
+++ b/src/com/vaadin/data/validator/DoubleValidator.java
@@ -12,7 +12,9 @@ package com.vaadin.data.validator;
* @version
* @VERSION@
* @since 5.4
+ * @deprecated in Vaadin 7.0. Use an Double converter on the field instead.
*/
+@Deprecated
@SuppressWarnings("serial")
public class DoubleValidator extends AbstractStringValidator {
@@ -22,13 +24,17 @@ public class DoubleValidator extends AbstractStringValidator {
*
* @param errorMessage
* the message to display in case the value does not validate.
+ * @deprecated in Vaadin 7.0. Use a Double converter on the field instead
+ * and/or use a {@link DoubleRangeValidator} for validating that
+ * the value is inside a given range.
*/
+ @Deprecated
public DoubleValidator(String errorMessage) {
super(errorMessage);
}
@Override
- protected boolean isValidString(String value) {
+ protected boolean isValidValue(String value) {
try {
Double.parseDouble(value);
return true;
diff --git a/src/com/vaadin/data/validator/IntegerRangeValidator.java b/src/com/vaadin/data/validator/IntegerRangeValidator.java
new file mode 100644
index 0000000000..c171dd97d8
--- /dev/null
+++ b/src/com/vaadin/data/validator/IntegerRangeValidator.java
@@ -0,0 +1,37 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.data.validator;
+
+/**
+ * Validator for validating that an {@link Integer} is inside a given range.
+ *
+ * @author Vaadin Ltd.
+ * @version
+ * @VERSION@
+ * @since 5.4
+ */
+@SuppressWarnings("serial")
+public class IntegerRangeValidator extends RangeValidator<Integer> {
+
+ /**
+ * Creates a validator for checking that an Integer is within a given range.
+ *
+ * By default the range is inclusive i.e. both minValue and maxValue are
+ * valid values. Use {@link #setMinValueIncluded(boolean)} or
+ * {@link #setMaxValueIncluded(boolean)} to change it.
+ *
+ *
+ * @param errorMessage
+ * the message to display in case the value does not validate.
+ * @param minValue
+ * The minimum value to accept or null for no limit
+ * @param maxValue
+ * The maximum value to accept or null for no limit
+ */
+ public IntegerRangeValidator(String errorMessage, Integer minValue,
+ Integer maxValue) {
+ super(errorMessage, Integer.class, minValue, maxValue);
+ }
+
+}
diff --git a/src/com/vaadin/data/validator/IntegerValidator.java b/src/com/vaadin/data/validator/IntegerValidator.java
index 50b45b90ce..bf46497594 100644
--- a/src/com/vaadin/data/validator/IntegerValidator.java
+++ b/src/com/vaadin/data/validator/IntegerValidator.java
@@ -12,8 +12,10 @@ package com.vaadin.data.validator;
* @version
* @VERSION@
* @since 5.4
+ * @deprecated in Vaadin 7.0. Use an Integer converter on the field instead.
*/
@SuppressWarnings("serial")
+@Deprecated
public class IntegerValidator extends AbstractStringValidator {
/**
@@ -22,14 +24,18 @@ public class IntegerValidator extends AbstractStringValidator {
*
* @param errorMessage
* the message to display in case the value does not validate.
+ * @deprecated in Vaadin 7.0. Use an Integer converter on the field instead
+ * and/or use an {@link IntegerRangeValidator} for validating
+ * that the value is inside a given range.
*/
+ @Deprecated
public IntegerValidator(String errorMessage) {
super(errorMessage);
}
@Override
- protected boolean isValidString(String value) {
+ protected boolean isValidValue(String value) {
try {
Integer.parseInt(value);
return true;
diff --git a/src/com/vaadin/data/validator/RangeValidator.java b/src/com/vaadin/data/validator/RangeValidator.java
new file mode 100644
index 0000000000..0847b8ed7b
--- /dev/null
+++ b/src/com/vaadin/data/validator/RangeValidator.java
@@ -0,0 +1,172 @@
+package com.vaadin.data.validator;
+
+/**
+ * An base implementation for validating any objects that implement
+ * {@link Comparable}.
+ *
+ * Verifies that the value is of the given type and within the (optionally)
+ * given limits. Typically you want to use a sub class of this like
+ * {@link IntegerRangeValidator}, {@link DoubleRangeValidator} or
+ * {@link DateRangeValidator} in applications.
+ *
+ * @param <T>
+ * The type of Number to validate. Must implement Comparable so that
+ * minimum and maximum checks work.
+ * @author Vaadin Ltd.
+ * @version
+ * @VERSION@
+ * @since 7.0
+ */
+public class RangeValidator<T extends Comparable> extends AbstractValidator<T> {
+
+ private T minValue = null;
+ private boolean minValueIncluded = true;
+ private T maxValue = null;
+ private boolean maxValueIncluded = true;
+ private Class<T> type;
+
+ /**
+ * Creates a new range validator of the given type.
+ *
+ * @param errorMessage
+ * The error message to use if validation fails
+ * @param type
+ * The type of object the validator can validate.
+ * @param minValue
+ * The minimum value that should be accepted or null for no limit
+ * @param maxValue
+ * The maximum value that should be accepted or null for no limit
+ */
+ public RangeValidator(String errorMessage, Class<T> type, T minValue,
+ T maxValue) {
+ super(errorMessage);
+ this.type = type;
+ this.minValue = minValue;
+ this.maxValue = maxValue;
+ }
+
+ /**
+ * Checks if the minimum value is part of the accepted range
+ *
+ * @return true if the minimum value is part of the range, false otherwise
+ */
+ public boolean isMinValueIncluded() {
+ return minValueIncluded;
+ }
+
+ /**
+ * Sets if the minimum value is part of the accepted range
+ *
+ * @param minValueIncluded
+ * true if the minimum value should be part of the range, false
+ * otherwise
+ */
+ public void setMinValueIncluded(boolean minValueIncluded) {
+ this.minValueIncluded = minValueIncluded;
+ }
+
+ /**
+ * Checks if the maximum value is part of the accepted range
+ *
+ * @return true if the maximum value is part of the range, false otherwise
+ */
+ public boolean isMaxValueIncluded() {
+ return maxValueIncluded;
+ }
+
+ /**
+ * Sets if the maximum value is part of the accepted range
+ *
+ * @param maxValueIncluded
+ * true if the maximum value should be part of the range, false
+ * otherwise
+ */
+ public void setMaxValueIncluded(boolean maxValueIncluded) {
+ this.maxValueIncluded = maxValueIncluded;
+ }
+
+ /**
+ * Gets the minimum value of the range
+ *
+ * @return the minimum value
+ */
+ public T getMinValue() {
+ return minValue;
+ }
+
+ /**
+ * Sets the minimum value of the range. Use
+ * {@link #setMinValueIncluded(boolean)} to control whether this value is
+ * part of the range or not.
+ *
+ * @param minValue
+ * the minimum value
+ */
+ public void setMinValue(T minValue) {
+ this.minValue = minValue;
+ }
+
+ /**
+ * Gets the maximum value of the range
+ *
+ * @return the maximum value
+ */
+ public T getMaxValue() {
+ return maxValue;
+ }
+
+ /**
+ * Sets the maximum value of the range. Use
+ * {@link #setMaxValueIncluded(boolean)} to control whether this value is
+ * part of the range or not.
+ *
+ * @param maxValue
+ * the maximum value
+ */
+ public void setMaxValue(T maxValue) {
+ this.maxValue = maxValue;
+ }
+
+ /* (non-Javadoc)
+ * @see com.vaadin.data.validator.AbstractValidator#isValidValue(java.lang.Object)
+ */
+ @Override
+ protected boolean isValidValue(T value) {
+ if (value == null) {
+ return true;
+ }
+
+ if (getMinValue() != null) {
+ // Ensure that the min limit is ok
+ int result = value.compareTo(getMinValue());
+ if (result < 0) {
+ // value less than min value
+ return false;
+ } else if (result == 0 && !isMinValueIncluded()) {
+ // values equal and min value not included
+ return false;
+ }
+ }
+ if (getMaxValue() != null) {
+ // Ensure that the Max limit is ok
+ int result = value.compareTo(getMaxValue());
+ if (result > 0) {
+ // value greater than max value
+ return false;
+ } else if (result == 0 && !isMaxValueIncluded()) {
+ // values equal and max value not included
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see com.vaadin.data.validator.AbstractValidator#getType()
+ */
+ @Override
+ public Class<T> getType() {
+ return type;
+ }
+
+}
diff --git a/src/com/vaadin/data/validator/RegexpValidator.java b/src/com/vaadin/data/validator/RegexpValidator.java
index 684de7697c..dec0bf0be9 100644
--- a/src/com/vaadin/data/validator/RegexpValidator.java
+++ b/src/com/vaadin/data/validator/RegexpValidator.java
@@ -62,8 +62,11 @@ public class RegexpValidator extends AbstractStringValidator {
this.complete = complete;
}
+ /* (non-Javadoc)
+ * @see com.vaadin.data.validator.AbstractValidator#isValidValue(java.lang.Object)
+ */
@Override
- protected boolean isValidString(String value) {
+ protected boolean isValidValue(String value) {
if (complete) {
return getMatcher(value).matches();
} else {
diff --git a/src/com/vaadin/data/validator/StringLengthValidator.java b/src/com/vaadin/data/validator/StringLengthValidator.java
index 1f2b569726..a6242e8170 100644
--- a/src/com/vaadin/data/validator/StringLengthValidator.java
+++ b/src/com/vaadin/data/validator/StringLengthValidator.java
@@ -14,7 +14,7 @@ package com.vaadin.data.validator;
* @since 3.0
*/
@SuppressWarnings("serial")
-public class StringLengthValidator extends AbstractValidator {
+public class StringLengthValidator extends AbstractStringValidator {
private int minLength = -1;
@@ -62,15 +62,11 @@ public class StringLengthValidator extends AbstractValidator {
* @return <code>true</code> for valid value, otherwise <code>false</code>.
*/
@Override
- protected boolean isValidValue(Object value) {
+ protected boolean isValidValue(String value) {
if (value == null) {
return allowNull;
}
- final String s = value.toString();
- if (s == null) {
- return allowNull;
- }
- final int len = s.length();
+ final int len = value.length();
if ((minLength >= 0 && len < minLength)
|| (maxLength >= 0 && len > maxLength)) {
return false;
diff --git a/src/com/vaadin/ui/AbstractField.java b/src/com/vaadin/ui/AbstractField.java
index fb7efc41f5..6b1c535a03 100644
--- a/src/com/vaadin/ui/AbstractField.java
+++ b/src/com/vaadin/ui/AbstractField.java
@@ -245,15 +245,12 @@ public abstract class AbstractField<T> extends AbstractComponent implements
public void commit() throws Buffered.SourceException, InvalidValueException {
if (dataSource != null && !dataSource.isReadOnly()) {
if ((isInvalidCommitted() || isValid())) {
- final T fieldValue = getFieldValue();
try {
// Commits the value to datasource.
valueWasModifiedByDataSourceDuringCommit = false;
committingValueToDataSource = true;
- getPropertyDataSource().setValue(
- convertToDataSource(fieldValue));
-
+ getPropertyDataSource().setValue(getConvertedFieldValue());
} catch (final Throwable e) {
// Sets the buffering state.
@@ -518,7 +515,8 @@ public abstract class AbstractField<T> extends AbstractComponent implements
* @throws Property.ConversionException
*/
protected void setValue(T newFieldValue, boolean repaintIsNotNeeded)
- throws Property.ReadOnlyException, Property.ConversionException {
+ throws Property.ReadOnlyException, Property.ConversionException,
+ InvalidValueException {
if (!equals(newFieldValue, getInternalValue())) {
@@ -529,19 +527,19 @@ public abstract class AbstractField<T> extends AbstractComponent implements
// Repaint is needed even when the client thinks that it knows the
// new state if validity of the component may change
- if (repaintIsNotNeeded && (isRequired() || getValidators() != null)) {
+ if (repaintIsNotNeeded
+ && (isRequired() || getValidators() != null || getValueConverter() != null)) {
repaintIsNotNeeded = false;
}
- // If invalid values are not allowed, the value must be checked
if (!isInvalidAllowed()) {
- final Collection<Validator> v = getValidators();
- if (v != null) {
- for (final Iterator<Validator> i = v.iterator(); i
- .hasNext();) {
- (i.next()).validate(newFieldValue);
- }
- }
+ /*
+ * If invalid values are not allowed the value must be validated
+ * before it is set. If validation fails, the
+ * InvalidValueException is thrown and the internal value is not
+ * updated.
+ */
+ validate(newFieldValue);
}
// Changes the value
@@ -717,15 +715,15 @@ public abstract class AbstractField<T> extends AbstractComponent implements
}
/**
- * Sets the value converter for the field from the converter factory defined
- * for the application. Clears the value converter if no application
+ * Retrieves a value converter for the field from the converter factory
+ * defined for the application. Clears the value converter if no application
* reference is available or if the factory returns null.
*
* @param datamodelType
* The type of the data model that we want to be able to convert
* from
*/
- private void updateValueConverterFromFactory(Class<?> datamodelType) {
+ public void updateValueConverterFromFactory(Class<?> datamodelType) {
Converter<?, T> converter = null;
Application app = Application.getCurrentApplication();
@@ -822,6 +820,15 @@ public abstract class AbstractField<T> extends AbstractComponent implements
}
}
+ /**
+ * Returns the current field value converted to the data source type.
+ *
+ * @return The converted value that is compatible with the data source type
+ */
+ public Object getConvertedFieldValue() {
+ return convertToDataSource(getFieldValue());
+ }
+
/* Validation */
/**
@@ -891,8 +898,9 @@ public abstract class AbstractField<T> extends AbstractComponent implements
* Checks the validity of the Field.
*
* A field is invalid if it is set as required (using
- * {@link #setRequired(boolean)} and is empty or if one or several of the
- * validators added to the field indicate it is invalid.
+ * {@link #setRequired(boolean)} and is empty, if one or several of the
+ * validators added to the field indicate it is invalid or if the value
+ * cannot be converted provided a value converter has been set.
*
* The "required" validation is a built-in validation feature. If the field
* is required and empty this method throws an EmptyValueException with the
@@ -905,30 +913,49 @@ public abstract class AbstractField<T> extends AbstractComponent implements
if (isRequired() && isEmpty()) {
throw new Validator.EmptyValueException(requiredError);
}
+ validate(getFieldValue());
+ }
- // If there is no validator, there can not be any errors
- if (validators == null) {
- return;
- }
-
- final Object fieldValue = getFieldValue();
+ /**
+ * Validates that the given value pass the validators for the field.
+ * <p>
+ * This method does not check the requiredness of the field.
+ *
+ * @param fieldValue
+ * The value to check
+ * @throws Validator.InvalidValueException
+ * if one or several validators fail
+ */
+ protected void validate(T fieldValue)
+ throws Validator.InvalidValueException {
- List<InvalidValueException> validationExceptions = null;
+ Object valueToValidate = fieldValue;
- // Gets all the validation errors
- for (Validator v : validators) {
+ // If there is a converter we start by converting the value as we want
+ // to validate the converted value
+ if (getValueConverter() != null) {
try {
- v.validate(fieldValue);
- } catch (final Validator.InvalidValueException e) {
- if (validationExceptions == null) {
- validationExceptions = new ArrayList<InvalidValueException>();
+ valueToValidate = getValueConverter()
+ .convertFromTargetToSource(fieldValue, getLocale());
+ } catch (Exception e) {
+ throw new InvalidValueException(e.getMessage());
+ }
+ }
+
+ List<InvalidValueException> validationExceptions = new ArrayList<InvalidValueException>();
+ if (validators != null) {
+ // Gets all the validation errors
+ for (Validator v : validators) {
+ try {
+ v.validate(valueToValidate);
+ } catch (final Validator.InvalidValueException e) {
+ validationExceptions.add(e);
}
- validationExceptions.add(e);
}
}
- // If there were no error
- if (validationExceptions == null) {
+ // If there were no errors
+ if (validationExceptions.isEmpty()) {
return;
}
diff --git a/tests/server-side/com/vaadin/tests/server/validation/RangeValidatorTest.java b/tests/server-side/com/vaadin/tests/server/validation/RangeValidatorTest.java
new file mode 100644
index 0000000000..2cb2c6a509
--- /dev/null
+++ b/tests/server-side/com/vaadin/tests/server/validation/RangeValidatorTest.java
@@ -0,0 +1,52 @@
+package com.vaadin.tests.server.validation;
+
+import junit.framework.TestCase;
+
+import com.vaadin.data.validator.IntegerRangeValidator;
+
+public class RangeValidatorTest extends TestCase {
+
+ // This test uses IntegerRangeValidator for simplicity.
+ // IntegerRangeValidator contains no code so we really are testing
+ // RangeValidator
+ public void testMinValueNonInclusive() {
+ IntegerRangeValidator iv = new IntegerRangeValidator("Failed", 0, 10);
+ iv.setMinValueIncluded(false);
+ assertFalse(iv.isValid(0));
+ assertTrue(iv.isValid(10));
+ assertFalse(iv.isValid(11));
+ assertFalse(iv.isValid(-1));
+ }
+
+ public void testMinMaxValuesInclusive() {
+ IntegerRangeValidator iv = new IntegerRangeValidator("Failed", 0, 10);
+ assertTrue(iv.isValid(0));
+ assertTrue(iv.isValid(1));
+ assertTrue(iv.isValid(10));
+ assertFalse(iv.isValid(11));
+ assertFalse(iv.isValid(-1));
+ }
+
+ public void testMaxValueNonInclusive() {
+ IntegerRangeValidator iv = new IntegerRangeValidator("Failed", 0, 10);
+ iv.setMaxValueIncluded(false);
+ assertTrue(iv.isValid(0));
+ assertTrue(iv.isValid(9));
+ assertFalse(iv.isValid(10));
+ assertFalse(iv.isValid(11));
+ assertFalse(iv.isValid(-1));
+ }
+
+ public void testMinMaxValuesNonInclusive() {
+ IntegerRangeValidator iv = new IntegerRangeValidator("Failed", 0, 10);
+ iv.setMinValueIncluded(false);
+ iv.setMaxValueIncluded(false);
+
+ assertFalse(iv.isValid(0));
+ assertTrue(iv.isValid(1));
+ assertTrue(iv.isValid(9));
+ assertFalse(iv.isValid(10));
+ assertFalse(iv.isValid(11));
+ assertFalse(iv.isValid(-1));
+ }
+}
diff --git a/tests/testbench/com/vaadin/tests/components/datefield/DateFieldRangeValidation.html b/tests/testbench/com/vaadin/tests/components/datefield/DateFieldRangeValidation.html
new file mode 100644
index 0000000000..25bd6357c3
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/components/datefield/DateFieldRangeValidation.html
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="http://localhost:8888/" />
+<title>New Test</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">New Test</td></tr>
+</thead><tbody>
+<tr>
+ <td>open</td>
+ <td>/run/com.vaadin.tests.components.datefield.DateFieldRangeValidation?restartApplication</td>
+ <td></td>
+</tr>
+<!--select 15.12.2011-->
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRangeValidation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VPopupCalendar[0]#popupButton</td>
+ <td>11,17</td>
+</tr>
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRangeValidation::Root/VOverlay[0]/VCalendarPanel[0]#day15</td>
+ <td>7,5</td>
+</tr>
+<!--should not be error-->
+<tr>
+ <td>assertElementNotPresent</td>
+ <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRangeValidation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/domChild[0]/domChild[4]/domChild[1]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRangeValidation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VCheckBox[0]/domChild[0]</td>
+ <td>26,6</td>
+</tr>
+<!--should be error-->
+<tr>
+ <td>assertElementPresent</td>
+ <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRangeValidation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/domChild[0]/domChild[4]/domChild[1]/domChild[0]</td>
+ <td></td>
+</tr>
+<!--select 3.12.2011-->
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRangeValidation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VPopupCalendar[0]#popupButton</td>
+ <td>13,17</td>
+</tr>
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRangeValidation::Root/VOverlay[0]/VCalendarPanel[0]#day3</td>
+ <td>13,13</td>
+</tr>
+<!--should be error-->
+<tr>
+ <td>assertElementPresent</td>
+ <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRangeValidation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/domChild[0]/domChild[4]/domChild[1]/domChild[0]</td>
+ <td></td>
+</tr>
+<!--change to 4.12.2011 by writing-->
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRangeValidation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VPopupCalendar[0]#field</td>
+ <td>52,6</td>
+</tr>
+<tr>
+ <td>enterCharacter</td>
+ <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRangeValidation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VPopupCalendar[0]#field</td>
+ <td>12/4/11</td>
+</tr>
+<!--should not be error-->
+<tr>
+ <td>assertElementNotPresent</td>
+ <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRangeValidation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/domChild[0]/domChild[4]/domChild[1]/domChild[0]</td>
+ <td></td>
+</tr>
+<!--no longer include start date-->
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRangeValidation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VCheckBox[0]/domChild[0]</td>
+ <td>6,8</td>
+</tr>
+<!--should be error-->
+<tr>
+ <td>assertElementPresent</td>
+ <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRangeValidation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/domChild[0]/domChild[4]/domChild[1]/domChild[0]</td>
+ <td></td>
+</tr>
+
+</tbody></table>
+</body>
+</html>
diff --git a/tests/testbench/com/vaadin/tests/components/datefield/DateFieldRangeValidation.java b/tests/testbench/com/vaadin/tests/components/datefield/DateFieldRangeValidation.java
new file mode 100644
index 0000000000..bd1a6655cd
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/components/datefield/DateFieldRangeValidation.java
@@ -0,0 +1,142 @@
+package com.vaadin.tests.components.datefield;
+
+import java.util.Date;
+
+import com.vaadin.data.Property.ValueChangeEvent;
+import com.vaadin.data.Property.ValueChangeListener;
+import com.vaadin.data.util.BeanItem;
+import com.vaadin.data.validator.RangeValidator;
+import com.vaadin.tests.components.TestBase;
+import com.vaadin.ui.CheckBox;
+import com.vaadin.ui.DateField.Resolution;
+import com.vaadin.ui.PopupDateField;
+
+public class DateFieldRangeValidation extends TestBase {
+
+ public class Range {
+ private Date from, to;
+ private boolean fromInclusive = true;
+ private boolean toInclusive = true;
+
+ public boolean isFromInclusive() {
+ return fromInclusive;
+ }
+
+ public void setFromInclusive(boolean fromInclusive) {
+ this.fromInclusive = fromInclusive;
+ }
+
+ public boolean isToInclusive() {
+ return toInclusive;
+ }
+
+ public void setToInclusive(boolean toInclusive) {
+ this.toInclusive = toInclusive;
+ }
+
+ public Date getFrom() {
+ return from;
+ }
+
+ public void setFrom(Date from) {
+ this.from = from;
+ }
+
+ public Date getTo() {
+ return to;
+ }
+
+ public void setTo(Date to) {
+ this.to = to;
+ }
+
+ }
+
+ private Range range = new Range();
+ private ValueChangeListener refreshField = new ValueChangeListener() {
+
+ public void valueChange(ValueChangeEvent event) {
+ actualDateField.requestRepaint();
+ }
+ };
+
+ private PopupDateField actualDateField;
+
+ @Override
+ protected void setup() {
+ BeanItem<Range> bi = new BeanItem<Range>(range);
+ range.setFrom(new Date(2011 - 1900, 12 - 1, 4));
+ range.setTo(new Date(2011 - 1900, 12 - 1, 15));
+
+ PopupDateField fromField = createDateField();
+ fromField.setPropertyDataSource(bi.getItemProperty("from"));
+ CheckBox fromInclusive = new CheckBox("From inclusive",
+ bi.getItemProperty("fromInclusive"));
+ CheckBox toInclusive = new CheckBox("To inclusive",
+ bi.getItemProperty("toInclusive"));
+ fromInclusive.setImmediate(true);
+ fromInclusive.addListener(refreshField);
+ toInclusive.setImmediate(true);
+ toInclusive.addListener(refreshField);
+
+ PopupDateField toField = createDateField();
+ toField.setPropertyDataSource(bi.getItemProperty("to"));
+
+ actualDateField = createDateField();
+ actualDateField.addValidator(new RangeValidator<Date>("", Date.class,
+ null, null) {
+ @Override
+ public boolean isMinValueIncluded() {
+ return range.isFromInclusive();
+ }
+
+ @Override
+ public boolean isMaxValueIncluded() {
+ return range.isToInclusive();
+ }
+
+ @Override
+ public Date getMaxValue() {
+ return range.getTo();
+ }
+
+ @Override
+ public Date getMinValue() {
+ return range.getFrom();
+ }
+
+ @Override
+ public String getErrorMessage() {
+ return "Date must be in range " + getMinValue() + " - "
+ + getMaxValue();
+ }
+ });
+ addComponent(fromField);
+ addComponent(fromInclusive);
+ addComponent(toField);
+ addComponent(toInclusive);
+ addComponent(actualDateField);
+ }
+
+ private PopupDateField createDateField() {
+ PopupDateField df = new PopupDateField();
+ df.setResolution(Resolution.DAY);
+ df.setValue(new Date());
+ df.setWriteThrough(true);
+ df.setReadThrough(true);
+ df.setImmediate(true);
+ return df;
+ }
+
+ @Override
+ protected String getDescription() {
+ return "Tests the DateField range validator. The first field sets the minimum date, the second the maximum. Checkboxes control if the selected date is ok or not.";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
diff --git a/tests/testbench/com/vaadin/tests/components/datefield/RequiredInvalidDateField.java b/tests/testbench/com/vaadin/tests/components/datefield/RequiredInvalidDateField.java
index 6721ddcbdb..a89459d37a 100644
--- a/tests/testbench/com/vaadin/tests/components/datefield/RequiredInvalidDateField.java
+++ b/tests/testbench/com/vaadin/tests/components/datefield/RequiredInvalidDateField.java
@@ -35,16 +35,21 @@ public class RequiredInvalidDateField extends TestBase {
Date date = new Date(2011 - 1900, 9 - 1, 1);
- Validator dateValidator = new AbstractValidator(
+ Validator dateValidator = new AbstractValidator<Date>(
"Day of month must be an even number") {
@Override
- protected boolean isValidValue(Object value) {
- if (!(value instanceof Date)) {
- return false;
+ protected boolean isValidValue(Date value) {
+ if (value == null) {
+ return true;
}
- Date date = (Date) value;
- return (date.getDate() % 2 == 0);
+
+ return (value.getDate() % 2 == 0);
+ }
+
+ @Override
+ public Class getType() {
+ return Date.class;
}
};
diff --git a/tests/testbench/com/vaadin/tests/util/AlwaysFailValidator.java b/tests/testbench/com/vaadin/tests/util/AlwaysFailValidator.java
index 65b67e5077..ddb58999ee 100644
--- a/tests/testbench/com/vaadin/tests/util/AlwaysFailValidator.java
+++ b/tests/testbench/com/vaadin/tests/util/AlwaysFailValidator.java
@@ -2,7 +2,7 @@ package com.vaadin.tests.util;
import com.vaadin.data.validator.AbstractValidator;
-public class AlwaysFailValidator extends AbstractValidator {
+public class AlwaysFailValidator extends AbstractValidator<Object> {
public AlwaysFailValidator() {
super("Validation error");
}
@@ -15,4 +15,9 @@ public class AlwaysFailValidator extends AbstractValidator {
protected boolean isValidValue(Object value) {
return false;
}
+
+ @Override
+ public Class getType() {
+ return Object.class;
+ }
}
diff --git a/tests/testbench/com/vaadin/tests/validation/TestValidators.java b/tests/testbench/com/vaadin/tests/validation/TestValidators.java
index 9523ff96b0..a530dee852 100644
--- a/tests/testbench/com/vaadin/tests/validation/TestValidators.java
+++ b/tests/testbench/com/vaadin/tests/validation/TestValidators.java
@@ -3,12 +3,12 @@ package com.vaadin.tests.validation;
import com.vaadin.data.Validator;
import com.vaadin.data.validator.AbstractStringValidator;
import com.vaadin.data.validator.CompositeValidator;
+import com.vaadin.data.validator.CompositeValidator.CombinationMode;
import com.vaadin.data.validator.DoubleValidator;
import com.vaadin.data.validator.EmailValidator;
import com.vaadin.data.validator.IntegerValidator;
import com.vaadin.data.validator.RegexpValidator;
import com.vaadin.data.validator.StringLengthValidator;
-import com.vaadin.data.validator.CompositeValidator.CombinationMode;
import com.vaadin.tests.components.TestBase;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
@@ -97,8 +97,7 @@ public class TestValidators extends TestBase {
// TODO CompositeValidator
tf = new TextField(
"A field, must be a floating point number with 4-5 chars");
- CompositeValidator cv = new CompositeValidator(
- CombinationMode.AND,
+ CompositeValidator cv = new CompositeValidator(CombinationMode.AND,
"The field must contain a floating point number with 4-5 characters");
cv.addValidator(new StringLengthValidator(
"String length of '{0}' should be 4-5 characters", 4, 5, false));
@@ -128,7 +127,7 @@ public class TestValidators extends TestBase {
Validator postalCodeValidator = new AbstractStringValidator(
"Postal code must be a number 10000-99999.") {
@Override
- protected boolean isValidString(String value) {
+ protected boolean isValidValue(String value) {
return value.matches("[1-9][0-9]{4}");
}
};