global transition respecting bind and validate flags
This commit is contained in:
@@ -73,8 +73,8 @@ public class ProgressiveUICommand extends UICommand {
|
||||
RequestContext context = RequestContextHolder.getRequestContext();
|
||||
if (context != null && getActionExpression().isLiteralText()
|
||||
&& context.getCurrentState() instanceof TransitionableState) {
|
||||
TransitionDefinition transition = ((TransitionableState) context.getCurrentState())
|
||||
.getTransition(getActionExpression().getExpressionString());
|
||||
TransitionDefinition transition = context
|
||||
.getMatchingTransition(getActionExpression().getExpressionString());
|
||||
if (transition != null && transition.getAttributes().contains("bind")) {
|
||||
return Boolean.FALSE.equals(transition.getAttributes().getBoolean("bind"));
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.webflow.definition.TransitionDefinition;
|
||||
import org.springframework.webflow.definition.TransitionableStateDefinition;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.RequestContextHolder;
|
||||
import org.springframework.webflow.execution.View;
|
||||
@@ -147,11 +146,10 @@ public class FlowActionListener implements ActionListener {
|
||||
if (model == null) {
|
||||
return false;
|
||||
}
|
||||
TransitionableStateDefinition currentState = (TransitionableStateDefinition) requestContext.getCurrentState();
|
||||
TransitionDefinition transition = currentState.getTransition(eventId);
|
||||
TransitionDefinition transition = requestContext.getMatchingTransition(eventId);
|
||||
if (transition != null) {
|
||||
if (transition.getAttributes().contains("bind")) {
|
||||
return transition.getAttributes().getBoolean("bind").booleanValue();
|
||||
if (transition.getAttributes().contains("validate")) {
|
||||
return transition.getAttributes().getBoolean("validate").booleanValue();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -20,8 +20,8 @@ import javax.faces.lifecycle.Lifecycle;
|
||||
import org.springframework.binding.convert.ConversionService;
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.webflow.engine.builder.BinderConfiguration;
|
||||
import org.springframework.webflow.engine.builder.ViewFactoryCreator;
|
||||
import org.springframework.webflow.engine.model.BinderModel;
|
||||
import org.springframework.webflow.execution.ViewFactory;
|
||||
|
||||
/**
|
||||
@@ -36,7 +36,7 @@ public class JsfViewFactoryCreator implements ViewFactoryCreator {
|
||||
private Lifecycle lifecycle;
|
||||
|
||||
public ViewFactory createViewFactory(Expression viewIdExpression, ExpressionParser expressionParser,
|
||||
ConversionService conversionService, BinderModel binderModel) {
|
||||
ConversionService conversionService, BinderConfiguration binderConfiguration) {
|
||||
return new JsfViewFactory(viewIdExpression, getLifecycle());
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,9 @@ import org.springframework.faces.model.converter.FacesConversionService;
|
||||
import org.springframework.faces.webflow.JSFMockHelper;
|
||||
import org.springframework.faces.webflow.JsfManagedBeanAwareELExpressionParser;
|
||||
import org.springframework.faces.webflow.JsfViewFactoryCreator;
|
||||
import org.springframework.webflow.engine.builder.BinderConfiguration;
|
||||
import org.springframework.webflow.engine.builder.ViewFactoryCreator;
|
||||
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
|
||||
import org.springframework.webflow.engine.model.BinderModel;
|
||||
import org.springframework.webflow.execution.ViewFactory;
|
||||
import org.springframework.webflow.expression.el.WebFlowELExpressionParser;
|
||||
|
||||
@@ -64,7 +64,7 @@ public class FacesFlowBuilderServicesBeanDefinitionParserTests extends TestCase
|
||||
public static class TestViewFactoryCreator implements ViewFactoryCreator {
|
||||
|
||||
public ViewFactory createViewFactory(Expression viewIdExpression, ExpressionParser expressionParser,
|
||||
ConversionService conversionService, BinderModel binderModel) {
|
||||
ConversionService conversionService, BinderConfiguration binderConfiguration) {
|
||||
throw new UnsupportedOperationException("Auto-generated method stub");
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.springframework.webflow.core.collection.LocalAttributeMap;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.definition.FlowDefinition;
|
||||
import org.springframework.webflow.definition.StateDefinition;
|
||||
import org.springframework.webflow.definition.TransitionDefinition;
|
||||
import org.springframework.webflow.execution.FlowExecutionException;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
|
||||
@@ -466,6 +467,21 @@ public class Flow extends AnnotatedObject implements FlowDefinition {
|
||||
return globalTransitionSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the transition that matches the event with the provided id.
|
||||
* @param eventId the event id
|
||||
* @return the transition that matches, or null if no match is found.
|
||||
*/
|
||||
public TransitionDefinition getGlobalTransition(String eventId) {
|
||||
for (Iterator it = globalTransitionSet.iterator(); it.hasNext();) {
|
||||
Transition transition = (Transition) it.next();
|
||||
if (transition.getId().equals(eventId)) {
|
||||
return transition;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a reference to the application context hosting application objects needed by this flow.
|
||||
* @param applicationContext the application context
|
||||
|
||||
@@ -91,8 +91,8 @@ public abstract class TransitionableState extends State implements Transitionabl
|
||||
Transition transition = getTransitionSet().getTransition(context);
|
||||
if (transition == null) {
|
||||
throw new NoMatchingTransitionException(getFlow().getId(), getId(), context.getCurrentEvent(),
|
||||
"No transition found on occurence of event '" + context.getCurrentEvent() + "' in state '" + getId()
|
||||
+ "' of flow '" + getFlow().getId() + "' -- valid transitional criteria are "
|
||||
"No transition found on occurence of event '" + context.getCurrentEvent() + "' in state '"
|
||||
+ getId() + "' of flow '" + getFlow().getId() + "' -- valid transitional criteria are "
|
||||
+ StylerUtils.style(getTransitionSet().getTransitionCriterias())
|
||||
+ " -- likely programmer error, check the set of TransitionCriteria for this state");
|
||||
}
|
||||
|
||||
@@ -66,9 +66,9 @@ import org.springframework.webflow.engine.Transition;
|
||||
import org.springframework.webflow.engine.TransitionCriteria;
|
||||
import org.springframework.webflow.engine.VariableValueFactory;
|
||||
import org.springframework.webflow.engine.ViewVariable;
|
||||
import org.springframework.webflow.engine.builder.BinderConfiguration;
|
||||
import org.springframework.webflow.engine.builder.FlowBuilderContext;
|
||||
import org.springframework.webflow.engine.builder.FlowBuilderException;
|
||||
import org.springframework.webflow.engine.builder.BinderConfiguration;
|
||||
import org.springframework.webflow.engine.builder.BinderConfiguration.Binding;
|
||||
import org.springframework.webflow.engine.builder.support.AbstractFlowBuilder;
|
||||
import org.springframework.webflow.engine.model.AbstractActionModel;
|
||||
@@ -628,8 +628,12 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
|
||||
List bindings = binderModel.getBindings();
|
||||
for (Iterator it = bindings.iterator(); it.hasNext();) {
|
||||
BindingModel bindingModel = (BindingModel) it.next();
|
||||
boolean required = ((Boolean) fromStringTo(Boolean.class).execute(bindingModel.getRequired()))
|
||||
.booleanValue();
|
||||
boolean required;
|
||||
if (StringUtils.hasText(bindingModel.getRequired())) {
|
||||
required = ((Boolean) fromStringTo(Boolean.class).execute(bindingModel.getRequired())).booleanValue();
|
||||
} else {
|
||||
required = false;
|
||||
}
|
||||
Binding binding = new Binding(bindingModel.getProperty(), bindingModel.getConverter(), required);
|
||||
binderConfiguration.addBinding(binding);
|
||||
}
|
||||
@@ -797,6 +801,9 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
|
||||
if (StringUtils.hasText(transition.getBind())) {
|
||||
attributes.put("bind", fromStringTo(Boolean.class).execute(transition.getBind()));
|
||||
}
|
||||
if (StringUtils.hasText(transition.getValidate())) {
|
||||
attributes.put("validate", fromStringTo(Boolean.class).execute(transition.getValidate()));
|
||||
}
|
||||
if (StringUtils.hasText(transition.getHistory())) {
|
||||
attributes.put("history", fromStringTo(History.class).execute(transition.getHistory()));
|
||||
}
|
||||
|
||||
@@ -37,10 +37,12 @@ import org.springframework.webflow.core.collection.CollectionUtils;
|
||||
import org.springframework.webflow.core.collection.LocalAttributeMap;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.definition.FlowDefinition;
|
||||
import org.springframework.webflow.definition.TransitionDefinition;
|
||||
import org.springframework.webflow.engine.Flow;
|
||||
import org.springframework.webflow.engine.RequestControlContext;
|
||||
import org.springframework.webflow.engine.State;
|
||||
import org.springframework.webflow.engine.Transition;
|
||||
import org.springframework.webflow.engine.TransitionableState;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
import org.springframework.webflow.execution.FlowExecution;
|
||||
import org.springframework.webflow.execution.FlowExecutionException;
|
||||
@@ -321,8 +323,7 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
|
||||
* Create a flow execution control context.
|
||||
* @param externalContext the external context triggering this request
|
||||
*/
|
||||
protected RequestControlContext createRequestContext(ExternalContext externalContext,
|
||||
MessageContext messageContext) {
|
||||
protected RequestControlContext createRequestContext(ExternalContext externalContext, MessageContext messageContext) {
|
||||
return new RequestControlContextImpl(this, externalContext, messageContext);
|
||||
}
|
||||
|
||||
@@ -416,6 +417,16 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
|
||||
keyFactory.removeAllFlowExecutionSnapshots(this);
|
||||
}
|
||||
|
||||
TransitionDefinition getMatchingTransition(String eventId) {
|
||||
FlowSessionImpl session = getActiveSessionInternal();
|
||||
TransitionableState currentState = (TransitionableState) session.getState();
|
||||
TransitionDefinition transition = currentState.getTransition(eventId);
|
||||
if (transition == null) {
|
||||
transition = session.getFlow().getGlobalTransition(eventId);
|
||||
}
|
||||
return transition;
|
||||
}
|
||||
|
||||
// package private setters for restoring transient state used by FlowExecutionImplServicesConfigurer
|
||||
|
||||
FlowExecutionListener[] getListeners() {
|
||||
|
||||
@@ -105,6 +105,10 @@ class RequestControlContextImpl implements RequestControlContext {
|
||||
return flowExecution.getActiveSession().getState();
|
||||
}
|
||||
|
||||
public TransitionDefinition getMatchingTransition(String eventId) throws IllegalStateException {
|
||||
return flowExecution.getMatchingTransition(eventId);
|
||||
}
|
||||
|
||||
public MutableAttributeMap getRequestScope() {
|
||||
return requestScope;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@ public class TransitionModel extends AbstractModel {
|
||||
|
||||
private String bind;
|
||||
|
||||
private String validate;
|
||||
|
||||
private String history;
|
||||
|
||||
private LinkedList attributes;
|
||||
@@ -66,6 +68,7 @@ public class TransitionModel extends AbstractModel {
|
||||
setOnException(merge(getOnException(), transition.getOnException()));
|
||||
setTo(merge(getTo(), transition.getTo()));
|
||||
setBind(merge(getBind(), transition.getBind()));
|
||||
setBind(merge(getValidate(), transition.getValidate()));
|
||||
setHistory(merge(getHistory(), transition.getHistory()));
|
||||
setAttributes(merge(getAttributes(), transition.getAttributes()));
|
||||
setSecured((SecuredModel) merge(getSecured(), transition.getSecured()));
|
||||
@@ -144,6 +147,24 @@ public class TransitionModel extends AbstractModel {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the validate
|
||||
*/
|
||||
public String getValidate() {
|
||||
return validate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param validate the validate to set
|
||||
*/
|
||||
public void setValidate(String validate) {
|
||||
if (StringUtils.hasText(validate)) {
|
||||
this.validate = validate;
|
||||
} else {
|
||||
this.validate = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the history
|
||||
*/
|
||||
|
||||
@@ -471,6 +471,7 @@ public class XmlFlowModelBuilder implements FlowModelBuilder {
|
||||
transition.setTo(element.getAttribute("to"));
|
||||
transition.setOnException(element.getAttribute("on-exception"));
|
||||
transition.setBind(element.getAttribute("bind"));
|
||||
transition.setValidate(element.getAttribute("validate"));
|
||||
transition.setHistory(element.getAttribute("history"));
|
||||
transition.setAttributes(parseAttributes(element));
|
||||
transition.setSecured(parseSecured(element));
|
||||
|
||||
@@ -74,6 +74,15 @@ public interface RequestContext {
|
||||
*/
|
||||
public StateDefinition getCurrentState() throws IllegalStateException;
|
||||
|
||||
/**
|
||||
* Returns the transition that would execute on the occurrence of the given event.
|
||||
* @param eventId the id of the user event
|
||||
* @return the transition that would trigger, or <code>null</code> if no transition matches
|
||||
* @throws IllegalStateException if this flow execution has not been started at all, or if this execution has ended
|
||||
* and is no longer actively executing
|
||||
*/
|
||||
public TransitionDefinition getMatchingTransition(String eventId) throws IllegalStateException;
|
||||
|
||||
/**
|
||||
* Returns true if the flow is currently active and in a view state. When in a view state {@link #getViewScope()},
|
||||
* can be safely called.
|
||||
|
||||
@@ -51,7 +51,6 @@ import org.springframework.validation.Errors;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
import org.springframework.webflow.core.collection.ParameterMap;
|
||||
import org.springframework.webflow.definition.TransitionDefinition;
|
||||
import org.springframework.webflow.definition.TransitionableStateDefinition;
|
||||
import org.springframework.webflow.engine.builder.BinderConfiguration;
|
||||
import org.springframework.webflow.engine.builder.BinderConfiguration.Binding;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
@@ -186,13 +185,14 @@ public abstract class AbstractMvcView implements View {
|
||||
if (model == null) {
|
||||
return;
|
||||
}
|
||||
if (shouldBind(model)) {
|
||||
TransitionDefinition transition = requestContext.getMatchingTransition(eventId);
|
||||
if (shouldBind(model, transition)) {
|
||||
mappingResults = bind(model);
|
||||
if (hasMappingErrors(mappingResults)) {
|
||||
viewErrors = true;
|
||||
addErrorMessages(mappingResults);
|
||||
} else {
|
||||
if (shouldValidate(model)) {
|
||||
if (shouldValidate(model, transition)) {
|
||||
validate(model);
|
||||
if (requestContext.getMessageContext().hasErrorMessages()) {
|
||||
viewErrors = true;
|
||||
@@ -275,9 +275,7 @@ public abstract class AbstractMvcView implements View {
|
||||
return (Expression) requestContext.getCurrentState().getAttributes().get("model");
|
||||
}
|
||||
|
||||
private boolean shouldBind(Object model) {
|
||||
TransitionableStateDefinition currentState = (TransitionableStateDefinition) requestContext.getCurrentState();
|
||||
TransitionDefinition transition = currentState.getTransition(eventId);
|
||||
private boolean shouldBind(Object model, TransitionDefinition transition) {
|
||||
if (transition == null) {
|
||||
return true;
|
||||
}
|
||||
@@ -409,9 +407,7 @@ public abstract class AbstractMvcView implements View {
|
||||
.defaultText(errorCode + " on " + field).build();
|
||||
}
|
||||
|
||||
private boolean shouldValidate(Object model) {
|
||||
TransitionableStateDefinition currentState = (TransitionableStateDefinition) requestContext.getCurrentState();
|
||||
TransitionDefinition transition = currentState.getTransition(eventId);
|
||||
private boolean shouldValidate(Object model, TransitionDefinition transition) {
|
||||
if (transition == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.springframework.webflow.definition.StateDefinition;
|
||||
import org.springframework.webflow.definition.TransitionDefinition;
|
||||
import org.springframework.webflow.engine.Flow;
|
||||
import org.springframework.webflow.engine.Transition;
|
||||
import org.springframework.webflow.engine.TransitionableState;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
import org.springframework.webflow.execution.FlowExecutionContext;
|
||||
import org.springframework.webflow.execution.FlowSession;
|
||||
@@ -118,6 +119,15 @@ public class MockRequestContext implements RequestContext {
|
||||
return getFlowExecutionContext().getActiveSession().getState();
|
||||
}
|
||||
|
||||
public TransitionDefinition getMatchingTransition(String eventId) throws IllegalStateException {
|
||||
TransitionableState state = (TransitionableState) getFlowExecutionContext().getActiveSession().getState();
|
||||
TransitionDefinition transition = state.getTransition(eventId);
|
||||
if (transition == null) {
|
||||
transition = getRootFlow().getGlobalTransition(eventId);
|
||||
}
|
||||
return transition;
|
||||
}
|
||||
|
||||
public boolean inViewState() {
|
||||
return getFlowExecutionContext().isActive() && getCurrentState() != null && getCurrentState().isViewState();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user