@Override
public Result<T> convertToModel(T value, ValueContext context) {
- Result<? super T> validationResult = validator.apply(value);
+ Result<? super T> validationResult = validator.apply(value,
+ context);
if (validationResult.isError()) {
return Result.error(validationResult.getMessage().get());
} else {
return this;
}
+ /**
+ * A convenience method to add a validator to this binder using the
+ * {@link Validator#from(SerializablePredicate, String)} factory method.
+ * <p>
+ * Bean level validators are applied on the bean instance after the bean is
+ * updated. If the validators fail, the bean instance is reverted to its
+ * previous state.
+ *
+ * @see #save(Object)
+ * @see #saveIfValid(Object)
+ *
+ * @param predicate
+ * the predicate performing validation, not null
+ * @param message
+ * the error message to report in case validation failure
+ * @return this binder, for chaining
+ */
+ public Binder<BEAN> withValidator(SerializablePredicate<BEAN> predicate,
+ String message) {
+ return withValidator(Validator.from(predicate, message));
+ }
+
/**
* Validates the values of all bound fields and returns the validation
* status.
*/
private List<Result<?>> validateBean(BEAN bean) {
Objects.requireNonNull(bean, "bean cannot be null");
- List<Result<?>> results = Collections.unmodifiableList(
- validators.stream().map(validator -> validator.apply(bean))
- .collect(Collectors.toList()));
+ List<Result<?>> results = Collections.unmodifiableList(validators
+ .stream()
+ .map(validator -> validator.apply(bean, new ValueContext()))
+ .collect(Collectors.toList()));
return results;
}
import java.io.Serializable;
import java.util.Objects;
-import java.util.function.Function;
-import java.util.function.Predicate;
+import java.util.function.BiFunction;
+import com.vaadin.data.util.converter.ValueContext;
import com.vaadin.server.SerializablePredicate;
/**
* @see Result
*/
@FunctionalInterface
-public interface Validator<T> extends Function<T, Result<T>>, Serializable {
-
- /**
- * Returns a validator that chains this validator with the given function.
- * Specifically, the function may be another validator. The resulting
- * validator first applies this validator, and if the value passes, then the
- * given validator.
- * <p>
- * For instance, the following chained validator checks if a number is
- * between 0 and 10, inclusive:
- *
- * <pre>
- * Validator<Integer> v = Validator.from(num -> num >= 0, "number must be >= 0")
- * .chain(Validator.from(num -> num <= 10, "number must be <= 10"));
- * </pre>
- *
- * @param next
- * the validator to apply next, not null
- * @return a chained validator
- *
- * @see #from(Predicate, String)
- */
- public default Validator<T> chain(Function<T, Result<T>> next) {
- Objects.requireNonNull(next, "next cannot be null");
- return val -> apply(val).flatMap(next);
- }
+public interface Validator<T>
+ extends BiFunction<T, ValueContext, Result<T>>, Serializable {
/**
* Validates the given value. Returns a {@code Result} instance representing
*
* @param value
* the input value to validate
+ * @param context
+ * the value context for validation
* @return the validation result
*/
@Override
- public Result<T> apply(T value);
+ public Result<T> apply(T value, ValueContext context);
/**
* Returns a validator that passes any value.
* @return an always-passing validator
*/
public static <T> Validator<T> alwaysPass() {
- return v -> Result.ok(v);
+ return (v, ctx) -> Result.ok(v);
}
/**
String errorMessage) {
Objects.requireNonNull(guard, "guard cannot be null");
Objects.requireNonNull(errorMessage, "errorMessage cannot be null");
- return value -> {
+ return (value, context) -> {
try {
if (guard.test(value)) {
return Result.ok(value);
* Constructor for {@code ValueContext} without a {@code Locale}.
*/
public ValueContext() {
- this.locale = null;
this.component = null;
+ this.locale = findLocale();
}
/**
import com.vaadin.data.Result;
import com.vaadin.data.Validator;
import com.vaadin.data.util.BeanUtil;
+import com.vaadin.data.util.converter.ValueContext;
/**
* A {@code Validator} using the JSR-303 (javax.validation) annotation-based
* annotation or equivalent.
*/
@Override
- public Result<Object> apply(final Object value) {
+ public Result<Object> apply(final Object value, ValueContext context) {
Set<? extends ConstraintViolation<?>> violations = getJavaxBeanValidator()
.validateValue(beanType, propertyName, value);
import com.vaadin.data.HasRequired;
import com.vaadin.data.Result;
import com.vaadin.data.Validator;
+import com.vaadin.data.util.converter.ValueContext;
/**
* Simple validator to check against {@code null} value and empty {@link String}
}
@Override
- public Result<T> apply(T value) {
+ public Result<T> apply(T value, ValueContext context) {
if (Objects.isNull(value) || Objects.equals(value, "")) {
return Result.error(message);
} else {
import java.util.Objects;
import com.vaadin.data.Result;
+import com.vaadin.data.util.converter.ValueContext;
/**
* This validator is used for validating properties that do not allow null
}
@Override
- public Result<String> apply(String value) {
+ public Result<String> apply(String value, ValueContext context) {
return Objects.isNull(value) ? Result.error(getMessage(value))
: Result.ok(value);
}
import java.util.Objects;
import com.vaadin.data.Result;
+import com.vaadin.data.util.converter.ValueContext;
/**
* Verifies that a value is within the given range.
* behavior depends on the used comparator.
*/
@Override
- public Result<T> apply(T value) {
+ public Result<T> apply(T value, ValueContext context) {
return toResult(value, isValid(value));
}
import java.util.regex.Pattern;
import com.vaadin.data.Result;
+import com.vaadin.data.util.converter.ValueContext;
/**
* A string validator comparing the string against a Java regular expression.
}
@Override
- public Result<String> apply(String value) {
+ public Result<String> apply(String value, ValueContext context) {
return toResult(value, isValid(value));
}
package com.vaadin.data.validator;
import com.vaadin.data.Result;
+import com.vaadin.data.util.converter.ValueContext;
/**
* Verifies that the length of a string is within the given range.
}
@Override
- public Result<String> apply(String value) {
+ public Result<String> apply(String value, ValueContext context) {
if (value == null) {
return toResult(value, true);
}
- Result<?> lengthCheck = validator.apply(value.length());
+ Result<?> lengthCheck = validator.apply(value.length(), context);
return toResult(value, !lengthCheck.isError());
}
import org.jsoup.nodes.Element;
import com.vaadin.data.Result;
+import com.vaadin.data.util.converter.ValueContext;
import com.vaadin.data.validator.DateRangeValidator;
import com.vaadin.event.FieldEvents.BlurEvent;
import com.vaadin.event.FieldEvents.BlurListener;
getDateOutOfRangeMessage(),
getDate(getRangeStart(), getResolution()),
getDate(getRangeEnd(), getResolution()));
- Result<LocalDate> result = validator.apply(value);
+ Result<LocalDate> result = validator.apply(value,
+ new ValueContext(this));
if (result.isError()) {
setComponentError(new UserError(getDateOutOfRangeMessage()));
}
binder.forField(yearOfBirth)
.withConverter(new StringToIntegerConverter("err"))
.bind(BookPerson::getYearOfBirth, BookPerson::setYearOfBirth);
- binder.withValidator(bean -> bean.yearOfBirth < 2000 ? Result.ok(bean)
- : Result.error(message))
- .withValidator(bean -> bean.yearOfBirth == 2000
- ? Result.error(message2) : Result.ok(bean));
+ binder.withValidator(bean -> bean.yearOfBirth < 2000, message)
+ .withValidator(bean -> bean.yearOfBirth != 2000, message2);
binder.setBean(p);
BookPerson p = new BookPerson(1500, 12);
binder.forField(yearOfBirth)
.withConverter(new StringToIntegerConverter("err"))
- .withValidator(value -> value % 2 == 0 ? Result.ok(value)
- : Result.error(bindingMessage))
+ .withValidator(value -> value % 2 == 0, bindingMessage)
.bind(BookPerson::getYearOfBirth, BookPerson::setYearOfBirth);
- binder.withValidator(bean -> bean.yearOfBirth < 2000 ? Result.ok(bean)
- : Result.error(message))
- .withValidator(bean -> bean.yearOfBirth == 2000
- ? Result.error(message2) : Result.ok(bean));
+ binder.withValidator(bean -> bean.yearOfBirth < 2000, message)
+ .withValidator(bean -> bean.yearOfBirth != 2000, message2);
binder.setBean(p);
import com.vaadin.data.Binder.Binding;
import com.vaadin.data.util.converter.StringToIntegerConverter;
+import com.vaadin.data.util.converter.ValueContext;
import com.vaadin.data.validator.NotEmptyValidator;
import com.vaadin.server.AbstractErrorMessage;
import com.vaadin.server.ErrorMessage;
String msg2 = "bar";
binding.withValidator(new Validator<String>() {
@Override
- public Result<String> apply(String value) {
+ public Result<String> apply(String value, ValueContext context) {
return new SimpleResult<>(null, msg1);
}
});
// validator for Number can be used on a Double
TextField salaryField = new TextField();
- Validator<Number> positiveNumberValidator = value -> {
+ Validator<Number> positiveNumberValidator = (value, context) -> {
if (value.doubleValue() >= 0) {
return Result.ok(value);
} else {
Binding<Person, String, String> binding = binder.forField(nameField)
.withValidator(notEmpty);
binding.bind(Person::getFirstName, Person::setFirstName);
- binder.withValidator(bean -> bean.getFirstName().contains("error")
- ? Result.error("error") : Result.ok(bean));
+ binder.withValidator(bean -> !bean.getFirstName().contains("error"),
+ "error");
Person person = new Person();
person.setFirstName("");
binder.setBean(person);
/*
* Copyright 2000-2016 Vaadin Ltd.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
import com.vaadin.tests.data.bean.Person;
import com.vaadin.ui.Label;
-public class BinderValidationStatusTest extends
- BinderTestBase<Binder<Person>, Person> {
+public class BinderValidationStatusTest
+ extends BinderTestBase<Binder<Person>, Person> {
protected final static ValidationStatusHandler NOOP = event -> {
};
"Using a custom status change handler so no change should end up here");
}).bind(Person::getAge, Person::setAge);
binder.withValidator(
- bean -> !bean.getFirstName().isEmpty() && bean.getAge() > 0
- ? Result.ok(bean)
- : Result.error("Need first name and age"));
+ bean -> !bean.getFirstName().isEmpty() && bean.getAge() > 0,
+ "Need first name and age");
binder.setValidationStatusHandler(r -> {
statusCapture.set(r);
import org.junit.Assert;
import org.junit.Test;
+import com.vaadin.data.util.converter.ValueContext;
+
/**
* @author Vaadin Ltd
*
@Test
public void alwaysPass() {
Validator<String> alwaysPass = Validator.alwaysPass();
- Result<String> result = alwaysPass.apply("foo");
+ Result<String> result = alwaysPass.apply("foo", new ValueContext());
Assert.assertTrue(result instanceof SimpleResult);
SimpleResult<String> implRes = (SimpleResult<String>) result;
Assert.assertFalse(implRes.getMessage().isPresent());
}
- @Test
- public void chain_alwaysPassAndError() {
- Validator<String> alwaysPass = Validator.alwaysPass();
- Validator<String> chain = alwaysPass
- .chain(value -> Result.error("foo"));
- Result<String> result = chain.apply("bar");
- Assert.assertTrue(result.isError());
- Assert.assertEquals("foo", result.getMessage().get());
- }
-
- @SuppressWarnings("serial")
- @Test
- public void chain_mixture() {
- Validator<String> first = new Validator<String>() {
-
- @Override
- public Result<String> apply(String value) {
- if (value == null) {
- return Result.error("Cannot be null");
- }
- return Result.ok(value);
- }
- };
- Validator<String> second = new Validator<String>() {
-
- @Override
- public Result<String> apply(String value) {
- if (value != null && value.isEmpty()) {
- return Result.error("Cannot be empty");
- }
- return Result.ok(value);
- }
- };
-
- Validator<String> chain = first.chain(second);
- Result<String> result = chain.apply("bar");
- Assert.assertFalse(result.isError());
-
- result = chain.apply(null);
- Assert.assertTrue(result.isError());
-
- result = chain.apply("");
- Assert.assertTrue(result.isError());
- }
-
@Test
public void from() {
Validator<String> validator = Validator.from(Objects::nonNull,
"Cannot be null");
- Result<String> result = validator.apply(null);
+ Result<String> result = validator.apply(null, new ValueContext());
Assert.assertTrue(result.isError());
- result = validator.apply("");
+ result = validator.apply("", new ValueContext());
Assert.assertFalse(result.isError());
}
}
import org.junit.Test;
import com.vaadin.data.Result;
+import com.vaadin.data.util.converter.ValueContext;
/**
* @author Vaadin Ltd
@Test
public void nullValueIsDisallowed() {
NotEmptyValidator<String> validator = new NotEmptyValidator<>("foo");
- Result<String> result = validator.apply(null);
+ Result<String> result = validator.apply(null, new ValueContext());
Assert.assertTrue(result.isError());
Assert.assertEquals("foo", result.getMessage().get());
}
@Test
public void emptyValueIsDisallowed() {
NotEmptyValidator<String> validator = new NotEmptyValidator<>("foo");
- Result<String> result = validator.apply("");
+ Result<String> result = validator.apply("", new ValueContext());
Assert.assertTrue(result.isError());
Assert.assertEquals("foo", result.getMessage().get());
}
public void nonNullValueIsAllowed() {
NotEmptyValidator<Object> validator = new NotEmptyValidator<>("foo");
Object value = new Object();
- Result<Object> result = validator.apply(value);
+ Result<Object> result = validator.apply(value, new ValueContext());
Assert.assertFalse(result.isError());
result.ifOk(val -> Assert.assertEquals(value, val));
result.ifError(msg -> Assert.fail());
import org.junit.Test;
import com.vaadin.data.Result;
+import com.vaadin.data.util.converter.ValueContext;
public class NotNullValidatorTest {
@Test
public void nullValueIsDisallowed() {
NotNullValidator validator = new NotNullValidator("foo");
- Result<String> result = validator.apply(null);
+ Result<String> result = validator.apply(null, new ValueContext());
Assert.assertTrue(result.isError());
Assert.assertEquals("foo", result.getMessage().get());
}
@Test
public void nonNullValueIsAllowed() {
NotNullValidator validator = new NotNullValidator("foo");
- Result<String> result = validator.apply("bar");
+ Result<String> result = validator.apply("bar", new ValueContext());
Assert.assertFalse(result.isError());
result.ifOk(value -> Assert.assertEquals("bar", value));
result.ifError(msg -> Assert.fail());
import org.junit.Assert;
import com.vaadin.data.Validator;
+import com.vaadin.data.util.converter.ValueContext;
public class ValidatorTestBase {
protected <T> void assertPasses(T value, Validator<? super T> v) {
- v.apply(value).handle(val -> Assert.assertEquals(value, val),
- err -> Assert
+ v.apply(value, new ValueContext())
+ .handle(val -> Assert.assertEquals(value, val), err -> Assert
.fail(value + " should pass " + v + " but got " + err));
}
protected <T> void assertFails(T value, String errorMessage,
Validator<? super T> v) {
- v.apply(value).handle(val -> Assert.fail(value + " should fail " + v),
+ v.apply(value, new ValueContext()).handle(
+ val -> Assert.fail(value + " should fail " + v),
err -> Assert.assertEquals(errorMessage, err));
}