Support validation-hints on transitions
Issue SWF-1626
This commit is contained in:
@@ -812,6 +812,11 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
|
||||
if (StringUtils.hasText(transition.getValidate())) {
|
||||
attributes.put("validate", fromStringTo(Boolean.class).execute(transition.getValidate()));
|
||||
}
|
||||
if (StringUtils.hasText(transition.getValidationHints())) {
|
||||
attributes.put("validationHints",
|
||||
getLocalContext().getExpressionParser().parseExpression(transition.getValidationHints(),
|
||||
new FluentParserContext().evaluate(RequestContext.class)));
|
||||
}
|
||||
if (StringUtils.hasText(transition.getHistory())) {
|
||||
attributes.put("history", fromStringTo(History.class).execute(transition.getHistory().toUpperCase()));
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.springframework.util.StringUtils;
|
||||
* A path from this state to another state triggered by an event. Transitions may execute one or more actions. All
|
||||
* transition actions must execute successfully for the transition itself to complete. If no transition target is
|
||||
* specified, the transition acts as a simple event handler and does not change the state of the flow.
|
||||
*
|
||||
*
|
||||
* @author Scott Andrews
|
||||
*/
|
||||
public class TransitionModel extends AbstractModel {
|
||||
@@ -41,6 +41,8 @@ public class TransitionModel extends AbstractModel {
|
||||
|
||||
private String validate;
|
||||
|
||||
private String validationHints;
|
||||
|
||||
private String history;
|
||||
|
||||
private LinkedList<AttributeModel> attributes;
|
||||
@@ -70,6 +72,7 @@ public class TransitionModel extends AbstractModel {
|
||||
setTo(merge(getTo(), transition.getTo()));
|
||||
setBind(merge(getBind(), transition.getBind()));
|
||||
setValidate(merge(getValidate(), transition.getValidate()));
|
||||
setValidationHints(merge(getValidationHints(), transition.getValidationHints()));
|
||||
setHistory(merge(getHistory(), transition.getHistory()));
|
||||
setAttributes(merge(getAttributes(), transition.getAttributes()));
|
||||
setSecured((SecuredModel) merge(getSecured(), transition.getSecured()));
|
||||
@@ -83,6 +86,7 @@ public class TransitionModel extends AbstractModel {
|
||||
copy.setTo(to);
|
||||
copy.setBind(bind);
|
||||
copy.setValidate(validate);
|
||||
copy.setValidationHints(validationHints);
|
||||
copy.setHistory(history);
|
||||
copy.setAttributes(copyList(attributes));
|
||||
copy.setSecured((SecuredModel) copy(secured));
|
||||
@@ -180,6 +184,25 @@ public class TransitionModel extends AbstractModel {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the validation hints
|
||||
*/
|
||||
public String getValidationHints() {
|
||||
return this.validationHints;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param validationHints the validation hints expression to set
|
||||
*/
|
||||
public void setValidationHints(String validationHints) {
|
||||
if (StringUtils.hasText(validationHints)) {
|
||||
this.validationHints = validationHints;
|
||||
} else {
|
||||
this.validationHints = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the history
|
||||
*/
|
||||
|
||||
@@ -435,6 +435,7 @@ public class XmlFlowModelBuilder implements FlowModelBuilder {
|
||||
transition.setOnException(element.getAttribute("on-exception"));
|
||||
transition.setBind(element.getAttribute("bind"));
|
||||
transition.setValidate(element.getAttribute("validate"));
|
||||
transition.setValidationHints(element.getAttribute("validation-hints"));
|
||||
transition.setHistory(element.getAttribute("history"));
|
||||
transition.setAttributes(parseAttributes(element));
|
||||
transition.setSecured(parseSecured(element));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-2012 the original author or authors.
|
||||
* Copyright 2004-2014 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.
|
||||
@@ -232,7 +232,7 @@ public abstract class AbstractMvcView implements View {
|
||||
addErrorMessages(mappingResults);
|
||||
}
|
||||
if (shouldValidate(model, transition)) {
|
||||
validate(model);
|
||||
validate(model, transition);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -593,31 +593,37 @@ public abstract class AbstractMvcView implements View {
|
||||
return (Expression) requestContext.getCurrentState().getAttributes().get("model");
|
||||
}
|
||||
|
||||
private Object[] getValidationHints(Object model) {
|
||||
Expression expr = (Expression) requestContext.getCurrentState().getAttributes().get("validationHints");
|
||||
private Object[] getValidationHints(Object model, TransitionDefinition transition) {
|
||||
Expression expr = null;
|
||||
if (transition != null) {
|
||||
expr = (Expression) transition.getAttributes().get("validationHints");
|
||||
}
|
||||
if (expr == null) {
|
||||
expr = (Expression) requestContext.getCurrentState().getAttributes().get("validationHints");
|
||||
}
|
||||
if (expr == null) {
|
||||
return null;
|
||||
}
|
||||
String flowId = requestContext.getActiveFlow().getId();
|
||||
String stateId = requestContext.getCurrentState().getId();
|
||||
if (expr != null) {
|
||||
try {
|
||||
Object hintsValue = expr.getValue(requestContext);
|
||||
if (hintsValue instanceof String) {
|
||||
String[] hints = StringUtils.commaDelimitedListToStringArray((String) hintsValue);
|
||||
return validationHintResolver.resolveValidationHints(model, flowId, stateId, hints);
|
||||
}
|
||||
else if (hintsValue instanceof Object[]) {
|
||||
return (Object[]) hintsValue;
|
||||
}
|
||||
else {
|
||||
throw new FlowExecutionException(flowId, stateId,
|
||||
"Failed to resolve validation hints [" + hintsValue + "]");
|
||||
}
|
||||
try {
|
||||
Object hintsValue = expr.getValue(requestContext);
|
||||
if (hintsValue instanceof String) {
|
||||
String[] hints = StringUtils.commaDelimitedListToStringArray((String) hintsValue);
|
||||
return validationHintResolver.resolveValidationHints(model, flowId, stateId, hints);
|
||||
}
|
||||
catch (EvaluationException e) {
|
||||
else if (hintsValue instanceof Object[]) {
|
||||
return (Object[]) hintsValue;
|
||||
}
|
||||
else {
|
||||
throw new FlowExecutionException(flowId, stateId,
|
||||
"Failed to resolve validation hints expression [" + expr + "]", e);
|
||||
"Failed to resolve validation hints [" + hintsValue + "]");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
catch (EvaluationException e) {
|
||||
throw new FlowExecutionException(flowId, stateId,
|
||||
"Failed to resolve validation hints expression [" + expr + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Object getEmptyValue(Class<?> fieldType) {
|
||||
@@ -665,14 +671,14 @@ public abstract class AbstractMvcView implements View {
|
||||
}
|
||||
}
|
||||
|
||||
private void validate(Object model) {
|
||||
private void validate(Object model, TransitionDefinition transition) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Validating model");
|
||||
}
|
||||
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, getModelExpression()
|
||||
.getExpressionString(), expressionParser, messageCodesResolver, mappingResults);
|
||||
helper.setValidator(this.validator);
|
||||
helper.setValidationHints(getValidationHints(model));
|
||||
helper.setValidationHints(getValidationHints(model, transition));
|
||||
helper.validate();
|
||||
}
|
||||
|
||||
|
||||
@@ -1485,6 +1485,21 @@ Indicates whether model validation should occur before this transition executes.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="validation-hints" type="expression">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
A comma-separated list of validation hints such as validation groups against a JSR-303 provider.
|
||||
Each hint is used to find an inner Class either in the Class of the model or its parent classes.
|
||||
For example, given org.example.MyModel with inner type MyGroup, the hint "MyGroup" can be used.
|
||||
The hint "default" is reserved as the default validation group, i.e. "javax.validation.groups.Default".
|
||||
A hint can also be a fully qualified class name.
|
||||
The validation hints string is evaluated as an expression before hints are resolved.
|
||||
The result of the expression can be a String or an Object[] with Class instances.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="history">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.springframework.webflow.engine.builder.model.FlowModelFlowBuilder;
|
||||
import org.springframework.webflow.engine.impl.FlowExecutionImplFactory;
|
||||
import org.springframework.webflow.engine.model.FlowModel;
|
||||
import org.springframework.webflow.engine.model.SecuredModel;
|
||||
import org.springframework.webflow.engine.model.TransitionModel;
|
||||
import org.springframework.webflow.engine.model.ViewStateModel;
|
||||
import org.springframework.webflow.engine.model.builder.DefaultFlowModelHolder;
|
||||
import org.springframework.webflow.engine.model.builder.FlowModelBuilder;
|
||||
@@ -327,7 +328,7 @@ public class XmlFlowModelBuilderTests extends TestCase {
|
||||
assertTrue(((TestBeanValidator) action.getValidator()).getInvoked());
|
||||
}
|
||||
|
||||
public void testParseFlowValidationHints() {
|
||||
public void testParsedFlowValidationHints() {
|
||||
ClassPathResource res = new ClassPathResource("flow-validation-hints.xml", getClass());
|
||||
XmlFlowModelBuilder builder = new XmlFlowModelBuilder(res);
|
||||
DefaultFlowModelHolder holder = new DefaultFlowModelHolder(builder);
|
||||
@@ -336,6 +337,9 @@ public class XmlFlowModelBuilderTests extends TestCase {
|
||||
ViewStateModel state = (ViewStateModel) model.getStateById("state1");
|
||||
assertEquals("foo,bar", state.getValidationHints());
|
||||
|
||||
TransitionModel transition = state.getTransitions().get(0);
|
||||
assertEquals("baz", transition.getValidationHints());
|
||||
|
||||
state = (ViewStateModel) model.getStateById("state2");
|
||||
assertNull(state.getValidationHints());
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.4.xsd">
|
||||
|
||||
<view-state id="state1" validation-hints="foo,bar" />
|
||||
<view-state id="state1" validation-hints="foo,bar">
|
||||
<transition on="submit" to="state2" validation-hints="baz" />
|
||||
</view-state>
|
||||
|
||||
<view-state id="state2" />
|
||||
|
||||
|
||||
Reference in New Issue
Block a user