You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

BeanValidator.java 6.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /*
  2. * Copyright 2000-2014 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.data.validator;
  17. import java.util.Locale;
  18. import java.util.Objects;
  19. import java.util.Set;
  20. import java.util.function.BinaryOperator;
  21. import javax.validation.ConstraintViolation;
  22. import javax.validation.MessageInterpolator.Context;
  23. import javax.validation.Validation;
  24. import javax.validation.ValidatorFactory;
  25. import javax.validation.metadata.ConstraintDescriptor;
  26. import com.vaadin.data.Result;
  27. import com.vaadin.data.Validator;
  28. /**
  29. * A {@code Validator} using the JSR-303 (javax.validation) annotation-based
  30. * bean validation mechanism. Values passed to this validator are compared
  31. * against the constraints, if any, specified by annotations on the
  32. * corresponding bean property.
  33. * <p>
  34. * Note that a JSR-303 implementation (e.g. Hibernate Validator or Apache Bean
  35. * Validation - formerly agimatec validation) must be present on the project
  36. * classpath when using bean validation.
  37. *
  38. * @author Petri Hakala
  39. * @author Vaadin Ltd.
  40. *
  41. * @since 8.0
  42. */
  43. public class BeanValidator implements Validator<Object> {
  44. private static final long serialVersionUID = 1L;
  45. private static ValidatorFactory factory;
  46. private String propertyName;
  47. private Class<?> beanType;
  48. private Locale locale;
  49. /**
  50. * Creates a new JSR-303 {@code BeanValidator} that validates values of the
  51. * specified property. Localizes validation messages using the
  52. * {@linkplain Locale#getDefault() default locale}.
  53. *
  54. * @param beanType
  55. * the bean type declaring the property, not null
  56. * @param propertyName
  57. * the property to validate, not null
  58. */
  59. public BeanValidator(Class<?> beanType, String propertyName) {
  60. this(beanType, propertyName, Locale.getDefault());
  61. }
  62. /**
  63. * Creates a new JSR-303 {@code BeanValidator} that validates values of the
  64. * specified property. Localizes validation messages using the given locale.
  65. *
  66. * @param beanType
  67. * the bean class declaring the property, not null
  68. * @param propertyName
  69. * the property to validate, not null
  70. * @param locale
  71. * the locale to use, not null
  72. */
  73. public BeanValidator(Class<?> beanType, String propertyName,
  74. Locale locale) {
  75. Objects.requireNonNull(beanType, "bean class cannot be null");
  76. Objects.requireNonNull(propertyName, "property name cannot be null");
  77. this.beanType = beanType;
  78. this.propertyName = propertyName;
  79. setLocale(locale);
  80. }
  81. /**
  82. * Validates the given value as if it were the value of the bean property
  83. * configured for this validator. Returns {@code Result.ok} if there are no
  84. * JSR-303 constraint violations, a {@code Result.error} of chained
  85. * constraint violation messages otherwise.
  86. * <p>
  87. * Null values are accepted unless the property has an {@code @NotNull}
  88. * annotation or equivalent.
  89. */
  90. @Override
  91. public Result<Object> apply(final Object value) {
  92. Set<? extends ConstraintViolation<?>> violations = getJavaxBeanValidator()
  93. .validateValue(beanType, propertyName, value);
  94. BinaryOperator<Result<Object>> accumulator = (result1,
  95. result2) -> result1.flatMap(val -> result2);
  96. return violations.stream().map(v -> Result.error(getMessage(v)))
  97. .reduce(Result.ok(value), accumulator);
  98. }
  99. /**
  100. * Returns the locale used for validation error messages.
  101. *
  102. * @return the locale used for error messages
  103. */
  104. public Locale getLocale() {
  105. return locale;
  106. }
  107. @Override
  108. public String toString() {
  109. return String.format("%s[%s.%s]", getClass().getSimpleName(),
  110. beanType.getSimpleName(), propertyName);
  111. }
  112. /**
  113. * Returns the underlying JSR-303 bean validator factory used. A factory is
  114. * created using {@link Validation} if necessary.
  115. *
  116. * @return the validator factory to use
  117. */
  118. protected static ValidatorFactory getJavaxBeanValidatorFactory() {
  119. if (factory == null) {
  120. factory = Validation.buildDefaultValidatorFactory();
  121. }
  122. return factory;
  123. }
  124. /**
  125. * Returns a shared JSR-303 validator instance to use.
  126. *
  127. * @return the validator to use
  128. */
  129. protected javax.validation.Validator getJavaxBeanValidator() {
  130. return getJavaxBeanValidatorFactory().getValidator();
  131. }
  132. /**
  133. * Returns the interpolated error message for the given constraint violation
  134. * using the locale specified for this validator.
  135. *
  136. * @param v
  137. * the constraint violation
  138. * @return the localized error message
  139. */
  140. protected String getMessage(ConstraintViolation<?> v) {
  141. return getJavaxBeanValidatorFactory().getMessageInterpolator()
  142. .interpolate(v.getMessageTemplate(), createContext(v), locale);
  143. }
  144. /**
  145. * Creates a simple message interpolation context based on the given
  146. * constraint violation.
  147. *
  148. * @param v
  149. * the constraint violation
  150. * @return the message interpolation context
  151. */
  152. protected Context createContext(ConstraintViolation<?> v) {
  153. return new Context() {
  154. @Override
  155. public ConstraintDescriptor<?> getConstraintDescriptor() {
  156. return v.getConstraintDescriptor();
  157. }
  158. @Override
  159. public Object getValidatedValue() {
  160. return v.getInvalidValue();
  161. }
  162. };
  163. }
  164. /**
  165. * Sets the locale used for validation error messages. Revalidation is not
  166. * automatically triggered by setting the locale.
  167. *
  168. * @param locale
  169. * the locale to use for error messages, not null
  170. */
  171. private void setLocale(Locale locale) {
  172. Objects.requireNonNull(locale, "locale cannot be null");
  173. this.locale = locale;
  174. }
  175. }