Change-Id: I657535d377c471369e8c77fa1db946c490023939tags/8.0.0.alpha6
@@ -149,6 +149,9 @@ public class Binder<BEAN> implements Serializable { | |||
* property. If any validator returns a failure, the property value is | |||
* not updated. | |||
* | |||
* @see #withValidator(SerializablePredicate, String) | |||
* @see #withValidator(SerializablePredicate, ErrorMessageProvider) | |||
* | |||
* @param validator | |||
* the validator to add, not null | |||
* @return this binding, for chaining | |||
@@ -167,6 +170,7 @@ public class Binder<BEAN> implements Serializable { | |||
* failure, the property value is not updated. | |||
* | |||
* @see #withValidator(Validator) | |||
* @see #withValidator(SerializablePredicate, ErrorMessageProvider) | |||
* @see Validator#from(SerializablePredicate, String) | |||
* | |||
* @param predicate | |||
@@ -183,6 +187,34 @@ public class Binder<BEAN> implements Serializable { | |||
return withValidator(Validator.from(predicate, message)); | |||
} | |||
/** | |||
* A convenience method to add a validator to this binding using the | |||
* {@link Validator#from(SerializablePredicate, ErrorMessageProvider)} | |||
* factory method. | |||
* <p> | |||
* Validators are applied, in registration order, when the field value | |||
* is written to the backing property. If any validator returns a | |||
* failure, the property value is not updated. | |||
* | |||
* @see #withValidator(Validator) | |||
* @see #withValidator(SerializablePredicate, String) | |||
* @see Validator#from(SerializablePredicate, ErrorMessageProvider) | |||
* | |||
* @param predicate | |||
* the predicate performing validation, not null | |||
* @param errorMessageProvider | |||
* the provider to generate error messages, not null | |||
* @return this binding, for chaining | |||
* @throws IllegalStateException | |||
* if {@code bind} has already been called | |||
*/ | |||
public default Binding<BEAN, FIELDVALUE, TARGET> withValidator( | |||
SerializablePredicate<? super TARGET> predicate, | |||
ErrorMessageProvider errorMessageProvider) { | |||
return withValidator( | |||
Validator.from(predicate, errorMessageProvider)); | |||
} | |||
/** | |||
* Maps the binding to another data type using the given | |||
* {@link Converter}. | |||
@@ -1062,6 +1094,8 @@ public class Binder<BEAN> implements Serializable { | |||
* | |||
* @see #writeBean(Object) | |||
* @see #writeBeanIfValid(Object) | |||
* @see #withValidator(SerializablePredicate, String) | |||
* @see #withValidator(SerializablePredicate, ErrorMessageProvider) | |||
* | |||
* @param validator | |||
* the validator to add, not null | |||
@@ -1081,8 +1115,10 @@ public class Binder<BEAN> implements Serializable { | |||
* updated. If the validators fail, the bean instance is reverted to its | |||
* previous state. | |||
* | |||
* @see #save(Object) | |||
* @see #saveIfValid(Object) | |||
* @see #writeBean(Object) | |||
* @see #writeBeanIfValid(Object) | |||
* @see #withValidator(Validator) | |||
* @see #withValidator(SerializablePredicate, ErrorMessageProvider) | |||
* | |||
* @param predicate | |||
* the predicate performing validation, not null | |||
@@ -1095,6 +1131,31 @@ public class Binder<BEAN> implements Serializable { | |||
return withValidator(Validator.from(predicate, message)); | |||
} | |||
/** | |||
* A convenience method to add a validator to this binder using the | |||
* {@link Validator#from(SerializablePredicate, ErrorMessageProvider)} | |||
* 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 #writeBean(Object) | |||
* @see #writeBeanIfValid(Object) | |||
* @see #withValidator(Validator) | |||
* @see #withValidator(SerializablePredicate, String) | |||
* | |||
* @param predicate | |||
* the predicate performing validation, not null | |||
* @param errorMessageProvider | |||
* the provider to generate error messages, not null | |||
* @return this binder, for chaining | |||
*/ | |||
public Binder<BEAN> withValidator(SerializablePredicate<BEAN> predicate, | |||
ErrorMessageProvider errorMessageProvider) { | |||
return withValidator(Validator.from(predicate, errorMessageProvider)); | |||
} | |||
/** | |||
* Validates the values of all bound fields and returns the validation | |||
* status. |
@@ -0,0 +1,42 @@ | |||
/* | |||
* 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 | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.data; | |||
import com.vaadin.data.util.converter.ValueContext; | |||
import com.vaadin.server.SerializableFunction; | |||
/** | |||
* Provider interface for generating localizable error messages using | |||
* {@link ValueContext}. | |||
* | |||
* @since | |||
* @author Vaadin Ltd. | |||
*/ | |||
@FunctionalInterface | |||
public interface ErrorMessageProvider | |||
extends SerializableFunction<ValueContext, String> { | |||
/** | |||
* Returns a generated error message for given {@code ValueContext}. | |||
* | |||
* @param context | |||
* the value context | |||
* | |||
* @return generated error message | |||
*/ | |||
@Override | |||
public String apply(ValueContext context); | |||
} |
@@ -101,15 +101,37 @@ public interface Validator<T> | |||
String errorMessage) { | |||
Objects.requireNonNull(guard, "guard cannot be null"); | |||
Objects.requireNonNull(errorMessage, "errorMessage cannot be null"); | |||
return from(guard, ctx -> errorMessage); | |||
} | |||
/** | |||
* Builds a validator out of a conditional function and an error message | |||
* provider. If the function returns true, the validator returns | |||
* {@code Result.ok()}; if it returns false or throws an exception, | |||
* {@code Result.error()} is returned with the message from the provider. | |||
* | |||
* @param <T> | |||
* the value type | |||
* @param guard | |||
* the function used to validate, not null | |||
* @param errorMessageProvider | |||
* the provider to generate error messages, not null | |||
* @return the new validator using the function | |||
*/ | |||
public static <T> Validator<T> from(SerializablePredicate<T> guard, | |||
ErrorMessageProvider errorMessageProvider) { | |||
Objects.requireNonNull(guard, "guard cannot be null"); | |||
Objects.requireNonNull(errorMessageProvider, | |||
"errorMessageProvider cannot be null"); | |||
return (value, context) -> { | |||
try { | |||
if (guard.test(value)) { | |||
return Result.ok(value); | |||
} else { | |||
return Result.error(errorMessage); | |||
return Result.error(errorMessageProvider.apply(context)); | |||
} | |||
} catch (Exception e) { | |||
return Result.error(errorMessage); | |||
return Result.error(errorMessageProvider.apply(context)); | |||
} | |||
}; | |||
} |
@@ -15,18 +15,20 @@ | |||
*/ | |||
package com.vaadin.data; | |||
import java.util.Locale; | |||
import java.util.Objects; | |||
import org.junit.Assert; | |||
import org.junit.Test; | |||
import com.vaadin.data.util.converter.ValueContext; | |||
import com.vaadin.data.validator.ValidatorTestBase; | |||
/** | |||
* @author Vaadin Ltd | |||
* | |||
*/ | |||
public class ValidatorTest { | |||
public class ValidatorTest extends ValidatorTestBase { | |||
@Test | |||
public void alwaysPass() { | |||
@@ -47,4 +49,30 @@ public class ValidatorTest { | |||
result = validator.apply("", new ValueContext()); | |||
Assert.assertFalse(result.isError()); | |||
} | |||
@Test | |||
public void withValidator_customErrorMessageProvider() { | |||
String finnishError = "Käyttäjän tulee olla täysi-ikäinen"; | |||
String englishError = "The user must be an adult"; | |||
String notTranslatableError = "NOT TRANSLATABLE"; | |||
Validator<Integer> ageValidator = Validator.from(age -> age >= 18, | |||
ctx -> { | |||
Locale locale = ctx.getLocale().orElse(Locale.ENGLISH); | |||
if (locale.getLanguage().equals("fi")) { | |||
return finnishError; | |||
} else if (locale.getLanguage().equals("en")) { | |||
return englishError; | |||
} | |||
return notTranslatableError; | |||
}); | |||
setLocale(Locale.ENGLISH); | |||
assertFails(17, englishError, ageValidator); | |||
setLocale(new Locale("fi", "FI")); | |||
assertFails(17, finnishError, ageValidator); | |||
setLocale(Locale.GERMAN); | |||
assertFails(17, notTranslatableError, ageValidator); | |||
} | |||
} |
@@ -1,12 +1,23 @@ | |||
package com.vaadin.data.validator; | |||
import java.util.Locale; | |||
import org.junit.Assert; | |||
import org.junit.Before; | |||
import com.vaadin.data.Validator; | |||
import com.vaadin.data.util.converter.ValueContext; | |||
import com.vaadin.ui.Label; | |||
public class ValidatorTestBase { | |||
private Label localeContext; | |||
@Before | |||
public void setUp() { | |||
localeContext = new Label(); | |||
} | |||
protected <T> void assertPasses(T value, Validator<? super T> v) { | |||
v.apply(value, new ValueContext()) | |||
.handle(val -> Assert.assertEquals(value, val), err -> Assert | |||
@@ -15,7 +26,7 @@ public class ValidatorTestBase { | |||
protected <T> void assertFails(T value, String errorMessage, | |||
Validator<? super T> v) { | |||
v.apply(value, new ValueContext()).handle( | |||
v.apply(value, new ValueContext(localeContext)).handle( | |||
val -> Assert.fail(value + " should fail " + v), | |||
err -> Assert.assertEquals(errorMessage, err)); | |||
} | |||
@@ -23,4 +34,8 @@ public class ValidatorTestBase { | |||
protected <T> void assertFails(T value, AbstractValidator<? super T> v) { | |||
assertFails(value, v.getMessage(value), v); | |||
} | |||
} | |||
protected void setLocale(Locale locale) { | |||
localeContext.setLocale(locale); | |||
} | |||
} |