diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilder.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilder.java index 7f8b53a2..97d1577f 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilder.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilder.java @@ -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())); } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/TransitionModel.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/TransitionModel.java index 67de2385..93be256a 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/TransitionModel.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/TransitionModel.java @@ -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 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 */ diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilder.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilder.java index 663d7392..754b986f 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilder.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilder.java @@ -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)); diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java index 11fe9445..7ab2b01d 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java @@ -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(); } diff --git a/spring-webflow/src/main/resources/org/springframework/webflow/engine/model/builder/xml/spring-webflow-2.4.xsd b/spring-webflow/src/main/resources/org/springframework/webflow/engine/model/builder/xml/spring-webflow-2.4.xsd index 5c68c46a..6b8ecdc3 100644 --- a/spring-webflow/src/main/resources/org/springframework/webflow/engine/model/builder/xml/spring-webflow-2.4.xsd +++ b/spring-webflow/src/main/resources/org/springframework/webflow/engine/model/builder/xml/spring-webflow-2.4.xsd @@ -1485,6 +1485,21 @@ Indicates whether model validation should occur before this transition executes. + + + + + + + diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilderTests.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilderTests.java index c424e3a2..754dd83e 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilderTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilderTests.java @@ -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()); } diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/flow-validation-hints.xml b/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/flow-validation-hints.xml index 002f9544..8fa112da 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/flow-validation-hints.xml +++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/flow-validation-hints.xml @@ -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"> - + + +