message builder
This commit is contained in:
parent
05e3c00a98
commit
3f5c43aaf5
|
|
@ -79,7 +79,7 @@ public interface Binder {
|
|||
|
||||
/**
|
||||
* Bind source values in the map to the properties of the model object.
|
||||
* @param values the source values to bind
|
||||
* @param sourceValues the source values to bind
|
||||
* @return the results of the binding operation
|
||||
*/
|
||||
BindingResults bind(Map<String, ? extends Object> sourceValues);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.expression.MapAccessor;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
|
|
@ -58,11 +59,12 @@ import org.springframework.ui.binding.BindingResults;
|
|||
import org.springframework.ui.binding.FormatterRegistry;
|
||||
import org.springframework.ui.format.AnnotationFormatterFactory;
|
||||
import org.springframework.ui.format.Formatter;
|
||||
import org.springframework.ui.message.MessageBuilder;
|
||||
import org.springframework.ui.message.ResolvableArgument;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A generic {@link Binder binder} suitable for use in most environments.
|
||||
* TODO - localization of alert messages using MessageResolver/MessageSource
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
* @see #configureBinding(BindingConfiguration)
|
||||
|
|
@ -87,6 +89,8 @@ public class GenericBinder implements Binder {
|
|||
|
||||
private static Formatter defaultFormatter = new DefaultFormatter();
|
||||
|
||||
private MessageSource messageSource;
|
||||
|
||||
/**
|
||||
* Creates a new binder for the model object.
|
||||
* @param model the model object containing properties this binder will bind to
|
||||
|
|
@ -118,6 +122,25 @@ public class GenericBinder implements Binder {
|
|||
this.formatterRegistry = formatterRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the MessageSource that resolves localized {@link BindingResult} alert messages.
|
||||
* @param messageSource the message source
|
||||
*/
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
this.messageSource = messageSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the TypeConverter that converts values as required by Binding setValue and getValue attempts.
|
||||
* For a setValue attempt, the TypeConverter will be asked to perform a conversion if the value parsed by the Binding's Formatter is not assignable to the target property type.
|
||||
* For a getValue attempt, the TypeConverter will be asked to perform a conversion if the property type does not match the type T required by the Binding's Formatter.
|
||||
* @param typeConverter the type converter used by the binding system, which is based on Spring EL
|
||||
* @see EvaluationContext#getTypeConverter()
|
||||
*/
|
||||
public void setTypeConverter(TypeConverter typeConverter) {
|
||||
this.typeConverter = typeConverter;
|
||||
}
|
||||
|
||||
public Binding configureBinding(BindingConfiguration configuration) {
|
||||
Binding binding;
|
||||
try {
|
||||
|
|
@ -158,14 +181,17 @@ public class GenericBinder implements Binder {
|
|||
} else {
|
||||
results.add(new NoSuchBindingResult(property, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
// subclassing hooks
|
||||
|
||||
|
||||
/**
|
||||
* Hook subclasses may use to filter the source values to bind.
|
||||
* This hook allows the binder to pre-process the source values before binding occurs.
|
||||
- * For example, a Binder might insert empty or default values for fields that are not present.
|
||||
- * As another example, a Binder might collapse multiple source values into a single source value.
|
||||
* @param sourceValues the original source values map provided by the caller
|
||||
* @return the filtered source values map that will be used to bind
|
||||
*/
|
||||
|
|
@ -334,7 +360,7 @@ public class GenericBinder implements Binder {
|
|||
try {
|
||||
formatter = getFormatter();
|
||||
} catch (EvaluationException e) {
|
||||
// could occur the property was not found or is not readable
|
||||
// could occur if the property was not found or is not readable
|
||||
// TODO probably should not handle all EL failures, only type conversion & property not found?
|
||||
return new ExpressionEvaluationErrorResult(property.getExpressionString(), formatted, e);
|
||||
}
|
||||
|
|
@ -352,7 +378,7 @@ public class GenericBinder implements Binder {
|
|||
try {
|
||||
formatter = getFormatter();
|
||||
} catch (EvaluationException e) {
|
||||
// could occur the property was not found or is not readable
|
||||
// could occur if the property was not found or is not readable
|
||||
// TODO probably should not handle all EL failures, only type conversion & property not found?
|
||||
return new ExpressionEvaluationErrorResult(property.getExpressionString(), formatted, e);
|
||||
}
|
||||
|
|
@ -461,16 +487,17 @@ public class GenericBinder implements Binder {
|
|||
}
|
||||
}
|
||||
|
||||
static class NoSuchBindingResult implements BindingResult {
|
||||
class NoSuchBindingResult implements BindingResult {
|
||||
|
||||
private String property;
|
||||
|
||||
|
||||
private Object sourceValue;
|
||||
|
||||
|
||||
public NoSuchBindingResult(String property, Object sourceValue) {
|
||||
this.property = property;
|
||||
this.sourceValue = sourceValue;
|
||||
}
|
||||
|
||||
|
||||
public String getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
|
@ -486,7 +513,6 @@ public class GenericBinder implements Binder {
|
|||
public Alert getAlert() {
|
||||
return new AbstractAlert() {
|
||||
public String getElement() {
|
||||
// TODO append model first? e.g. model.property
|
||||
return getProperty();
|
||||
}
|
||||
|
||||
|
|
@ -499,21 +525,30 @@ public class GenericBinder implements Binder {
|
|||
}
|
||||
|
||||
public String getMessage() {
|
||||
return "Failed to bind to property '" + property + "'; no binding has been added for the property";
|
||||
MessageBuilder builder = new MessageBuilder(messageSource);
|
||||
builder.code(getCode());
|
||||
builder.arg("label", new ResolvableArgument(property));
|
||||
builder.arg("value", sourceValue);
|
||||
builder.defaultMessage("Failed to bind to property '" + property
|
||||
+ "'; no binding has been added for the property");
|
||||
return builder.build();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class InvalidFormatResult implements BindingResult {
|
||||
|
||||
class InvalidFormatResult implements BindingResult {
|
||||
|
||||
private String property;
|
||||
|
||||
private Object formatted;
|
||||
private Object sourceValue;
|
||||
|
||||
public InvalidFormatResult(String property, Object formatted, ParseException e) {
|
||||
private ParseException cause;
|
||||
|
||||
public InvalidFormatResult(String property, Object sourceValue, ParseException cause) {
|
||||
this.property = property;
|
||||
this.formatted = formatted;
|
||||
this.sourceValue = sourceValue;
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
public String getProperty() {
|
||||
|
|
@ -521,7 +556,7 @@ public class GenericBinder implements Binder {
|
|||
}
|
||||
|
||||
public Object getSourceValue() {
|
||||
return formatted;
|
||||
return sourceValue;
|
||||
}
|
||||
|
||||
public boolean isFailure() {
|
||||
|
|
@ -531,7 +566,6 @@ public class GenericBinder implements Binder {
|
|||
public Alert getAlert() {
|
||||
return new AbstractAlert() {
|
||||
public String getElement() {
|
||||
// TODO append model first? e.g. model.property
|
||||
return getProperty();
|
||||
}
|
||||
|
||||
|
|
@ -544,26 +578,31 @@ public class GenericBinder implements Binder {
|
|||
}
|
||||
|
||||
public String getMessage() {
|
||||
return "Failed to bind to property '" + property + "'; the user value "
|
||||
+ StylerUtils.style(formatted) + " has an invalid format and could no be parsed";
|
||||
MessageBuilder builder = new MessageBuilder(messageSource);
|
||||
builder.code(getCode());
|
||||
builder.arg("label", new ResolvableArgument(property));
|
||||
builder.arg("value", sourceValue);
|
||||
builder.arg("errorOffset", cause.getErrorOffset());
|
||||
builder.defaultMessage("Failed to bind to property '" + property + "'; the user value "
|
||||
+ StylerUtils.style(sourceValue) + " has an invalid format and could no be parsed");
|
||||
return builder.build();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// TODO the if branching in here is not very clean
|
||||
static class ExpressionEvaluationErrorResult implements BindingResult {
|
||||
class ExpressionEvaluationErrorResult implements BindingResult {
|
||||
|
||||
private String property;
|
||||
|
||||
private Object formatted;
|
||||
private Object sourceValue;
|
||||
|
||||
private EvaluationException e;
|
||||
private EvaluationException cause;
|
||||
|
||||
public ExpressionEvaluationErrorResult(String property, Object formatted, EvaluationException e) {
|
||||
public ExpressionEvaluationErrorResult(String property, Object sourceValue, EvaluationException cause) {
|
||||
this.property = property;
|
||||
this.formatted = formatted;
|
||||
this.e = e;
|
||||
this.sourceValue = sourceValue;
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
public String getProperty() {
|
||||
|
|
@ -571,7 +610,7 @@ public class GenericBinder implements Binder {
|
|||
}
|
||||
|
||||
public Object getSourceValue() {
|
||||
return formatted;
|
||||
return sourceValue;
|
||||
}
|
||||
|
||||
public boolean isFailure() {
|
||||
|
|
@ -581,95 +620,97 @@ public class GenericBinder implements Binder {
|
|||
public Alert getAlert() {
|
||||
return new AbstractAlert() {
|
||||
public String getElement() {
|
||||
// TODO append model first? e.g. model.property
|
||||
return getProperty();
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return getFailureCode();
|
||||
SpelMessage spelCode = ((SpelEvaluationException) cause).getMessageCode();
|
||||
if (spelCode == SpelMessage.EXCEPTION_DURING_PROPERTY_WRITE) {
|
||||
return "conversionFailed";
|
||||
} else if (spelCode == SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE) {
|
||||
return "propertyNotFound";
|
||||
} else {
|
||||
return "couldNotSetValue";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Severity getSeverity() {
|
||||
return getFailureSeverity();
|
||||
SpelMessage spelCode = ((SpelEvaluationException) cause).getMessageCode();
|
||||
if (spelCode == SpelMessage.EXCEPTION_DURING_PROPERTY_WRITE) {
|
||||
return Severity.FATAL;
|
||||
} else if (spelCode == SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE) {
|
||||
return Severity.WARNING;
|
||||
} else {
|
||||
return Severity.FATAL;
|
||||
}
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return getFailureMessage();
|
||||
SpelMessage spelCode = ((SpelEvaluationException) cause).getMessageCode();
|
||||
if (spelCode == SpelMessage.EXCEPTION_DURING_PROPERTY_WRITE) {
|
||||
AccessException accessException = (AccessException) cause.getCause();
|
||||
if (accessException.getCause() != null) {
|
||||
Throwable cause = accessException.getCause();
|
||||
if (cause instanceof SpelEvaluationException
|
||||
&& ((SpelEvaluationException) cause).getMessageCode() == SpelMessage.TYPE_CONVERSION_ERROR) {
|
||||
ConversionFailedException failure = (ConversionFailedException) cause.getCause();
|
||||
MessageBuilder builder = new MessageBuilder(messageSource);
|
||||
builder.code("conversionFailed");
|
||||
builder.arg("label", new ResolvableArgument(property));
|
||||
builder.arg("value", sourceValue);
|
||||
builder.defaultMessage("Failed to bind to property '" + property + "'; user value "
|
||||
+ StylerUtils.style(sourceValue) + " could not be converted to property type ["
|
||||
+ failure.getTargetType().getName() + "]");
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
} else if (spelCode == SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE) {
|
||||
MessageBuilder builder = new MessageBuilder(messageSource);
|
||||
builder.code(getCode());
|
||||
builder.arg("label", new ResolvableArgument(property));
|
||||
builder.arg("value", sourceValue);
|
||||
builder.defaultMessage("Failed to bind to property '" + property + "'; no such property exists on model");
|
||||
return builder.build();
|
||||
}
|
||||
MessageBuilder builder = new MessageBuilder(messageSource);
|
||||
builder.code("couldNotSetValue");
|
||||
builder.arg("label", new ResolvableArgument(property));
|
||||
builder.arg("value", sourceValue);
|
||||
builder.defaultMessage("Failed to bind to property '" + property + "'; reason = " + cause.getLocalizedMessage());
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
public String getFailureCode() {
|
||||
SpelMessage spelCode = ((SpelEvaluationException) e).getMessageCode();
|
||||
if (spelCode == SpelMessage.EXCEPTION_DURING_PROPERTY_WRITE) {
|
||||
return "typeConversionFailure";
|
||||
} else if (spelCode == SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE) {
|
||||
return "propertyNotFound";
|
||||
} else {
|
||||
// TODO return more specific code based on underlying EvaluationException error code
|
||||
return "couldNotSetValue";
|
||||
}
|
||||
}
|
||||
|
||||
public Severity getFailureSeverity() {
|
||||
SpelMessage spelCode = ((SpelEvaluationException) e).getMessageCode();
|
||||
if (spelCode == SpelMessage.EXCEPTION_DURING_PROPERTY_WRITE) {
|
||||
return Severity.FATAL;
|
||||
} else if (spelCode == SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE) {
|
||||
return Severity.WARNING;
|
||||
} else {
|
||||
return Severity.FATAL;
|
||||
}
|
||||
}
|
||||
|
||||
public String getFailureMessage() {
|
||||
SpelMessage spelCode = ((SpelEvaluationException) e).getMessageCode();
|
||||
if (spelCode == SpelMessage.EXCEPTION_DURING_PROPERTY_WRITE) {
|
||||
AccessException accessException = (AccessException) e.getCause();
|
||||
if (accessException.getCause() != null) {
|
||||
Throwable cause = accessException.getCause();
|
||||
if (cause instanceof SpelEvaluationException
|
||||
&& ((SpelEvaluationException) cause).getMessageCode() == SpelMessage.TYPE_CONVERSION_ERROR) {
|
||||
ConversionFailedException failure = (ConversionFailedException) cause.getCause();
|
||||
return "Failed to bind to property '" + property + "'; user value "
|
||||
+ StylerUtils.style(formatted) + " could not be converted to property type ["
|
||||
+ failure.getTargetType().getName() + "]";
|
||||
}
|
||||
}
|
||||
} else if (spelCode == SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE) {
|
||||
return "Failed to bind to property '" + property + "'; no such property exists on model";
|
||||
}
|
||||
return "Failed to bind to property '" + property + "'; reason = " + e.getLocalizedMessage();
|
||||
}
|
||||
}
|
||||
|
||||
static class SuccessResult implements BindingResult {
|
||||
class SuccessResult implements BindingResult {
|
||||
|
||||
private String property;
|
||||
|
||||
private Object formatted;
|
||||
private Object sourceValue;
|
||||
|
||||
public SuccessResult(String property, Object formatted) {
|
||||
public SuccessResult(String property, Object sourceValue) {
|
||||
this.property = property;
|
||||
this.formatted = formatted;
|
||||
this.sourceValue = sourceValue;
|
||||
}
|
||||
|
||||
public String getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
|
||||
public Object getSourceValue() {
|
||||
return formatted;
|
||||
return sourceValue;
|
||||
}
|
||||
|
||||
|
||||
public boolean isFailure() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public Alert getAlert() {
|
||||
return new AbstractAlert() {
|
||||
public String getElement() {
|
||||
// TODO append model first? e.g. model.property
|
||||
return getProperty();
|
||||
}
|
||||
|
||||
|
|
@ -682,13 +723,18 @@ public class GenericBinder implements Binder {
|
|||
}
|
||||
|
||||
public String getMessage() {
|
||||
return "Sucessfully bound user value " + StylerUtils.style(formatted) + "to property '" + property + "'";
|
||||
MessageBuilder builder = new MessageBuilder(messageSource);
|
||||
builder.code("bindSuccess");
|
||||
builder.arg("label", new ResolvableArgument(property));
|
||||
builder.arg("value", sourceValue);
|
||||
builder.defaultMessage("Successfully bound user value " + StylerUtils.style(sourceValue) + "to property '" + property + "'");
|
||||
return builder.build();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static abstract class AbstractAlert implements Alert {
|
||||
public String toString() {
|
||||
return getElement() + ":" + getCode() + " - " + getMessage();
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ final class DefaultMessageResolver implements MessageResolver, MessageSourceReso
|
|||
|
||||
private Map<String, Object> args;
|
||||
|
||||
private String defaultText;
|
||||
private String defaultMessage;
|
||||
|
||||
private ExpressionParser expressionParser;
|
||||
|
||||
|
|
@ -47,18 +47,25 @@ final class DefaultMessageResolver implements MessageResolver, MessageSourceReso
|
|||
ExpressionParser expressionParser) {
|
||||
this.codes = codes;
|
||||
this.args = args;
|
||||
this.defaultText = defaultText;
|
||||
this.defaultMessage = defaultText;
|
||||
this.expressionParser = expressionParser;
|
||||
}
|
||||
|
||||
// implementing MessageResolver
|
||||
|
||||
public String resolveMessage(MessageSource messageSource, Locale locale) {
|
||||
if (messageSource == null) {
|
||||
if (defaultMessage != null) {
|
||||
return defaultMessage;
|
||||
} else {
|
||||
throw new MessageResolutionException("Unable to resolve message; MessagSource argument is null and no defaultMessage is configured");
|
||||
}
|
||||
}
|
||||
String messageString;
|
||||
try {
|
||||
messageString = messageSource.getMessage(this, locale);
|
||||
} catch (NoSuchMessageException e) {
|
||||
throw new MessageResolutionException("Unable to resolve message in MessageSource [" + messageSource + "]", e);
|
||||
throw new MessageResolutionException("Unable to resolve message in" + messageSource, e);
|
||||
}
|
||||
Expression message;
|
||||
try {
|
||||
|
|
@ -88,11 +95,11 @@ final class DefaultMessageResolver implements MessageResolver, MessageSourceReso
|
|||
}
|
||||
|
||||
public String getDefaultMessage() {
|
||||
return defaultText;
|
||||
return defaultMessage;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("codes", codes).append("defaultText", defaultText).toString();
|
||||
return new ToStringCreator(this).append("codes", codes).append("defaultText", defaultMessage).toString();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* Copyright 2004-2009 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.ui.message;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
|
||||
/**
|
||||
* Builds a localized message for display in a user interface.
|
||||
* Allows convenient specification of the codes to try to resolve the message.
|
||||
* Also supports named arguments that can inserted into a message template using eval #{expressions}.
|
||||
* <p>
|
||||
* Usage example:
|
||||
* <pre>
|
||||
* String message = new MessageBuilder(messageSource).
|
||||
* code("invalidFormat").
|
||||
* arg("label", new ResolvableArgument("mathForm.decimalField")).
|
||||
* arg("format", "#,###.##").
|
||||
* defaultMessage("The decimal field must be in format #,###.##").
|
||||
* build();
|
||||
* </pre>
|
||||
* Example messages.properties loaded by the MessageSource:
|
||||
* <pre>
|
||||
* invalidFormat=The #{label} must be in format #{format}.
|
||||
* mathForm.decimalField=Decimal Field
|
||||
* </pre>
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
* @see #code(String)
|
||||
* @see #arg(String, Object)
|
||||
* @see #defaultMessage(String)
|
||||
* @see #locale(Locale)
|
||||
*/
|
||||
public class MessageBuilder {
|
||||
|
||||
private MessageSource messageSource;
|
||||
|
||||
private Locale locale;
|
||||
|
||||
private MessageResolverBuilder builder = new MessageResolverBuilder();
|
||||
|
||||
/**
|
||||
* Create a new MessageBuilder that builds messages from message templates defined in the MessageSource
|
||||
* @param messageSource the message source
|
||||
*/
|
||||
public MessageBuilder(MessageSource messageSource) {
|
||||
this.messageSource = messageSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a code that will be tried to lookup the message template used to create the localized message.
|
||||
* Successive calls to this method add additional codes.
|
||||
* Codes are tried in the order they are added.
|
||||
* @param code a message code to try
|
||||
* @return this, for fluent API usage
|
||||
*/
|
||||
public MessageBuilder code(String code) {
|
||||
builder.code(code);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an argument to insert into the message.
|
||||
* Named arguments are inserted by eval #{expressions} denoted within the message template.
|
||||
* For example, the value of the 'format' argument would be inserted where a corresponding #{format} expression is defined in the message template.
|
||||
* Successive calls to this method add additional arguments.
|
||||
* May also add {@link ResolvableArgument resolvable arguments} whose values are resolved against the MessageSource.
|
||||
* @param name the argument name
|
||||
* @param value the argument value
|
||||
* @return this, for fluent API usage
|
||||
* @see ResolvableArgument
|
||||
*/
|
||||
public MessageBuilder arg(String name, Object value) {
|
||||
builder.arg(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default message.
|
||||
* If there are no codes to try, this will be used as the message.
|
||||
* If there are codes to try but none of those resolve to a message, this will be used as the message.
|
||||
* @param message the default text
|
||||
* @return this, for fluent API usage
|
||||
*/
|
||||
public MessageBuilder defaultMessage(String message) {
|
||||
builder.defaultMessage(message);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the message locale.
|
||||
* If not set, the default locale the Locale of the current request obtained from {@link LocaleContextHolder#getLocale()}.
|
||||
* @param message the locale
|
||||
* @return this, for fluent API usage
|
||||
*/
|
||||
public MessageBuilder locale(Locale locale) {
|
||||
this.locale = locale;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the resolver for the message.
|
||||
* Call after recording all builder instructions.
|
||||
* @return the built message resolver
|
||||
* @throws IllegalStateException if no codes have been added and there is no default message
|
||||
*/
|
||||
public String build() {
|
||||
return builder.build().resolveMessage(messageSource, getLocale());
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
private Locale getLocale() {
|
||||
if (locale != null) {
|
||||
return locale;
|
||||
} else {
|
||||
return LocaleContextHolder.getLocale();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -23,6 +23,14 @@ package org.springframework.ui.message;
|
|||
@SuppressWarnings("serial")
|
||||
public class MessageResolutionException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* Creates a new message resolution exception.
|
||||
* @param message a messaging describing the failure
|
||||
*/
|
||||
public MessageResolutionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new message resolution exception.
|
||||
* @param message a messaging describing the failure
|
||||
|
|
|
|||
|
|
@ -20,8 +20,7 @@ import java.util.Locale;
|
|||
import org.springframework.context.MessageSource;
|
||||
|
||||
/**
|
||||
* A factory for a localized message.
|
||||
* TODO - consider putting this abstraction together with MessageSource; does it need to be in its own package?
|
||||
* A factory for a localized message resolved from a MessageSource.
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
* @see MessageSource
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ public class GenericBinderTests {
|
|||
BindingResults results = binder.bind(values);
|
||||
assertEquals(3, results.size());
|
||||
assertTrue(results.get(1).isFailure());
|
||||
assertEquals("typeConversionFailure", results.get(1).getAlert().getCode());
|
||||
assertEquals("conversionFailed", results.get(1).getAlert().getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -229,7 +229,7 @@ public class GenericBinderTests {
|
|||
assertEquals(0, b.getCollectionValues().length);
|
||||
BindingResult result = b.setValue(new String[] { "BAR", "BOGUS", "BOOP" });
|
||||
assertTrue(result.isFailure());
|
||||
assertEquals("typeConversionFailure", result.getAlert().getCode());
|
||||
assertEquals("conversionFailed", result.getAlert().getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
package org.springframework.ui.message;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class MessageBuilderTests {
|
||||
|
||||
@Test
|
||||
public void buildMessage() {
|
||||
MockMessageSource messageSource = new MockMessageSource();
|
||||
messageSource.addMessage("invalidFormat", Locale.US, "#{label} must be in format #{format}");
|
||||
messageSource.addMessage("mathForm.decimalField", Locale.US, "Decimal Field");
|
||||
MessageBuilder builder = new MessageBuilder(messageSource);
|
||||
String message = builder.code("invalidFormat").arg("label", new ResolvableArgument("mathForm.decimalField"))
|
||||
.arg("format", "#,###.##").defaultMessage("Field must be in format #,###.##").build();
|
||||
assertEquals("Decimal Field must be in format #,###.##", message);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue