validate method fallback

This commit is contained in:
Keith Donald
2008-11-12 19:37:41 +00:00
parent 57fcb7ece9
commit c66b1d6492
2 changed files with 156 additions and 52 deletions

View File

@@ -28,6 +28,7 @@ import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import org.springframework.webflow.execution.RequestContext;
/**
@@ -82,82 +83,117 @@ public class ValidationHelper {
* Invoke the validators available by convention.
*/
public void validate() {
String validateMethodName = "validate" + StringUtils.capitalize(requestContext.getCurrentState().getId());
validateWithContext(model, validateMethodName);
BeanFactory beanFactory = requestContext.getActiveFlow().getApplicationContext();
if (beanFactory != null && StringUtils.hasText(modelName)) {
String validatorName = modelName + "Validator";
if (beanFactory.containsBean(validatorName)) {
Object validator = beanFactory.getBean(validatorName);
if (!validateWithModelAndContext(model, validator, validateMethodName)) {
validateWithModelAndErrors(model, validator, validateMethodName);
}
}
invokeModelValidationMethod(model);
Object validator = getModelValidator();
if (validator != null) {
invokeModelValidator(model, validator);
}
}
/*
* Invoke validate method on the model for the current state passing either a MessageContext or ValidationContext.
* Preference is given to the ValidationContext method.
*/
private boolean validateWithContext(Object model, String validateMethodName) {
boolean validationInvoked = false;
Method validateMethod = ReflectionUtils.findMethod(model.getClass(), validateMethodName,
private boolean invokeModelValidationMethod(Object model) {
boolean methodInvoked = invokeValidateMethodForCurrentState(model);
if (!methodInvoked) {
methodInvoked = invokeDefaultValidateMethod(model);
}
return methodInvoked;
}
private boolean invokeValidateMethodForCurrentState(Object model) {
String methodName = "validate" + StringUtils.capitalize(requestContext.getCurrentState().getId());
Method validateMethod = ReflectionUtils.findMethod(model.getClass(), methodName,
new Class[] { ValidationContext.class });
if (validateMethod != null) {
ReflectionUtils.invokeMethod(validateMethod, model, new Object[] { new DefaultValidationContext(
requestContext, eventId, mappingResults) });
validationInvoked = true;
return true;
} else {
validateMethod = ReflectionUtils.findMethod(model.getClass(), validateMethodName,
validateMethod = ReflectionUtils.findMethod(model.getClass(), methodName,
new Class[] { MessageContext.class });
if (validateMethod != null) {
ReflectionUtils
.invokeMethod(validateMethod, model, new Object[] { requestContext.getMessageContext() });
validationInvoked = true;
return true;
}
}
return validationInvoked;
return false;
}
/*
* Invoke validate method on a distinct validator providing the model to validate and either a MessageContext or
* ValidationContext. Preference is given to the ValidationContext method.
*/
private boolean validateWithModelAndContext(Object model, Object validator, String validateMethodName) {
boolean validationInvoked = false;
Method validateMethod = ReflectionUtils.findMethod(validator.getClass(), validateMethodName, new Class[] {
model.getClass(), ValidationContext.class });
private boolean invokeDefaultValidateMethod(Object model) {
Method validateMethod = ReflectionUtils.findMethod(model.getClass(), "validate",
new Class[] { ValidationContext.class });
if (validateMethod != null) {
ReflectionUtils.invokeMethod(validateMethod, model, new Object[] { new DefaultValidationContext(
requestContext, eventId, mappingResults) });
return true;
} else {
return false;
}
}
private Object getModelValidator() {
BeanFactory beanFactory = requestContext.getActiveFlow().getApplicationContext();
if (beanFactory != null && StringUtils.hasText(modelName)) {
String validatorName = modelName + "Validator";
if (beanFactory.containsBean(validatorName)) {
return beanFactory.getBean(validatorName);
}
}
return null;
}
private boolean invokeModelValidator(Object model, Object validator) {
if (validator instanceof Validator) {
// existing validator, just invoke it
MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName,
model, expressionParser, mappingResults);
((Validator) validator).validate(model, errors);
return true;
}
// try reflection
boolean methodInvoked = invokeValidatorValidateMethodForCurrentState(model, validator);
if (!methodInvoked) {
methodInvoked = invokeValidatorDefaultValidateMethod(model, validator);
}
return methodInvoked;
}
private boolean invokeValidatorValidateMethodForCurrentState(Object model, Object validator) {
String methodName = "validate" + StringUtils.capitalize(requestContext.getCurrentState().getId());
Method validateMethod = ReflectionUtils.findMethod(validator.getClass(), methodName,
new Class[] { ValidationContext.class });
if (validateMethod != null) {
ReflectionUtils.invokeMethod(validateMethod, validator, new Object[] { model,
new DefaultValidationContext(requestContext, eventId, mappingResults) });
validationInvoked = true;
} else {
validateMethod = ReflectionUtils.findMethod(validator.getClass(), validateMethodName, new Class[] {
model.getClass(), MessageContext.class });
if (validateMethod != null) {
ReflectionUtils.invokeMethod(validateMethod, validator, new Object[] { model,
requestContext.getMessageContext() });
validationInvoked = true;
}
return true;
}
return validationInvoked;
}
/*
* Invoke validate method on a distinct validator providing the model to validate and an Errors object.
*/
private boolean validateWithModelAndErrors(Object model, Object validator, String validateMethodName) {
boolean validationInvoked = false;
Method validateMethod = ReflectionUtils.findMethod(validator.getClass(), validateMethodName, new Class[] {
model.getClass(), Errors.class });
// web flow 2.0.3 or < compatibility only
validateMethod = ReflectionUtils.findMethod(validator.getClass(), methodName, new Class[] { model.getClass(),
Errors.class });
if (validateMethod != null) {
MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName,
model, expressionParser, mappingResults);
ReflectionUtils.invokeMethod(validateMethod, validator, new Object[] { model, errors });
validationInvoked = true;
return true;
}
return validationInvoked;
validateMethod = ReflectionUtils.findMethod(validator.getClass(), methodName,
new Class[] { MessageContext.class });
if (validateMethod != null) {
ReflectionUtils.invokeMethod(validateMethod, model, new Object[] { requestContext.getMessageContext() });
return true;
}
return false;
}
}
private boolean invokeValidatorDefaultValidateMethod(Object model, Object validator) {
Method validateMethod = ReflectionUtils.findMethod(validator.getClass(), "validate", new Class[] {
model.getClass(), ValidationContext.class });
if (validateMethod != null) {
ReflectionUtils.invokeMethod(validateMethod, model, new Object[] { new DefaultValidationContext(
requestContext, eventId, mappingResults) });
return true;
} else {
return false;
}
}
}

View File

@@ -14,6 +14,7 @@ import junit.framework.TestCase;
import org.springframework.binding.convert.service.DefaultConversionService;
import org.springframework.binding.expression.support.StaticExpression;
import org.springframework.binding.validation.ValidationContext;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockMultipartFile;
@@ -201,6 +202,7 @@ public class MvcViewTests extends TestCase {
assertEquals("1", bindBean.getListProperty().get(0));
assertEquals("2", bindBean.getListProperty().get(1));
assertEquals("3", bindBean.getListProperty().get(2));
assertFalse(bindBean.validationMethodInvoked);
}
public void testResumeEventBindingErrors() throws Exception {
@@ -307,6 +309,54 @@ public class MvcViewTests extends TestCase {
assertEquals(true, bindBean.getBooleanProperty());
}
public void testResumeEventModelBindAndValidate() throws Exception {
MockRequestContext context = new MockRequestContext();
context.putRequestParameter("_eventId", "submit");
context.putRequestParameter("stringProperty", "foo");
context.putRequestParameter("integerProperty", "5");
context.putRequestParameter("dateProperty", "2007-01-01");
BindBean bindBean = new ValidatingBindBean();
StaticExpression modelObject = new StaticExpression(bindBean);
modelObject.setExpressionString("bindBean");
context.getCurrentState().getAttributes().put("model", modelObject);
context.getFlowScope().put("bindBean", bindBean);
context.getMockExternalContext().setNativeContext(new MockServletContext());
context.getMockExternalContext().setNativeRequest(new MockHttpServletRequest());
context.getMockExternalContext().setNativeResponse(new MockHttpServletResponse());
context.getMockFlowExecutionContext().setKey(new MockFlowExecutionKey("c1v1"));
org.springframework.web.servlet.View mvcView = new MockView();
AbstractMvcView view = new MockMvcView(mvcView, context);
view.setExpressionParser(DefaultExpressionParserFactory.getExpressionParser());
view.processUserEvent();
assertTrue(view.hasFlowEvent());
assertEquals("submit", view.getFlowEvent().getId());
assertTrue(bindBean.validationMethodInvoked);
}
public void testResumeEventModelBindAndValidateDefaultValidatorFallback() throws Exception {
MockRequestContext context = new MockRequestContext();
context.putRequestParameter("_eventId", "submit");
context.putRequestParameter("stringProperty", "foo");
context.putRequestParameter("integerProperty", "5");
context.putRequestParameter("dateProperty", "2007-01-01");
BindBean bindBean = new ValidatingBindBeanFallback();
StaticExpression modelObject = new StaticExpression(bindBean);
modelObject.setExpressionString("bindBean");
context.getCurrentState().getAttributes().put("model", modelObject);
context.getFlowScope().put("bindBean", bindBean);
context.getMockExternalContext().setNativeContext(new MockServletContext());
context.getMockExternalContext().setNativeRequest(new MockHttpServletRequest());
context.getMockExternalContext().setNativeResponse(new MockHttpServletResponse());
context.getMockFlowExecutionContext().setKey(new MockFlowExecutionKey("c1v1"));
org.springframework.web.servlet.View mvcView = new MockView();
AbstractMvcView view = new MockMvcView(mvcView, context);
view.setExpressionParser(DefaultExpressionParserFactory.getExpressionParser());
view.processUserEvent();
assertTrue(view.hasFlowEvent());
assertEquals("submit", view.getFlowEvent().getId());
assertTrue(bindBean.validationMethodInvoked);
}
private class MockMvcView extends AbstractMvcView {
public MockMvcView(View view, RequestContext context) {
@@ -346,6 +396,7 @@ public class MvcViewTests extends TestCase {
private int[] primitiveArrayProperty;
private List listProperty;
private Map mapProperty;
private boolean validationMethodInvoked;
public BindBean() {
Calendar cal = Calendar.getInstance();
@@ -445,6 +496,23 @@ public class MvcViewTests extends TestCase {
}
public static class ValidatingBindBean extends BindBean {
public void validateMockState(ValidationContext context) {
super.validationMethodInvoked = true;
}
}
public static class ValidatingBindBeanFallback extends BindBean {
public void validate(ValidationContext context) {
assertEquals("submit", context.getUserEvent());
assertNull(context.getUserPrincipal());
assertEquals("foo", context.getUserValue("stringProperty"));
super.validationMethodInvoked = true;
}
}
public static class NestedBean {
private String name;