|
18 | 18 |
|
19 | 19 | import java.util.function.BiConsumer;
|
20 | 20 |
|
21 |
| -import org.springframework.util.Assert; |
22 |
| - |
23 | 21 | /**
|
24 | 22 | * A validator for application-specific objects.
|
25 | 23 | *
|
|
30 | 28 | * of an application, and supports the encapsulation of validation
|
31 | 29 | * logic as a first-class citizen in its own right.
|
32 | 30 | *
|
33 |
| - * <p>Find below a simple but complete {@code Validator} |
34 |
| - * implementation, which validates that the various {@link String} |
35 |
| - * properties of a {@code UserLogin} instance are not empty |
36 |
| - * (that is they are not {@code null} and do not consist |
| 31 | + * <p>Implementations can be created via the static factory methods |
| 32 | + * {@link #forInstanceOf(Class, BiConsumer)} or |
| 33 | + * {@link #forType(Class, BiConsumer)}. |
| 34 | + * Below is a simple but complete {@code Validator} that validates that the |
| 35 | + * various {@link String} properties of a {@code UserLogin} instance are not |
| 36 | + * empty (they are not {@code null} and do not consist |
37 | 37 | * wholly of whitespace), and that any password that is present is
|
38 | 38 | * at least {@code 'MINIMUM_PASSWORD_LENGTH'} characters in length.
|
39 | 39 | *
|
40 |
| - * <pre class="code">public class UserLoginValidator implements Validator { |
41 |
| - * |
42 |
| - * private static final int MINIMUM_PASSWORD_LENGTH = 6; |
43 |
| - * |
44 |
| - * public boolean supports(Class clazz) { |
45 |
| - * return UserLogin.class.isAssignableFrom(clazz); |
46 |
| - * } |
47 |
| - * |
48 |
| - * public void validate(Object target, Errors errors) { |
49 |
| - * ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userName", "field.required"); |
50 |
| - * ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "field.required"); |
51 |
| - * UserLogin login = (UserLogin) target; |
52 |
| - * if (login.getPassword() != null |
53 |
| - * && login.getPassword().trim().length() < MINIMUM_PASSWORD_LENGTH) { |
54 |
| - * errors.rejectValue("password", "field.min.length", |
55 |
| - * new Object[]{Integer.valueOf(MINIMUM_PASSWORD_LENGTH)}, |
56 |
| - * "The password must be at least [" + MINIMUM_PASSWORD_LENGTH + "] characters in length."); |
57 |
| - * } |
58 |
| - * } |
59 |
| - * }</pre> |
| 40 | + * <pre class="code">Validator userLoginValidator = Validator.forInstance(UserLogin.class, (login, errors) -> { |
| 41 | + * ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userName", "field.required"); |
| 42 | + * ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "field.required"); |
| 43 | + * if (login.getPassword() != null |
| 44 | + * && login.getPassword().trim().length() < MINIMUM_PASSWORD_LENGTH) { |
| 45 | + * errors.rejectValue("password", "field.min.length", |
| 46 | + * new Object[]{Integer.valueOf(MINIMUM_PASSWORD_LENGTH)}, |
| 47 | + * "The password must be at least [" + MINIMUM_PASSWORD_LENGTH + "] characters in length."); |
| 48 | + * } |
| 49 | + * });</pre> |
60 | 50 | *
|
61 | 51 | * <p>See also the Spring reference manual for a fuller discussion of
|
62 | 52 | * the {@code Validator} interface and its role in an enterprise
|
63 | 53 | * application.
|
64 | 54 | *
|
65 | 55 | * @author Rod Johnson
|
66 | 56 | * @author Toshiaki Maki
|
| 57 | + * @author Arjen Poutsma |
67 | 58 | * @see SmartValidator
|
68 | 59 | * @see Errors
|
69 | 60 | * @see ValidationUtils
|
@@ -97,38 +88,54 @@ public interface Validator {
|
97 | 88 | */
|
98 | 89 | void validate(Object target, Errors errors);
|
99 | 90 |
|
| 91 | + |
100 | 92 | /**
|
101 |
| - * Takes the {@link BiConsumer} containing the validation logic for the specific type |
102 |
| - * <code><T></code> and returns the {@link Validator} instance.<br> |
103 |
| - * This validator implements the <i>typical</i> {@link #supports(Class)} method |
104 |
| - * for the given <code><T></code>.<br> |
105 |
| - * |
106 |
| - * By using this method, a {@link Validator} can be implemented as follows: |
| 93 | + * Return a {@code Validator} that checks whether the target object |
| 94 | + * {@linkplain Class#isAssignableFrom(Class) is an instance of} |
| 95 | + * {@code targetClass}, resorting to {@code delegate} to populate |
| 96 | + * {@link Errors} if it is. |
107 | 97 | *
|
108 |
| - * <pre class="code">Validator passwordEqualsValidator = Validator.of(PasswordResetForm.class, (form, errors) -> { |
109 |
| - * if (!Objects.equals(form.getPassword(), form.getConfirmPassword())) { |
110 |
| - * errors.rejectValue("confirmPassword", |
111 |
| - * "PasswordEqualsValidator.passwordResetForm.password", |
112 |
| - * "password and confirm password must be same."); |
113 |
| - * } |
114 |
| - * });</pre> |
115 |
| - * @param targetClass the class of the object that is to be validated |
116 |
| - * @param delegate the validation logic to delegate for the specific type <code><T></code> |
117 |
| - * @param <T> the type of the object that is to be validated |
118 |
| - * @return the {@link Validator} instance |
| 98 | + * <p>For instance: |
| 99 | + * <pre class="code">Validator passwordEqualsValidator = Validator.forInstanceOf(PasswordResetForm.class, (form, errors) -> { |
| 100 | + * if (!Objects.equals(form.getPassword(), form.getConfirmPassword())) { |
| 101 | + * errors.rejectValue("confirmPassword", |
| 102 | + * "PasswordEqualsValidator.passwordResetForm.password", |
| 103 | + * "password and confirm password must be same."); |
| 104 | + * } |
| 105 | + * });</pre> |
| 106 | + * @param targetClass the class supported by the returned validator |
| 107 | + * @param delegate function invoked with the target object, if it is an |
| 108 | + * instance of type T |
| 109 | + * @param <T> the target object type |
| 110 | + * @return the created {@code Validator} |
| 111 | + * @since 6.1 |
119 | 112 | */
|
120 |
| - static <T> Validator of(Class<T> targetClass, BiConsumer<T, Errors> delegate) { |
121 |
| - Assert.notNull(targetClass, "'targetClass' must not be null."); |
122 |
| - return new Validator() { |
123 |
| - @Override |
124 |
| - public boolean supports(Class<?> clazz) { |
125 |
| - return targetClass.isAssignableFrom(clazz); |
126 |
| - } |
| 113 | + static <T> Validator forInstanceOf(Class<T> targetClass, BiConsumer<T, Errors> delegate) { |
| 114 | + return new TypedValidator<>(targetClass, targetClass::isAssignableFrom, delegate); |
| 115 | + } |
127 | 116 |
|
128 |
| - @Override |
129 |
| - public void validate(Object target, Errors errors) { |
130 |
| - delegate.accept(targetClass.cast(target), errors); |
131 |
| - } |
132 |
| - }; |
| 117 | + /** |
| 118 | + * Return a {@code Validator} that checks whether the target object's class |
| 119 | + * is identical to {@code targetClass}, resorting to {@code delegate} to |
| 120 | + * populate {@link Errors} if it is. |
| 121 | + * |
| 122 | + * <p>For instance: |
| 123 | + * <pre class="code">Validator passwordEqualsValidator = Validator.forType(PasswordResetForm.class, (form, errors) -> { |
| 124 | + * if (!Objects.equals(form.getPassword(), form.getConfirmPassword())) { |
| 125 | + * errors.rejectValue("confirmPassword", |
| 126 | + * "PasswordEqualsValidator.passwordResetForm.password", |
| 127 | + * "password and confirm password must be same."); |
| 128 | + * } |
| 129 | + * });</pre> |
| 130 | + * @param targetClass the exact class supported by the returned validator (no subclasses) |
| 131 | + * @param delegate function invoked with the target object, if it is an |
| 132 | + * instance of type T |
| 133 | + * @param <T> the target object type |
| 134 | + * @return the created {@code Validator} |
| 135 | + * @since 6.1 |
| 136 | + */ |
| 137 | + static <T> Validator forType(Class<T> targetClass, BiConsumer<T, Errors> delegate) { |
| 138 | + return new TypedValidator<>(targetClass, targetClass::equals, delegate); |
133 | 139 | }
|
| 140 | + |
134 | 141 | }
|
0 commit comments