made message codes resolver pluggable; now delegate resolver strategy from Errors MessageContext adapter

This commit is contained in:
Keith Donald
2009-03-10 03:15:46 +00:00
parent 8f8082321c
commit 53ee66b04e
8 changed files with 144 additions and 37 deletions

View File

@@ -29,6 +29,7 @@ import org.springframework.binding.mapping.MappingResultsCriteria;
import org.springframework.validation.AbstractErrors;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.ObjectError;
/**
@@ -49,31 +50,47 @@ public class MessageContextErrors extends AbstractErrors {
private MappingResults mappingResults;
private MessageCodesResolver bindingErrorMessageCodesResolver;
/**
* Creates a new message context errors adapter.
* @param messageContext the backing message context
* @param objectName the object name
* @param boundObject the model object
* @param expressionParser the expression parser
* @param bindingErrorMessageCodesResolver the message codes resolver
* @param mappingResults object mapping results
*/
public MessageContextErrors(MessageContext messageContext, String objectName, Object boundObject,
ExpressionParser expressionParser, MappingResults mappingResults) {
ExpressionParser expressionParser, MessageCodesResolver bindingErrorMessageCodesResolver,
MappingResults mappingResults) {
this.messageContext = messageContext;
this.objectName = objectName;
this.boundObject = boundObject;
this.expressionParser = expressionParser;
this.bindingErrorMessageCodesResolver = bindingErrorMessageCodesResolver;
this.mappingResults = mappingResults;
}
public void reject(String errorCode, Object[] errorArgs, String defaultMessage) {
messageContext.addMessage(new MessageBuilder().error().code(errorCode).args(errorArgs).defaultText(
String[] messageCodes = bindingErrorMessageCodesResolver.resolveMessageCodes(errorCode, defaultMessage);
messageContext.addMessage(new MessageBuilder().error().codes(messageCodes).args(errorArgs).defaultText(
defaultMessage).build());
}
public void rejectValue(String field, String errorCode, Object[] errorArgs, String defaultMessage) {
messageContext.addMessage(new MessageBuilder().error().source(fixedField(field)).code(errorCode)
.args(errorArgs).defaultText(defaultMessage).build());
field = fixedField(field);
Class fieldType;
if (expressionParser != null) {
FluentParserContext parserContext = new FluentParserContext().evaluate(boundObject.getClass());
fieldType = expressionParser.parseExpression(field, parserContext).getValueType(boundObject);
} else {
fieldType = null;
}
String[] messageCodes = bindingErrorMessageCodesResolver.resolveMessageCodes(errorCode, defaultMessage, field,
fieldType);
messageContext.addMessage(new MessageBuilder().error().source(field).codes(messageCodes).args(errorArgs)
.defaultText(defaultMessage).build());
}
public void addAllErrors(Errors errors) {

View File

@@ -6,6 +6,7 @@ import java.util.Locale;
import junit.framework.TestCase;
import org.springframework.context.support.StaticMessageSource;
import org.springframework.validation.DefaultMessageCodesResolver;
import org.springframework.validation.MapBindingResult;
public class MessageContextErrorsTests extends TestCase {
@@ -16,7 +17,8 @@ public class MessageContextErrorsTests extends TestCase {
DefaultMessageContext context = new DefaultMessageContext(messageSource);
Object object = new Object();
MessageContextErrors errors = new MessageContextErrors(context, "object", object, null, null);
MessageContextErrors errors = new MessageContextErrors(context, "object", object, null,
new DefaultMessageCodesResolver(), null);
errors.reject("foo");
errors.reject("bogus", "baz");
errors.reject("bar", new Object[] { "boop" }, null);
@@ -44,7 +46,8 @@ public class MessageContextErrorsTests extends TestCase {
DefaultMessageContext context = new DefaultMessageContext(messageSource);
Object object = new Object();
MessageContextErrors errors = new MessageContextErrors(context, "object", object, null, null);
MessageContextErrors errors = new MessageContextErrors(context, "object", object, null,
new DefaultMessageCodesResolver(), null);
errors.rejectValue("field", "foo");
errors.rejectValue("field", "bogus", "baz");
errors.rejectValue("field", "bar", new Object[] { "boop" }, null);
@@ -72,7 +75,8 @@ public class MessageContextErrorsTests extends TestCase {
DefaultMessageContext context = new DefaultMessageContext(messageSource);
Object object = new Object();
MessageContextErrors errors = new MessageContextErrors(context, "object", object, null, null);
MessageContextErrors errors = new MessageContextErrors(context, "object", object, null,
new DefaultMessageCodesResolver(), null);
MapBindingResult result = new MapBindingResult(new HashMap(), "object");
result.reject("bar", new Object[] { "boop" }, null);
result.rejectValue("field", "bar", new Object[] { "boop" }, null);

View File

@@ -29,11 +29,13 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.binding.expression.Expression;
import org.springframework.util.StringUtils;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.webflow.definition.TransitionDefinition;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.execution.RequestContextHolder;
import org.springframework.webflow.execution.View;
import org.springframework.webflow.validation.ValidationHelper;
import org.springframework.webflow.validation.WebFlowMessageCodesResolver;
/**
* The default {@link ActionListener} implementation to be used with Web Flow.
@@ -58,6 +60,8 @@ public class FlowActionListener implements ActionListener {
private ActionListener delegate;
private MessageCodesResolver messageCodesResolver = new WebFlowMessageCodesResolver();
public FlowActionListener(ActionListener delegate) {
this.delegate = delegate;
}
@@ -152,6 +156,6 @@ public class FlowActionListener implements ActionListener {
private void validate(RequestContext requestContext, Object model, String eventId) {
new ValidationHelper(model, requestContext, eventId, getModelExpression(requestContext).getExpressionString(),
null, null).validate();
null, messageCodesResolver, null).validate();
}
}

View File

@@ -43,6 +43,7 @@ import org.springframework.binding.message.MessageResolver;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.Assert;
import org.springframework.validation.BindingResult;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.web.util.WebUtils;
import org.springframework.webflow.core.collection.AttributeMap;
import org.springframework.webflow.core.collection.ParameterMap;
@@ -54,6 +55,7 @@ import org.springframework.webflow.execution.FlowExecutionKey;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.execution.View;
import org.springframework.webflow.validation.ValidationHelper;
import org.springframework.webflow.validation.WebFlowMessageCodesResolver;
/**
* Base view implementation for the Spring Web MVC Servlet and Spring Web MVC Portlet frameworks.
@@ -86,6 +88,8 @@ public abstract class AbstractMvcView implements View {
private BinderConfiguration binderConfiguration;
private MessageCodesResolver bindingErrorMessageCodesResolver = new WebFlowMessageCodesResolver();
/**
* Creates a new MVC view.
* @param view the Spring MVC view to render
@@ -120,6 +124,14 @@ public abstract class AbstractMvcView implements View {
this.binderConfiguration = binderConfiguration;
}
/**
* Set the message codes resolver to use to resolve bind and validation failure message codes.
* @param bindingErrorMessageCodesResolver the binding error message code resolver to use
*/
public void setBindingErrorMessageCodesResolver(MessageCodesResolver bindingErrorMessageCodesResolver) {
this.bindingErrorMessageCodesResolver = bindingErrorMessageCodesResolver;
}
/**
* Specify a prefix that can be used for parameters that mark potentially empty fields, having "prefix + field" as
* name. Such a marker parameter is checked by existence: You can send any value for it, for example "visible". This
@@ -483,12 +495,13 @@ public abstract class AbstractMvcView implements View {
}
private MessageResolver createMessageResolver(MappingResult error) {
String model = getModelExpression().getExpressionString();
String field = error.getMapping().getTargetExpression().getExpressionString();
String errorCode = error.getCode();
String propertyErrorCode = new StringBuffer().append(getModelExpression().getExpressionString()).append('.')
.append(field).append('.').append(errorCode).toString();
return new MessageBuilder().error().source(field).code(propertyErrorCode).code(errorCode).resolvableArg(field)
.defaultText(errorCode + " on " + field).build();
Class fieldType = error.getMapping().getTargetExpression().getValueType(getModelObject());
String[] messageCodes = bindingErrorMessageCodesResolver.resolveMessageCodes(error.getCode(), model, field,
fieldType);
return new MessageBuilder().error().source(field).codes(messageCodes).resolvableArg(field).defaultText(
error.getCode() + " on " + field).build();
}
private Boolean getValidateAttribute(TransitionDefinition transition) {
@@ -504,7 +517,7 @@ public abstract class AbstractMvcView implements View {
logger.debug("Validating model");
}
new ValidationHelper(model, requestContext, eventId, getModelExpression().getExpressionString(),
expressionParser, mappingResults).validate();
expressionParser, bindingErrorMessageCodesResolver, mappingResults).validate();
}
private static class PropertyNotFoundError implements MappingResultsCriteria {

View File

@@ -31,6 +31,7 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.webflow.execution.RequestContext;
@@ -53,6 +54,8 @@ public class ValidationHelper {
private final ExpressionParser expressionParser;
private final MessageCodesResolver bindingErrorMessageCodesResolver;
private final MappingResults mappingResults;
/**
@@ -73,7 +76,8 @@ public class ValidationHelper {
* @param mappingResults object mapping results
*/
public ValidationHelper(Object model, RequestContext requestContext, String eventId, String modelName,
ExpressionParser expressionParser, MappingResults mappingResults) {
ExpressionParser expressionParser, MessageCodesResolver bindingErrorMessageCodesResolver,
MappingResults mappingResults) {
Assert.notNull(model, "The model to validate is required");
Assert.notNull(requestContext, "The request context for the validator is required");
this.model = model;
@@ -81,6 +85,7 @@ public class ValidationHelper {
this.eventId = eventId;
this.modelName = modelName;
this.expressionParser = expressionParser;
this.bindingErrorMessageCodesResolver = bindingErrorMessageCodesResolver;
this.mappingResults = mappingResults;
}
@@ -123,7 +128,7 @@ public class ValidationHelper {
validateMethod = ReflectionUtils.findMethod(model.getClass(), methodName, new Class[] { Errors.class });
if (validateMethod != null) {
MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName,
model, expressionParser, mappingResults);
model, expressionParser, bindingErrorMessageCodesResolver, mappingResults);
if (logger.isDebugEnabled()) {
logger.debug("Invoking current state model validation method '" + methodName + "(Errors)'");
}
@@ -152,7 +157,7 @@ public class ValidationHelper {
logger.debug("Invoking default model validation method 'validate(Errors)'");
}
MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName,
model, expressionParser, mappingResults);
model, expressionParser, bindingErrorMessageCodesResolver, mappingResults);
ReflectionUtils.invokeMethod(validateMethod, model, new Object[] { errors });
return true;
}
@@ -200,7 +205,7 @@ public class ValidationHelper {
+ ClassUtils.getShortName(model.getClass()) + ", Errors)'");
}
MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName,
model, expressionParser, mappingResults);
model, expressionParser, bindingErrorMessageCodesResolver, mappingResults);
ReflectionUtils.invokeMethod(validateMethod, validator, new Object[] { model, errors });
return true;
}
@@ -222,7 +227,7 @@ public class ValidationHelper {
logger.debug("Invoking Spring Validator '" + ClassUtils.getShortName(validator.getClass()) + "'");
}
MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName,
model, expressionParser, mappingResults);
model, expressionParser, bindingErrorMessageCodesResolver, mappingResults);
((Validator) validator).validate(model, errors);
return true;
}
@@ -247,7 +252,7 @@ public class ValidationHelper {
+ ".validate(" + ClassUtils.getShortName(model.getClass()) + ", Errors)'");
}
MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName,
model, expressionParser, mappingResults);
model, expressionParser, bindingErrorMessageCodesResolver, mappingResults);
ReflectionUtils.invokeMethod(validateMethod, validator, new Object[] { model, errors });
return true;
}

View File

@@ -0,0 +1,48 @@
package org.springframework.webflow.validation;
import org.springframework.validation.MessageCodesResolver;
public class WebFlowMessageCodesResolver implements MessageCodesResolver {
private String failureMessageCodePrefix = "";
/**
* The prefix to prepend to all validation failure message codes.
*/
public String getFailureMessageCodePrefix() {
return failureMessageCodePrefix;
}
/**
* A prefix to prepend to all validation failure message codes; default if not set explicitly is "validation".
* @param failureMessageCodePrefix the failure message code prefix
*/
public void setFailureMessageCodePrefix(String failureMessageCodePrefix) {
this.failureMessageCodePrefix = failureMessageCodePrefix;
}
public String[] resolveMessageCodes(String errorCode, String objectName) {
String constraintMessageCode = appendFailureMessageCodePrefix().append(codeSeparator()).append(errorCode)
.toString();
String objectConstraintMessageCode = appendFailureMessageCodePrefix().append(codeSeparator())
.append(objectName).append(codeSeparator()).append(errorCode).toString();
return new String[] { objectConstraintMessageCode, constraintMessageCode };
}
public String[] resolveMessageCodes(String errorCode, String objectName, String field, Class fieldType) {
String propertyConstraintMessageCode = appendFailureMessageCodePrefix().append(codeSeparator()).append(
objectName).append(codeSeparator()).append(field).append(codeSeparator()).append(errorCode).toString();
String typeConstraintMessageCode = appendFailureMessageCodePrefix().append(codeSeparator()).append(fieldType)
.append(codeSeparator()).append(errorCode).toString();
return new String[] { propertyConstraintMessageCode, typeConstraintMessageCode, propertyConstraintMessageCode };
}
protected StringBuilder appendFailureMessageCodePrefix() {
return new StringBuilder().append(failureMessageCodePrefix);
}
protected char codeSeparator() {
return '.';
}
}

View File

@@ -23,7 +23,7 @@ import org.springframework.validation.Errors;
public class StubModelErrors {
public void validateMockState(Object model, Errors errors) {
errors.rejectValue("errors-external", "", "");
errors.rejectValue("errors-external", "test", "test");
}
}

View File

@@ -20,6 +20,7 @@ import junit.framework.TestCase;
import org.springframework.binding.message.MessageContext;
import org.springframework.binding.validation.ValidationContext;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.validation.DefaultMessageCodesResolver;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import org.springframework.webflow.engine.Flow;
@@ -46,7 +47,7 @@ public class ValidationHelperTests extends TestCase {
public void testValidateWithMessageContext() {
Object model = new StubModelMessageContext();
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null, null);
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null, null, null);
helper.validate();
MessageContext messages = requestContext.getMessageContext();
assertEquals(1, messages.getAllMessages().length);
@@ -56,7 +57,8 @@ public class ValidationHelperTests extends TestCase {
public void testValidateWithValidationContext() {
Object model = new StubModelValidationContext();
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null, null);
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null,
new DefaultMessageCodesResolver(), null);
helper.validate();
MessageContext messages = requestContext.getMessageContext();
assertEquals(1, messages.getAllMessages().length);
@@ -67,7 +69,8 @@ public class ValidationHelperTests extends TestCase {
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("modelValidator", StubModelMessageContext.class);
((Flow) requestContext.getActiveFlow()).setApplicationContext(applicationContext);
ValidationHelper helper = new ValidationHelper(new Object(), requestContext, eventId, modelName, null, null);
ValidationHelper helper = new ValidationHelper(new Object(), requestContext, eventId, modelName, null,
new DefaultMessageCodesResolver(), null);
helper.validate();
MessageContext messages = requestContext.getMessageContext();
assertEquals(1, messages.getAllMessages().length);
@@ -78,7 +81,8 @@ public class ValidationHelperTests extends TestCase {
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("modelValidator", StubModelValidationContext.class);
((Flow) requestContext.getActiveFlow()).setApplicationContext(applicationContext);
ValidationHelper helper = new ValidationHelper(new Object(), requestContext, eventId, modelName, null, null);
ValidationHelper helper = new ValidationHelper(new Object(), requestContext, eventId, modelName, null,
new DefaultMessageCodesResolver(), null);
helper.validate();
MessageContext messages = requestContext.getMessageContext();
assertEquals(1, messages.getAllMessages().length);
@@ -89,7 +93,8 @@ public class ValidationHelperTests extends TestCase {
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("modelValidator", StubModelErrors.class);
((Flow) requestContext.getActiveFlow()).setApplicationContext(applicationContext);
ValidationHelper helper = new ValidationHelper(new Object(), requestContext, eventId, modelName, null, null);
ValidationHelper helper = new ValidationHelper(new Object(), requestContext, eventId, modelName, null,
new DefaultMessageCodesResolver(), null);
helper.validate();
MessageContext messages = requestContext.getMessageContext();
assertEquals(1, messages.getAllMessages().length);
@@ -100,7 +105,8 @@ public class ValidationHelperTests extends TestCase {
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("modelValidator", StubModelErrorsOverridden.class);
((Flow) requestContext.getActiveFlow()).setApplicationContext(applicationContext);
ValidationHelper helper = new ValidationHelper(new Object(), requestContext, eventId, modelName, null, null);
ValidationHelper helper = new ValidationHelper(new Object(), requestContext, eventId, modelName, null,
new DefaultMessageCodesResolver(), null);
helper.validate();
MessageContext messages = requestContext.getMessageContext();
assertEquals(1, messages.getAllMessages().length);
@@ -109,7 +115,8 @@ public class ValidationHelperTests extends TestCase {
public void testStateAndFallbackModelValidationMethodInvoked() {
Model model = new Model();
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null, null);
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null,
new DefaultMessageCodesResolver(), null);
ViewState state1 = new ViewState(requestContext.getRootFlow(), "state1", new StubViewFactory());
requestContext.setCurrentState(state1);
helper.validate();
@@ -119,7 +126,8 @@ public class ValidationHelperTests extends TestCase {
public void testFallbackModelValidationMethodInvoked() {
Model model = new Model();
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null, null);
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null,
new DefaultMessageCodesResolver(), null);
ViewState state1 = new ViewState(requestContext.getRootFlow(), "state2", new StubViewFactory());
requestContext.setCurrentState(state1);
helper.validate();
@@ -129,7 +137,8 @@ public class ValidationHelperTests extends TestCase {
public void testStateAndFallbackErrorsModelValidationMethodInvoked() {
ErrorsModel model = new ErrorsModel();
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null, null);
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null,
new DefaultMessageCodesResolver(), null);
ViewState state1 = new ViewState(requestContext.getRootFlow(), "state1", new StubViewFactory());
requestContext.setCurrentState(state1);
helper.validate();
@@ -139,7 +148,8 @@ public class ValidationHelperTests extends TestCase {
public void testFallbackModelErrorsValidationMethodInvoked() {
ErrorsModel model = new ErrorsModel();
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null, null);
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null,
new DefaultMessageCodesResolver(), null);
ViewState state1 = new ViewState(requestContext.getRootFlow(), "state2", new StubViewFactory());
requestContext.setCurrentState(state1);
helper.validate();
@@ -154,7 +164,8 @@ public class ValidationHelperTests extends TestCase {
requestContext.getRootFlow().setApplicationContext(applicationContext);
Object model = new Object();
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null, null);
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null,
new DefaultMessageCodesResolver(), null);
ViewState state1 = new ViewState(requestContext.getRootFlow(), "state1", new StubViewFactory());
requestContext.setCurrentState(state1);
helper.validate();
@@ -169,7 +180,8 @@ public class ValidationHelperTests extends TestCase {
requestContext.getRootFlow().setApplicationContext(applicationContext);
Object model = new Object();
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null, null);
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null,
new DefaultMessageCodesResolver(), null);
ViewState state1 = new ViewState(requestContext.getRootFlow(), "state2", new StubViewFactory());
requestContext.setCurrentState(state1);
helper.validate();
@@ -184,7 +196,8 @@ public class ValidationHelperTests extends TestCase {
requestContext.getRootFlow().setApplicationContext(applicationContext);
Object model = new Object();
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null, null);
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null,
new DefaultMessageCodesResolver(), null);
ViewState state1 = new ViewState(requestContext.getRootFlow(), "state1", new StubViewFactory());
requestContext.setCurrentState(state1);
helper.validate();
@@ -199,7 +212,8 @@ public class ValidationHelperTests extends TestCase {
requestContext.getRootFlow().setApplicationContext(applicationContext);
Object model = new Object();
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null, null);
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null,
new DefaultMessageCodesResolver(), null);
ViewState state1 = new ViewState(requestContext.getRootFlow(), "state2", new StubViewFactory());
requestContext.setCurrentState(state1);
helper.validate();
@@ -214,7 +228,8 @@ public class ValidationHelperTests extends TestCase {
requestContext.getRootFlow().setApplicationContext(applicationContext);
Object model = new Object();
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null, null);
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null,
new DefaultMessageCodesResolver(), null);
ViewState state1 = new ViewState(requestContext.getRootFlow(), "state1", new StubViewFactory());
requestContext.setCurrentState(state1);
helper.validate();
@@ -229,7 +244,8 @@ public class ValidationHelperTests extends TestCase {
requestContext.getRootFlow().setApplicationContext(applicationContext);
Object model = new Object();
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null, null);
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null,
new DefaultMessageCodesResolver(), null);
ViewState state1 = new ViewState(requestContext.getRootFlow(), "state2", new StubViewFactory());
requestContext.setCurrentState(state1);
helper.validate();