Merge branch '5.1.x'
This commit is contained in:
@@ -165,27 +165,15 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation.
|
||||
String nestedField = bindingResult.getNestedPath() + field;
|
||||
if (nestedField.isEmpty()) {
|
||||
String[] errorCodes = bindingResult.resolveMessageCodes(errorCode);
|
||||
ObjectError error = new ObjectError(
|
||||
errors.getObjectName(), errorCodes, errorArgs, violation.getMessage()) {
|
||||
@Override
|
||||
public boolean shouldRenderDefaultMessage() {
|
||||
return requiresMessageFormat(violation);
|
||||
}
|
||||
};
|
||||
error.wrap(violation);
|
||||
ObjectError error = new ViolationObjectError(
|
||||
errors.getObjectName(), errorCodes, errorArgs, violation, this);
|
||||
bindingResult.addError(error);
|
||||
}
|
||||
else {
|
||||
Object rejectedValue = getRejectedValue(field, violation, bindingResult);
|
||||
String[] errorCodes = bindingResult.resolveMessageCodes(errorCode, field);
|
||||
FieldError error = new FieldError(errors.getObjectName(), nestedField,
|
||||
rejectedValue, false, errorCodes, errorArgs, violation.getMessage()) {
|
||||
@Override
|
||||
public boolean shouldRenderDefaultMessage() {
|
||||
return requiresMessageFormat(violation);
|
||||
}
|
||||
};
|
||||
error.wrap(violation);
|
||||
FieldError error = new ViolationFieldError(errors.getObjectName(), nestedField,
|
||||
rejectedValue, errorCodes, errorArgs, violation, this);
|
||||
bindingResult.addError(error);
|
||||
}
|
||||
}
|
||||
@@ -307,29 +295,6 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation.
|
||||
return new DefaultMessageSourceResolvable(codes, field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether this violation's interpolated message has remaining
|
||||
* placeholders and therefore requires {@link java.text.MessageFormat}
|
||||
* to be applied to it. Called for a Bean Validation defined message
|
||||
* (coming out {@code ValidationMessages.properties}) when rendered
|
||||
* as the default message in Spring's MessageSource.
|
||||
* <p>The default implementation considers a Spring-style "{0}" placeholder
|
||||
* for the field name as an indication for {@link java.text.MessageFormat}.
|
||||
* Any other placeholder or escape syntax occurrences are typically a
|
||||
* mismatch, coming out of regex pattern values or the like. Note that
|
||||
* standard Bean Validation does not support "{0}" style placeholders at all;
|
||||
* this is a feature typically used in Spring MessageSource resource bundles.
|
||||
* @param violation the Bean Validation constraint violation, including
|
||||
* BV-defined interpolation of named attribute references in its message
|
||||
* @return {@code true} if {@code java.text.MessageFormat} is to be applied,
|
||||
* or {@code false} if the violation's message should be used as-is
|
||||
* @since 5.1.8
|
||||
* @see #getArgumentsForConstraint
|
||||
*/
|
||||
protected boolean requiresMessageFormat(ConstraintViolation<?> violation) {
|
||||
return violation.getMessage().contains("{0}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the rejected value behind the given constraint violation,
|
||||
* for exposure through the Spring errors representation.
|
||||
@@ -354,6 +319,33 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation.
|
||||
return invalidValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether this violation's interpolated message has remaining
|
||||
* placeholders and therefore requires {@link java.text.MessageFormat}
|
||||
* to be applied to it. Called for a Bean Validation defined message
|
||||
* (coming out {@code ValidationMessages.properties}) when rendered
|
||||
* as the default message in Spring's MessageSource.
|
||||
* <p>The default implementation considers a Spring-style "{0}" placeholder
|
||||
* for the field name as an indication for {@link java.text.MessageFormat}.
|
||||
* Any other placeholder or escape syntax occurrences are typically a
|
||||
* mismatch, coming out of regex pattern values or the like. Note that
|
||||
* standard Bean Validation does not support "{0}" style placeholders at all;
|
||||
* this is a feature typically used in Spring MessageSource resource bundles.
|
||||
* @param violation the Bean Validation constraint violation, including
|
||||
* BV-defined interpolation of named attribute references in its message
|
||||
* @return {@code true} if {@code java.text.MessageFormat} is to be applied,
|
||||
* or {@code false} if the violation's message should be used as-is
|
||||
* @since 5.1.8
|
||||
* @see #getArgumentsForConstraint
|
||||
*/
|
||||
protected boolean requiresMessageFormat(ConstraintViolation<?> violation) {
|
||||
return containsSpringStylePlaceholder(violation.getMessage());
|
||||
}
|
||||
|
||||
private static boolean containsSpringStylePlaceholder(@Nullable String message) {
|
||||
return (message != null && message.contains("{0}"));
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of JSR-303 Validator interface
|
||||
@@ -436,6 +428,71 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation.
|
||||
public String getDefaultMessage() {
|
||||
return this.resolvableString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.resolvableString;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Subclass of {@code ObjectError} with Spring-style default message rendering.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private static class ViolationObjectError extends ObjectError implements Serializable {
|
||||
|
||||
@Nullable
|
||||
private transient SpringValidatorAdapter adapter;
|
||||
|
||||
@Nullable
|
||||
private transient ConstraintViolation<?> violation;
|
||||
|
||||
public ViolationObjectError(String objectName, String[] codes, Object[] arguments,
|
||||
ConstraintViolation<?> violation, SpringValidatorAdapter adapter) {
|
||||
|
||||
super(objectName, codes, arguments, violation.getMessage());
|
||||
this.adapter = adapter;
|
||||
this.violation = violation;
|
||||
wrap(violation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRenderDefaultMessage() {
|
||||
return (this.adapter != null && this.violation != null ?
|
||||
this.adapter.requiresMessageFormat(this.violation) :
|
||||
containsSpringStylePlaceholder(getDefaultMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Subclass of {@code FieldError} with Spring-style default message rendering.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private static class ViolationFieldError extends FieldError implements Serializable {
|
||||
|
||||
@Nullable
|
||||
private transient SpringValidatorAdapter adapter;
|
||||
|
||||
@Nullable
|
||||
private transient ConstraintViolation<?> violation;
|
||||
|
||||
public ViolationFieldError(String objectName, String field, @Nullable Object rejectedValue, String[] codes,
|
||||
Object[] arguments, ConstraintViolation<?> violation, SpringValidatorAdapter adapter) {
|
||||
|
||||
super(objectName, field, rejectedValue, false, codes, arguments, violation.getMessage());
|
||||
this.adapter = adapter;
|
||||
this.violation = violation;
|
||||
wrap(violation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRenderDefaultMessage() {
|
||||
return (this.adapter != null && this.violation != null ?
|
||||
this.adapter.requiresMessageFormat(this.violation) :
|
||||
containsSpringStylePlaceholder(getDefaultMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
package org.springframework.validation.beanvalidation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
@@ -48,12 +50,10 @@ import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.BeanWrapperImpl;
|
||||
import org.springframework.context.support.StaticMessageSource;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.SerializationTestUtils;
|
||||
import org.springframework.validation.BeanPropertyBindingResult;
|
||||
import org.springframework.validation.FieldError;
|
||||
|
||||
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||
import static java.lang.annotation.ElementType.TYPE;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
@@ -85,7 +85,7 @@ public class SpringValidatorAdapterTests {
|
||||
}
|
||||
|
||||
@Test // SPR-13406
|
||||
public void testNoStringArgumentValue() {
|
||||
public void testNoStringArgumentValue() throws Exception {
|
||||
TestBean testBean = new TestBean();
|
||||
testBean.setPassword("pass");
|
||||
testBean.setConfirmPassword("pass");
|
||||
@@ -100,10 +100,11 @@ public class SpringValidatorAdapterTests {
|
||||
assertThat(messageSource.getMessage(error, Locale.ENGLISH)).isEqualTo("Size of Password must be between 8 and 128");
|
||||
assertThat(error.contains(ConstraintViolation.class)).isTrue();
|
||||
assertThat(error.unwrap(ConstraintViolation.class).getPropertyPath().toString()).isEqualTo("password");
|
||||
assertThat(SerializationTestUtils.serializeAndDeserialize(error.toString())).isEqualTo(error.toString());
|
||||
}
|
||||
|
||||
@Test // SPR-13406
|
||||
public void testApplyMessageSourceResolvableToStringArgumentValueWithResolvedLogicalFieldName() {
|
||||
public void testApplyMessageSourceResolvableToStringArgumentValueWithResolvedLogicalFieldName() throws Exception {
|
||||
TestBean testBean = new TestBean();
|
||||
testBean.setPassword("password");
|
||||
testBean.setConfirmPassword("PASSWORD");
|
||||
@@ -118,6 +119,7 @@ public class SpringValidatorAdapterTests {
|
||||
assertThat(messageSource.getMessage(error, Locale.ENGLISH)).isEqualTo("Password must be same value as Password(Confirm)");
|
||||
assertThat(error.contains(ConstraintViolation.class)).isTrue();
|
||||
assertThat(error.unwrap(ConstraintViolation.class).getPropertyPath().toString()).isEqualTo("password");
|
||||
assertThat(SerializationTestUtils.serializeAndDeserialize(error.toString())).isEqualTo(error.toString());
|
||||
}
|
||||
|
||||
@Test // SPR-13406
|
||||
@@ -278,8 +280,8 @@ public class SpringValidatorAdapterTests {
|
||||
|
||||
@Documented
|
||||
@Constraint(validatedBy = {SameValidator.class})
|
||||
@Target({TYPE, ANNOTATION_TYPE})
|
||||
@Retention(RUNTIME)
|
||||
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Repeatable(SameGroup.class)
|
||||
@interface Same {
|
||||
|
||||
@@ -293,8 +295,8 @@ public class SpringValidatorAdapterTests {
|
||||
|
||||
String comparingField();
|
||||
|
||||
@Target({TYPE, ANNOTATION_TYPE})
|
||||
@Retention(RUNTIME)
|
||||
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@interface List {
|
||||
Same[] value();
|
||||
@@ -304,8 +306,8 @@ public class SpringValidatorAdapterTests {
|
||||
|
||||
@Documented
|
||||
@Inherited
|
||||
@Retention(RUNTIME)
|
||||
@Target({TYPE, ANNOTATION_TYPE})
|
||||
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface SameGroup {
|
||||
|
||||
Same[] value();
|
||||
@@ -441,7 +443,7 @@ public class SpringValidatorAdapterTests {
|
||||
|
||||
|
||||
@Constraint(validatedBy = AnythingValidator.class)
|
||||
@Retention(RUNTIME)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface AnythingValid {
|
||||
|
||||
String message() default "{AnythingValid.message}";
|
||||
@@ -462,14 +464,14 @@ public class SpringValidatorAdapterTests {
|
||||
|
||||
@Override
|
||||
public boolean isValid(Object value, ConstraintValidatorContext context) {
|
||||
List<Field> fieldsErros = new ArrayList<>();
|
||||
Arrays.asList(value.getClass().getDeclaredFields()).forEach(f -> {
|
||||
f.setAccessible(true);
|
||||
List<Field> fieldsErrors = new ArrayList<>();
|
||||
Arrays.asList(value.getClass().getDeclaredFields()).forEach(field -> {
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
if (!f.getName().equals(ID) && f.get(value) == null) {
|
||||
fieldsErros.add(f);
|
||||
if (!field.getName().equals(ID) && field.get(value) == null) {
|
||||
fieldsErrors.add(field);
|
||||
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())
|
||||
.addPropertyNode(f.getName())
|
||||
.addPropertyNode(field.getName())
|
||||
.addConstraintViolation();
|
||||
}
|
||||
}
|
||||
@@ -477,7 +479,7 @@ public class SpringValidatorAdapterTests {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
});
|
||||
return fieldsErros.isEmpty();
|
||||
return fieldsErrors.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user