Parcourir la source

Binding.withValidator and Binder.validate methods (#26).

Change-Id: I0641ea6118cd873c803d3c21d82b14fe8db4baa2
tags/8.0.0.alpha1
Denis Anisimov il y a 7 ans
Parent
révision
8139cd8c89

+ 2
- 2
documentation/datamodel/datamodel-forms.asciidoc Voir le fichier

@@ -322,7 +322,7 @@ Even if the user has not edited a field, all validation error will be shown if w
binder.load(new Person());

// This will make all current validation errors visible
Set<BinderResult> validationErrors = binder.validate();
List<ValidationError<?>> validationErrors = binder.validate();

if (!validationErrors.isEmpty()) {
Notification.show("Validation error count: "
@@ -594,7 +594,7 @@ Button saveButton = new Button("Save", event -> {
// Create non-shared copy to use for validation
Person copy = new Person(person);

Set<BinderResult> errors = binder.validateWithBean(copy);
List<ValidationError<?>> errors = binder.validateWithBean(copy);
if (errors.isEmpty()) {
// Write new values to the actual bean


+ 130
- 3
server/src/main/java/com/vaadin/data/Binder.java Voir le fichier

@@ -16,14 +16,20 @@
package com.vaadin.data;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import com.vaadin.event.Registration;
import com.vaadin.server.UserError;
import com.vaadin.ui.AbstractComponent;

/**
* Connects one or more {@code Field} components to properties of a backing data
@@ -105,6 +111,42 @@ public class Binder<T> implements Serializable {
* if {@code bind} has already been called on this binding
*/
public void bind(Function<T, V> getter, BiConsumer<T, V> setter);

/**
* Adds a validator to this binding. Validators are applied, in
* registration order, when the field value is saved to the backing
* property. If any validator returns a failure, the property value is
* not updated.
*
* @param validator
* the validator to add, not null
* @return this binding, for chaining
* @throws IllegalStateException
* if {@code bind} has already been called
*/
public Binding<T, V> withValidator(Validator<? super V> validator);

/**
* A convenience method to add a validator to this binding using the
* {@link Validator#from(Predicate, String)} factory method.
* <p>
* Validators are applied, in registration order, when the field value
* is saved to the backing property. If any validator returns a failure,
* the property value is not updated.
*
* @see #withValidator(Validator)
* @see Validator#from(Predicate, String)
*
* @param predicate
* the predicate performing validation, not null
* @param message
* the error message to report in case validation failure
* @return this binding, for chaining
* @throws IllegalStateException
* if {@code bind} has already been called
*/
public Binding<T, V> withValidator(Predicate<? super V> predicate,
String message);
}

/**
@@ -121,6 +163,8 @@ public class Binder<T> implements Serializable {
private Function<T, V> getter;
private BiConsumer<T, V> setter;

private List<Validator<? super V>> validators = new ArrayList<>();

/**
* Creates a new binding associated with the given field.
*
@@ -144,12 +188,35 @@ public class Binder<T> implements Serializable {
}
}

@Override
public Binding<T, V> withValidator(Validator<? super V> validator) {
checkUnbound();
Objects.requireNonNull(validator, "validator cannot be null");
validators.add(validator);
return this;
}

@Override
public Binding<T, V> withValidator(Predicate<? super V> predicate,
String message) {
return withValidator(Validator.from(predicate, message));
}

private void bind(T bean) {
setFieldValue(bean);
onValueChange = field
.addValueChangeListener(e -> storeFieldValue(bean));
}

private List<ValidationError<V>> validate() {
return validators.stream()
.map(validator -> validator.apply(field.getValue()))
.filter(Result::isError)
.map(result -> new ValidationError<>(field,
result.getMessage().orElse(null)))
.collect(Collectors.toList());
}

private void unbind() {
onValueChange.remove();
}
@@ -186,6 +253,7 @@ public class Binder<T> implements Serializable {
"cannot modify binding: already bound to a property");
}
}

}

private T bean;
@@ -286,6 +354,27 @@ public class Binder<T> implements Serializable {
bindings.forEach(b -> b.bind(bean));
}

/**
* Validates the values of all bound fields and returns the result of the
* validation as a set of validation errors.
* <p>
* Validation is successful if the resulting set is empty.
*
* @return the validation result.
*/
public List<ValidationError<?>> validate() {
List<ValidationError<?>> resultErrors = new ArrayList<>();
for (BindingImpl<?> binding : bindings) {
clearError(binding.field);
List<? extends ValidationError<?>> errors = binding.validate();
resultErrors.addAll(errors);
if (!errors.isEmpty()) {
handleError(binding.field, errors.get(0).getMessage());
}
}
return resultErrors;
}

/**
* Unbinds the currently bound bean if any. If there is no bound bean, does
* nothing.
@@ -293,7 +382,7 @@ public class Binder<T> implements Serializable {
public void unbind() {
if (bean != null) {
bean = null;
bindings.forEach(b -> b.unbind());
bindings.forEach(BindingImpl::unbind);
}
}

@@ -308,7 +397,10 @@ public class Binder<T> implements Serializable {
*/
public void load(T bean) {
Objects.requireNonNull(bean, "bean cannot be null");
bindings.forEach(binding -> binding.setFieldValue(bean));
bindings.forEach(

binding -> binding.setFieldValue(bean));

}

/**
@@ -323,7 +415,10 @@ public class Binder<T> implements Serializable {
*/
public void save(T bean) {
Objects.requireNonNull(bean, "bean cannot be null");
bindings.forEach(binding -> binding.storeFieldValue(bean));
bindings.forEach(

binding -> binding.storeFieldValue(bean));

}

/**
@@ -341,4 +436,36 @@ public class Binder<T> implements Serializable {
return b;
}

/**
* Clears the error condition of the given field, if any. The default
* implementation clears the
* {@link AbstractComponent#setComponentError(ErrorMessage) component error}
* of the field if it is a Component, otherwise does nothing.
*
* @param field
* the field with an invalid value
*/
protected void clearError(HasValue<?> field) {
if (field instanceof AbstractComponent) {
((AbstractComponent) field).setComponentError(null);
}
}

/**
* Handles a validation error emitted when trying to save the value of the
* given field. The default implementation sets the
* {@link AbstractComponent#setComponentError(ErrorMessage) component error}
* of the field if it is a Component, otherwise does nothing.
*
* @param field
* the field with the invalid value
* @param error
* the error message to set
*/
protected void handleError(HasValue<?> field, String error) {
if (field instanceof AbstractComponent) {
((AbstractComponent) field).setComponentError(new UserError(error));
}
}

}

+ 170
- 0
server/src/main/java/com/vaadin/data/Result.java Voir le fichier

@@ -0,0 +1,170 @@
/*
* Copyright 2000-2014 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 java.io.Serializable;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
* Represents the result of an operation that might fail, such as input
* validation or type conversion. A result may contain either a value,
* signifying a successful operation, or an error message in case of a failure.
* <p>
* Result instances are created using the factory methods {@link #ok(R)} and
* {@link #error(String)}, denoting success and failure respectively.
* <p>
* Unless otherwise specified, {@code Result} method arguments cannot be null.
*
* @param <R>
* the result value type
*/
public interface Result<R> extends Serializable {

/**
* Returns a successful result wrapping the given value.
*
* @param <R>
* the result value type
* @param value
* the result value, can be null
* @return a successful result
*/
public static <R> Result<R> ok(R value) {
return new SimpleResult<>(value, null);
}

/**
* Returns a failure result wrapping the given error message.
*
* @param <R>
* the result value type
* @param message
* the error message
* @return a failure result
*/
public static <R> Result<R> error(String message) {
Objects.requireNonNull(message, "message cannot be null");
return new SimpleResult<R>(null, message);
}

/**
* Returns a Result representing the result of invoking the given supplier.
* If the supplier returns a value, returns a {@code Result.ok} of the
* value; if an exception is thrown, returns the message in a
* {@code Result.error}.
*
* @param <R>
* the result value type
* @param supplier
* the supplier to run
* @param onError
* the function to provide the error message
* @return the result of invoking the supplier
*/
public static <R> Result<R> of(Supplier<R> supplier,
Function<Exception, String> onError) {
Objects.requireNonNull(supplier, "supplier cannot be null");
Objects.requireNonNull(onError, "onError cannot be null");

try {
return ok(supplier.get());
} catch (Exception e) {
return error(onError.apply(e));
}
}

/**
* If this Result has a value, returns a Result of applying the given
* function to the value. Otherwise, returns a Result bearing the same error
* as this one. Note that any exceptions thrown by the mapping function are
* not wrapped but allowed to propagate.
*
* @param <S>
* the type of the mapped value
* @param mapper
* the mapping function
* @return the mapped result
*/
public default <S> Result<S> map(Function<R, S> mapper) {
return flatMap(value -> ok(mapper.apply(value)));
}

/**
* If this Result has a value, applies the given Result-returning function
* to the value. Otherwise, returns a Result bearing the same error as this
* one. Note that any exceptions thrown by the mapping function are not
* wrapped but allowed to propagate.
*
* @param <S>
* the type of the mapped value
* @param mapper
* the mapping function
* @return the mapped result
*/
public <S> Result<S> flatMap(Function<R, Result<S>> mapper);

/**
* Invokes either the first callback or the second one, depending on whether
* this Result denotes a success or a failure, respectively.
*
* @param ifOk
* the function to call if success
* @param ifError
* the function to call if failure
*/
public void handle(Consumer<R> ifOk, Consumer<String> ifError);

/**
* Applies the {@code consumer} if result is not an error.
*
* @param consumer
* consumer to apply in case it's not an error
*/
public default void ifOk(Consumer<R> consumer) {
handle(consumer, error -> {
});
}

/**
* Applies the {@code consumer} if result is an error.
*
* @param consumer
* consumer to apply in case it's an error
*/
public default void ifError(Consumer<String> consumer) {
handle(value -> {
}, consumer);
}

/**
* Returns {@code true} if result is an error.
*
* @return whether the result is an error
*/
public boolean isError();

/**
* Returns an Optional of the result message, or an empty Optional if none.
*
* @return the optional message
*/
public Optional<String> getMessage();
}

+ 96
- 0
server/src/main/java/com/vaadin/data/SimpleResult.java Voir le fichier

@@ -0,0 +1,96 @@
/*
* Copyright 2000-2014 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 java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;

/**
* An internal implementation of {@code Result}.
*
* @param <R>
* the result value type
*/
class SimpleResult<R> implements Result<R> {

private final R value;
private final String message;

/**
* Creates a new {@link Result} instance using {@code value} for a non error
* {@link Result} and {@code message} for an error {@link Result}.
* <p>
* If {@code message} is null then {@code value} is ignored and result is an
* error.
*
* @param value
* the value of the result, may be {@code null}
* @param message
* the error message of the result, may be {@code null}
*/
SimpleResult(R value, String message) {
// value != null => message == null
assert value == null
|| message == null : "Message must be null if value is provided";
this.value = value;
this.message = message;
}

@Override
@SuppressWarnings("unchecked")
public <S> Result<S> flatMap(Function<R, Result<S>> mapper) {
Objects.requireNonNull(mapper, "mapper cannot be null");

if (isError()) {
// Safe cast; valueless
return (Result<S>) this;
} else {
return mapper.apply(value);
}
}

@Override
public void handle(Consumer<R> ifOk, Consumer<String> ifError) {
Objects.requireNonNull(ifOk, "ifOk cannot be null");
Objects.requireNonNull(ifError, "ifError cannot be null");
if (isError()) {
ifError.accept(message);
} else {
ifOk.accept(value);
}
}

@Override
public Optional<String> getMessage() {
return Optional.ofNullable(message);
}

@Override
public boolean isError() {
return message != null;
}

@Override
public String toString() {
if (isError()) {
return "error(" + message + ")";
} else {
return "ok(" + value + ")";
}
}
}

+ 68
- 0
server/src/main/java/com/vaadin/data/ValidationError.java Voir le fichier

@@ -0,0 +1,68 @@
/*
* Copyright 2000-2014 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 java.io.Serializable;
import java.util.Objects;

/**
* Represents a validation error. An error contains a reference to a field whose
* value is invalid and a message describing a validation failure.
*
* @author Vaadin Ltd
* @since 8.0
*
* @param <V>
* the field value type
*/
public class ValidationError<V> implements Serializable {

private HasValue<V> field;
private String message;

/**
* Creates a new instance of ValidationError with provided validated field
* and error message.
*
* @param field
* the validated field
* @param message
* the validation error message, not {@code null}
*/
public ValidationError(HasValue<V> field, String message) {
Objects.requireNonNull(message, "message cannot be null");
this.field = field;
this.message = message;
}

/**
* Returns a reference to the validated field.
*
* @return the validated field
*/
public HasValue<V> getField() {
return field;
}

/**
* Returns a validation error message.
*
* @return the validation error message
*/
public String getMessage() {
return message;
}
}

+ 136
- 0
server/src/main/java/com/vaadin/data/Validator.java Voir le fichier

@@ -0,0 +1,136 @@
/*
* Copyright 2000-2014 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 java.io.Serializable;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;

/**
* A functional interface for validating user input or other potentially invalid
* data. When a validator instance is applied to a value of the corresponding
* type, it returns a <i>result</i> signifying that the value either passed or
* failed the validation.
* <p>
* For instance, the following validator checks if a number is positive:
*
* <pre>
* Validator&lt;Integer&gt; v = num -> {
* if (num >= 0)
* return Result.ok(num);
* else
* return Result.error("number must be positive");
* };
* </pre>
*
* @author Vaadin Ltd.
*
* @param <T>
* the type of the value to validate
*
* @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&lt;Integer&gt; 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);
}

/**
* Validates the given value. Returns a {@code Result} instance representing
* the outcome of the validation.
*
* @param value
* the input value to validate
* @return the validation result
*/
@Override
public Result<T> apply(T value);

/**
* Returns a validator that passes any value.
*
* @param <T>
* the value type
* @return an always-passing validator
*/
public static <T> Validator<T> alwaysPass() {
return v -> Result.ok(v);
}

/**
* Builds a validator out of a conditional function and an error message. 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 given message.
* <p>
* For instance, the following validator checks if a number is between 0 and
* 10, inclusive:
*
* <pre>
* Validator&lt;Integer&gt; v = Validator.from(num -> num >= 0 && num <= 10,
* "number must be between 0 and 10");
* </pre>
*
* @param <T>
* the value type
* @param guard
* the function used to validate, not null
* @param errorMessage
* the message returned if validation fails, not null
* @return the new validator using the function
*/
public static <T> Validator<T> from(Predicate<T> guard,
String errorMessage) {
Objects.requireNonNull(guard, "guard cannot be null");
Objects.requireNonNull(errorMessage, "errorMessage cannot be null");
return value -> {
try {
if (guard.test(value)) {
return Result.ok(value);
} else {
return Result.error(errorMessage);
}
} catch (Exception e) {
return Result.error(errorMessage);
}
};
}
}

+ 68
- 0
server/src/test/java/com/vaadin/data/BinderTest.java Voir le fichier

@@ -4,10 +4,18 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import com.vaadin.data.Binder.Binding;
import com.vaadin.server.AbstractErrorMessage;
import com.vaadin.server.ErrorMessage;
import com.vaadin.server.UserError;
import com.vaadin.tests.data.bean.Person;
import com.vaadin.ui.AbstractField;

@@ -189,6 +197,66 @@ public class BinderTest {
Assert.assertEquals("", nameField.getValue());
}

@Test
public void validate_notBound_noErrors() {
Binder<Person> binder = new Binder<>();

List<ValidationError<?>> errors = binder.validate();

Assert.assertTrue(errors.isEmpty());
}

@Test
public void bound_validatorsAreOK_noErrors() {
Binder<Person> binder = new Binder<>();
Binding<Person, String> binding = binder.forField(nameField);
binding.withValidator(Validator.alwaysPass()).bind(Person::getFirstName,
Person::setFirstName);

nameField.setComponentError(new UserError(""));
List<ValidationError<?>> errors = binder.validate();

Assert.assertTrue(errors.isEmpty());
Assert.assertNull(nameField.getComponentError());
}

@SuppressWarnings("serial")
@Test
public void bound_validatorsFail_errors() {
Binder<Person> binder = new Binder<>();
Binding<Person, String> binding = binder.forField(nameField);
binding.withValidator(Validator.alwaysPass());
String msg1 = "foo";
String msg2 = "bar";
binding.withValidator(new Validator<String>() {
@Override
public Result<String> apply(String value) {
return new SimpleResult<>(null, msg1);
}
});
binding.withValidator(value -> false, msg2);
binding.bind(Person::getFirstName, Person::setFirstName);

List<ValidationError<?>> errors = binder.validate();

Assert.assertEquals(2, errors.size());

Set<String> errorMessages = errors.stream()
.map(ValidationError::getMessage).collect(Collectors.toSet());
Assert.assertTrue(errorMessages.contains(msg1));
Assert.assertTrue(errorMessages.contains(msg2));

Set<?> fields = errors.stream().map(ValidationError::getField)
.collect(Collectors.toSet());
Assert.assertEquals(1, fields.size());
Assert.assertTrue(fields.contains(nameField));

ErrorMessage componentError = nameField.getComponentError();
Assert.assertNotNull(componentError);
Assert.assertEquals("foo",
((AbstractErrorMessage) componentError).getMessage());
}

private void bindName() {
binder.bind(nameField, Person::getFirstName, Person::setFirstName);
binder.bind(p);

+ 105
- 0
server/src/test/java/com/vaadin/data/ResultTest.java Voir le fichier

@@ -0,0 +1,105 @@
/*
* Copyright 2000-2014 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 java.util.function.Function;

import org.junit.Assert;
import org.junit.Test;

/**
* @author Vaadin Ltd
*
*/
public class ResultTest {

@Test
public void testOk() {
String value = "foo";
Result<String> ok = Result.ok(value);
Assert.assertFalse(ok.isError());
Assert.assertFalse(ok.getMessage().isPresent());
ok.ifOk(v -> Assert.assertEquals(value, v));
ok.ifError(msg -> Assert.fail());
}

@Test
public void testError() {
String message = "foo";
Result<String> error = Result.error(message);
Assert.assertTrue(error.isError());
Assert.assertTrue(error.getMessage().isPresent());
error.ifOk(v -> Assert.fail());
error.ifError(msg -> Assert.assertEquals(message, msg));
Assert.assertEquals(message, error.getMessage().get());
}

@Test
public void of_noException() {
Result<String> result = Result.of(() -> "", exception -> null);
Assert.assertTrue(result instanceof SimpleResult);
Assert.assertFalse(((SimpleResult<?>) result).isError());
}

@Test
public void of_exception() {
String message = "foo";
Result<String> result = Result.<String> of(() -> {
throw new RuntimeException();
}, exception -> message);
Assert.assertTrue(result instanceof SimpleResult);
Assert.assertTrue(((SimpleResult<?>) result).isError());
Assert.assertEquals(message, result.getMessage().get());
}

@SuppressWarnings("serial")
@Test
public void map_norError_mapperIsApplied() {
Result<String> result = new SimpleResult<String>("foo", null) {

@Override
public <S> Result<S> flatMap(Function<String, Result<S>> mapper) {
return mapper.apply("foo");
}
};
Result<String> mapResult = result.map(value -> {
Assert.assertEquals("foo", value);
return "bar";
});
Assert.assertTrue(mapResult instanceof SimpleResult);
Assert.assertFalse(((SimpleResult<?>) mapResult).isError());
mapResult.ifOk(v -> Assert.assertEquals("bar", v));
}

@SuppressWarnings("serial")
@Test
public void map_error_mapperIsApplied() {
Result<String> result = new SimpleResult<String>("foo", null) {

@Override
public <S> Result<S> flatMap(Function<String, Result<S>> mapper) {
return new SimpleResult<S>(null, "bar");
}
};
Result<String> mapResult = result.map(value -> {
Assert.assertEquals("foo", value);
return "somevalue";
});
Assert.assertTrue(mapResult instanceof SimpleResult);
Assert.assertTrue(((SimpleResult<?>) mapResult).isError());
mapResult.ifError(msg -> Assert.assertEquals("bar", msg));
}
}

+ 93
- 0
server/src/test/java/com/vaadin/data/ValidatorTest.java Voir le fichier

@@ -0,0 +1,93 @@
/*
* Copyright 2000-2014 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 java.util.Objects;

import org.junit.Assert;
import org.junit.Test;

/**
* @author Vaadin Ltd
*
*/
public class ValidatorTest {

@Test
public void alwaysPass() {
Validator<String> alwaysPass = Validator.alwaysPass();
Result<String> result = alwaysPass.apply("foo");
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);
Assert.assertTrue(result.isError());

result = validator.apply("");
Assert.assertFalse(result.isError());
}
}

Chargement…
Annuler
Enregistrer