AbstractMessageSource does not attempt to format code-as-default-message
Issue: SPR-15123
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2016 the original author or authors.
|
* Copyright 2002-2017 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -160,30 +160,21 @@ public abstract class AbstractMessageSource extends MessageSourceSupport impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final String getMessage(MessageSourceResolvable resolvable, Locale locale)
|
public final String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException {
|
||||||
throws NoSuchMessageException {
|
|
||||||
|
|
||||||
String[] codes = resolvable.getCodes();
|
String[] codes = resolvable.getCodes();
|
||||||
if (codes == null) {
|
if (codes != null) {
|
||||||
codes = new String[0];
|
for (String code : codes) {
|
||||||
}
|
String message = getMessageInternal(code, resolvable.getArguments(), locale);
|
||||||
for (String code : codes) {
|
if (message != null) {
|
||||||
String msg = getMessageInternal(code, resolvable.getArguments(), locale);
|
return message;
|
||||||
if (msg != null) {
|
}
|
||||||
return msg;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String defaultMessage = resolvable.getDefaultMessage();
|
String defaultMessage = getDefaultMessage(resolvable, locale);
|
||||||
if (defaultMessage != null) {
|
if (defaultMessage != null) {
|
||||||
return renderDefaultMessage(defaultMessage, resolvable.getArguments(), locale);
|
return defaultMessage;
|
||||||
}
|
}
|
||||||
if (codes.length > 0) {
|
throw new NoSuchMessageException(!ObjectUtils.isEmpty(codes) ? codes[codes.length - 1] : null, locale);
|
||||||
String fallback = getDefaultMessage(codes[0]);
|
|
||||||
if (fallback != null) {
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new NoSuchMessageException(codes.length > 0 ? codes[codes.length - 1] : null, locale);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -194,7 +185,7 @@ public abstract class AbstractMessageSource extends MessageSourceSupport impleme
|
|||||||
* @param code the code to lookup up, such as 'calculator.noRateSet'
|
* @param code the code to lookup up, such as 'calculator.noRateSet'
|
||||||
* @param args array of arguments that will be filled in for params
|
* @param args array of arguments that will be filled in for params
|
||||||
* within the message
|
* within the message
|
||||||
* @param locale the Locale in which to do the lookup
|
* @param locale the locale in which to do the lookup
|
||||||
* @return the resolved message, or {@code null} if not found
|
* @return the resolved message, or {@code null} if not found
|
||||||
* @see #getMessage(String, Object[], String, Locale)
|
* @see #getMessage(String, Object[], String, Locale)
|
||||||
* @see #getMessage(String, Object[], Locale)
|
* @see #getMessage(String, Object[], Locale)
|
||||||
@@ -249,11 +240,11 @@ public abstract class AbstractMessageSource extends MessageSourceSupport impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to retrieve the given message from the parent MessageSource, if any.
|
* Try to retrieve the given message from the parent {@code MessageSource}, if any.
|
||||||
* @param code the code to lookup up, such as 'calculator.noRateSet'
|
* @param code the code to lookup up, such as 'calculator.noRateSet'
|
||||||
* @param args array of arguments that will be filled in for params
|
* @param args array of arguments that will be filled in for params
|
||||||
* within the message
|
* within the message
|
||||||
* @param locale the Locale in which to do the lookup
|
* @param locale the locale in which to do the lookup
|
||||||
* @return the resolved message, or {@code null} if not found
|
* @return the resolved message, or {@code null} if not found
|
||||||
* @see #getParentMessageSource()
|
* @see #getParentMessageSource()
|
||||||
*/
|
*/
|
||||||
@@ -274,11 +265,36 @@ public abstract class AbstractMessageSource extends MessageSourceSupport impleme
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a default message for the given {@code MessageSourceResolvable}.
|
||||||
|
* <p>This implementation fully renders the default message if available,
|
||||||
|
* or just returns the plain default message {@code String} if the primary
|
||||||
|
* message code is being used as a default message.
|
||||||
|
* @param resolvable the value object to resolve a default message for
|
||||||
|
* @param locale the current locale
|
||||||
|
* @return the default message, or {@code null} if none
|
||||||
|
* @since 4.3.6
|
||||||
|
* @see #renderDefaultMessage(String, Object[], Locale)
|
||||||
|
* @see #getDefaultMessage(String)
|
||||||
|
*/
|
||||||
|
protected String getDefaultMessage(MessageSourceResolvable resolvable, Locale locale) {
|
||||||
|
String defaultMessage = resolvable.getDefaultMessage();
|
||||||
|
String[] codes = resolvable.getCodes();
|
||||||
|
if (defaultMessage != null) {
|
||||||
|
if (!ObjectUtils.isEmpty(codes) && defaultMessage.equals(codes[0])) {
|
||||||
|
// Never format a code-as-default-message, even with alwaysUseMessageFormat=true
|
||||||
|
return defaultMessage;
|
||||||
|
}
|
||||||
|
return renderDefaultMessage(defaultMessage, resolvable.getArguments(), locale);
|
||||||
|
}
|
||||||
|
return (!ObjectUtils.isEmpty(codes) ? getDefaultMessage(codes[0]) : null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a fallback default message for the given code, if any.
|
* Return a fallback default message for the given code, if any.
|
||||||
* <p>Default is to return the code itself if "useCodeAsDefaultMessage" is activated,
|
* <p>Default is to return the code itself if "useCodeAsDefaultMessage" is activated,
|
||||||
* or return no fallback else. In case of no fallback, the caller will usually
|
* or return no fallback else. In case of no fallback, the caller will usually
|
||||||
* receive a NoSuchMessageException from {@code getMessage}.
|
* receive a {@code NoSuchMessageException} from {@code getMessage}.
|
||||||
* @param code the message code that we couldn't resolve
|
* @param code the message code that we couldn't resolve
|
||||||
* and that we didn't receive an explicit default message for
|
* and that we didn't receive an explicit default message for
|
||||||
* @return the default message to use, or {@code null} if none
|
* @return the default message to use, or {@code null} if none
|
||||||
@@ -328,7 +344,7 @@ public abstract class AbstractMessageSource extends MessageSourceSupport impleme
|
|||||||
* pattern doesn't contain argument placeholders in the first place. Therefore,
|
* pattern doesn't contain argument placeholders in the first place. Therefore,
|
||||||
* it is advisable to circumvent MessageFormat for messages without arguments.
|
* it is advisable to circumvent MessageFormat for messages without arguments.
|
||||||
* @param code the code of the message to resolve
|
* @param code the code of the message to resolve
|
||||||
* @param locale the Locale to resolve the code for
|
* @param locale the locale to resolve the code for
|
||||||
* (subclasses are encouraged to support internationalization)
|
* (subclasses are encouraged to support internationalization)
|
||||||
* @return the message String, or {@code null} if not found
|
* @return the message String, or {@code null} if not found
|
||||||
* @see #resolveCode
|
* @see #resolveCode
|
||||||
@@ -352,7 +368,7 @@ public abstract class AbstractMessageSource extends MessageSourceSupport impleme
|
|||||||
* for messages without arguments, not involving MessageFormat.</b>
|
* for messages without arguments, not involving MessageFormat.</b>
|
||||||
* See the {@link #resolveCodeWithoutArguments} javadoc for details.
|
* See the {@link #resolveCodeWithoutArguments} javadoc for details.
|
||||||
* @param code the code of the message to resolve
|
* @param code the code of the message to resolve
|
||||||
* @param locale the Locale to resolve the code for
|
* @param locale the locale to resolve the code for
|
||||||
* (subclasses are encouraged to support internationalization)
|
* (subclasses are encouraged to support internationalization)
|
||||||
* @return the MessageFormat for the message, or {@code null} if not found
|
* @return the MessageFormat for the message, or {@code null} if not found
|
||||||
* @see #resolveCodeWithoutArguments(String, java.util.Locale)
|
* @see #resolveCodeWithoutArguments(String, java.util.Locale)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2016 the original author or authors.
|
* Copyright 2002-2017 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -27,6 +27,7 @@ import javax.validation.ConstraintValidator;
|
|||||||
import javax.validation.ConstraintValidatorContext;
|
import javax.validation.ConstraintValidatorContext;
|
||||||
import javax.validation.Payload;
|
import javax.validation.Payload;
|
||||||
import javax.validation.Validation;
|
import javax.validation.Validation;
|
||||||
|
import javax.validation.constraints.Pattern;
|
||||||
import javax.validation.constraints.Size;
|
import javax.validation.constraints.Size;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -45,6 +46,7 @@ import static org.junit.Assert.*;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Kazuki Shimizu
|
* @author Kazuki Shimizu
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 4.3
|
* @since 4.3
|
||||||
*/
|
*/
|
||||||
public class SpringValidatorAdapterTests {
|
public class SpringValidatorAdapterTests {
|
||||||
@@ -64,6 +66,20 @@ public class SpringValidatorAdapterTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test // SPR-13406
|
||||||
|
public void testNoStringArgumentValue() {
|
||||||
|
TestBean testBean = new TestBean();
|
||||||
|
testBean.setPassword("pass");
|
||||||
|
testBean.setConfirmPassword("pass");
|
||||||
|
|
||||||
|
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(testBean, "testBean");
|
||||||
|
validatorAdapter.validate(testBean, errors);
|
||||||
|
|
||||||
|
assertThat(errors.getFieldErrorCount("password"), is(1));
|
||||||
|
assertThat(messageSource.getMessage(errors.getFieldError("password"), Locale.ENGLISH),
|
||||||
|
is("Size of Password is must be between 8 and 128"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test // SPR-13406
|
@Test // SPR-13406
|
||||||
public void testApplyMessageSourceResolvableToStringArgumentValueWithResolvedLogicalFieldName() {
|
public void testApplyMessageSourceResolvableToStringArgumentValueWithResolvedLogicalFieldName() {
|
||||||
TestBean testBean = new TestBean();
|
TestBean testBean = new TestBean();
|
||||||
@@ -76,7 +92,6 @@ public class SpringValidatorAdapterTests {
|
|||||||
assertThat(errors.getFieldErrorCount("password"), is(1));
|
assertThat(errors.getFieldErrorCount("password"), is(1));
|
||||||
assertThat(messageSource.getMessage(errors.getFieldError("password"), Locale.ENGLISH),
|
assertThat(messageSource.getMessage(errors.getFieldError("password"), Locale.ENGLISH),
|
||||||
is("Password must be same value with Password(Confirm)"));
|
is("Password must be same value with Password(Confirm)"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // SPR-13406
|
@Test // SPR-13406
|
||||||
@@ -89,24 +104,30 @@ public class SpringValidatorAdapterTests {
|
|||||||
validatorAdapter.validate(testBean, errors);
|
validatorAdapter.validate(testBean, errors);
|
||||||
|
|
||||||
assertThat(errors.getFieldErrorCount("email"), is(1));
|
assertThat(errors.getFieldErrorCount("email"), is(1));
|
||||||
|
assertThat(errors.getFieldErrorCount("confirmEmail"), is(1));
|
||||||
assertThat(messageSource.getMessage(errors.getFieldError("email"), Locale.ENGLISH),
|
assertThat(messageSource.getMessage(errors.getFieldError("email"), Locale.ENGLISH),
|
||||||
is("email must be same value with confirmEmail"));
|
is("email must be same value with confirmEmail"));
|
||||||
|
assertThat(messageSource.getMessage(errors.getFieldError("confirmEmail"), Locale.ENGLISH),
|
||||||
|
is("Email required"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // SPR-13406
|
@Test // SPR-15123
|
||||||
public void testNoStringArgumentValue() {
|
public void testApplyMessageSourceResolvableToStringArgumentValueWithAlwaysUseMessageFormat() {
|
||||||
|
messageSource.setAlwaysUseMessageFormat(true);
|
||||||
|
|
||||||
TestBean testBean = new TestBean();
|
TestBean testBean = new TestBean();
|
||||||
testBean.setPassword("pass");
|
testBean.setEmail("test@example.com");
|
||||||
testBean.setConfirmPassword("pass");
|
testBean.setConfirmEmail("TEST@EXAMPLE.IO");
|
||||||
|
|
||||||
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(testBean, "testBean");
|
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(testBean, "testBean");
|
||||||
validatorAdapter.validate(testBean, errors);
|
validatorAdapter.validate(testBean, errors);
|
||||||
|
|
||||||
assertThat(errors.getFieldErrorCount("password"), is(1));
|
assertThat(errors.getFieldErrorCount("email"), is(1));
|
||||||
assertThat(messageSource.getMessage(errors.getFieldError("password"), Locale.ENGLISH),
|
assertThat(errors.getFieldErrorCount("confirmEmail"), is(1));
|
||||||
is("Size of Password is must be between 8 and 128"));
|
assertThat(messageSource.getMessage(errors.getFieldError("email"), Locale.ENGLISH),
|
||||||
|
is("email must be same value with confirmEmail"));
|
||||||
|
assertThat(messageSource.getMessage(errors.getFieldError("confirmEmail"), Locale.ENGLISH),
|
||||||
|
is("Email required"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -116,9 +137,12 @@ public class SpringValidatorAdapterTests {
|
|||||||
|
|
||||||
@Size(min = 8, max = 128)
|
@Size(min = 8, max = 128)
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
private String confirmPassword;
|
private String confirmPassword;
|
||||||
|
|
||||||
private String email;
|
private String email;
|
||||||
|
|
||||||
|
@Pattern(regexp = "[\\p{L} -]*", message = "Email required")
|
||||||
private String confirmEmail;
|
private String confirmEmail;
|
||||||
|
|
||||||
public String getPassword() {
|
public String getPassword() {
|
||||||
@@ -190,6 +214,7 @@ public class SpringValidatorAdapterTests {
|
|||||||
Same[] value();
|
Same[] value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class SameValidator implements ConstraintValidator<Same, Object> {
|
public static class SameValidator implements ConstraintValidator<Same, Object> {
|
||||||
|
|
||||||
private String field;
|
private String field;
|
||||||
|
|||||||
Reference in New Issue
Block a user