separated out the concept of a template
normalized EL and OGNL to respect this concept
This commit is contained in:
@@ -75,7 +75,7 @@ class TextToTargetStateResolver extends AbstractConverter {
|
||||
if (targetStateId.startsWith(BEAN_PREFIX)) {
|
||||
return flowBuilderContext.getBeanFactory().getBean(targetStateId.substring(BEAN_PREFIX.length()));
|
||||
} else {
|
||||
Expression expression = parser.parseExpression(targetStateId, new ParserContextImpl().eval(
|
||||
Expression expression = parser.parseExpression(targetStateId, new ParserContextImpl().template().eval(
|
||||
RequestContext.class).expect(String.class));
|
||||
return new DefaultTargetStateResolver(expression);
|
||||
}
|
||||
|
||||
@@ -21,14 +21,12 @@ import org.springframework.binding.convert.support.AbstractConverter;
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.binding.expression.ExpressionVariable;
|
||||
import org.springframework.binding.expression.ognl.OgnlExpressionParser;
|
||||
import org.springframework.binding.expression.support.ParserContextImpl;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.webflow.engine.TransitionCriteria;
|
||||
import org.springframework.webflow.engine.WildcardTransitionCriteria;
|
||||
import org.springframework.webflow.engine.builder.FlowBuilderContext;
|
||||
import org.springframework.webflow.engine.support.BooleanExpressionTransitionCriteria;
|
||||
import org.springframework.webflow.engine.support.EventIdTransitionCriteria;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
|
||||
/**
|
||||
@@ -89,30 +87,10 @@ class TextToTransitionCriteria extends AbstractConverter {
|
||||
return flowBuilderContext.getBeanFactory().getBean(encodedCriteria.substring(BEAN_PREFIX.length()),
|
||||
TransitionCriteria.class);
|
||||
} else {
|
||||
if (parser instanceof OgnlExpressionParser) {
|
||||
// 1.0 compatability
|
||||
OgnlExpressionParser ognl = (OgnlExpressionParser) parser;
|
||||
if (ognl.isTemplateExpression(encodedCriteria)) {
|
||||
return createBooleanExpressionTransitionCriteria(encodedCriteria, parser);
|
||||
} else {
|
||||
return createEventIdTransitionCriteria(encodedCriteria);
|
||||
}
|
||||
} else {
|
||||
return createBooleanExpressionTransitionCriteria(encodedCriteria, parser);
|
||||
}
|
||||
return createBooleanExpressionTransitionCriteria(encodedCriteria, parser);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook method subclasses can override to return a specialized eventId matching transition criteria implementation.
|
||||
* @param eventId the event id to match
|
||||
* @return the transition criteria object
|
||||
* @throws ConversionException when something goes wrong
|
||||
*/
|
||||
protected TransitionCriteria createEventIdTransitionCriteria(String eventId) throws ConversionException {
|
||||
return new EventIdTransitionCriteria(eventId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook method subclasses can override to return a specialized expression evaluating transition criteria
|
||||
* implementation.
|
||||
@@ -123,7 +101,7 @@ class TextToTransitionCriteria extends AbstractConverter {
|
||||
*/
|
||||
protected TransitionCriteria createBooleanExpressionTransitionCriteria(String encodedCriteria,
|
||||
ExpressionParser parser) throws ConversionException {
|
||||
Expression expression = parser.parseExpression(encodedCriteria, new ParserContextImpl().eval(
|
||||
Expression expression = parser.parseExpression(encodedCriteria, new ParserContextImpl().template().eval(
|
||||
RequestContext.class).variable(new ExpressionVariable("result", "lastEvent.id")));
|
||||
return new BooleanExpressionTransitionCriteria(expression);
|
||||
}
|
||||
|
||||
@@ -543,7 +543,7 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde
|
||||
private Expression parseSubflowExpression(Element element) {
|
||||
String subflow = element.getAttribute("subflow");
|
||||
Expression subflowId = getExpressionParser().parseExpression(subflow,
|
||||
new ParserContextImpl().eval(RequestContext.class).expect(String.class));
|
||||
new ParserContextImpl().template().eval(RequestContext.class).expect(String.class));
|
||||
return new SubflowExpression(subflowId, getLocalContext().getFlowDefinitionLocator());
|
||||
}
|
||||
|
||||
@@ -595,26 +595,26 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde
|
||||
} else {
|
||||
encodedView = getLocalContext().getViewFactoryCreator().createViewIdByConvention(parseId(element));
|
||||
Expression viewName = getExpressionParser().parseExpression(encodedView,
|
||||
new ParserContextImpl().eval(RequestContext.class).expect(String.class));
|
||||
new ParserContextImpl().template().eval(RequestContext.class).expect(String.class));
|
||||
return getLocalContext().getViewFactoryCreator().createViewFactory(viewName,
|
||||
getLocalContext().getResourceLoader());
|
||||
}
|
||||
} else if (encodedView.startsWith("externalRedirect:")) {
|
||||
String encodedUrl = encodedView.substring("externalRedirect:".length());
|
||||
Expression externalUrl = getExpressionParser().parseExpression(encodedUrl,
|
||||
new ParserContextImpl().eval(RequestContext.class).expect(String.class));
|
||||
new ParserContextImpl().template().eval(RequestContext.class).expect(String.class));
|
||||
return new ActionExecutingViewFactory(new ExternalRedirectAction(externalUrl));
|
||||
} else if (encodedView.startsWith("flowRedirect:")) {
|
||||
String flowRedirect = encodedView.substring("flowRedirect:".length());
|
||||
Expression expression = getExpressionParser().parseExpression(flowRedirect,
|
||||
new ParserContextImpl().eval(RequestContext.class).expect(String.class));
|
||||
new ParserContextImpl().template().eval(RequestContext.class).expect(String.class));
|
||||
return new ActionExecutingViewFactory(new FlowDefinitionRedirectAction(expression));
|
||||
} else if (encodedView.startsWith("bean:")) {
|
||||
return (ViewFactory) getLocalContext().getBeanFactory().getBean(encodedView.substring("bean:".length()),
|
||||
ViewFactory.class);
|
||||
} else {
|
||||
Expression viewName = getExpressionParser().parseExpression(encodedView,
|
||||
new ParserContextImpl().eval(RequestContext.class).expect(String.class));
|
||||
new ParserContextImpl().template().eval(RequestContext.class).expect(String.class));
|
||||
return getLocalContext().getViewFactoryCreator().createViewFactory(viewName,
|
||||
getLocalContext().getResourceLoader());
|
||||
}
|
||||
|
||||
@@ -41,11 +41,40 @@ The unique identifier of this state; must be unique to this flow.
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Reusable types -->
|
||||
<xsd:simpleType name="type">
|
||||
<xsd:restriction base="xsd:string"/>
|
||||
</xsd:simpleType>
|
||||
|
||||
<xsd:simpleType name="expression">
|
||||
<xsd:restriction base="xsd:string"/>
|
||||
</xsd:simpleType>
|
||||
|
||||
<xsd:simpleType name="type">
|
||||
<xsd:simpleType name="template">
|
||||
<xsd:restriction base="xsd:string"/>
|
||||
</xsd:simpleType>
|
||||
|
||||
<xsd:simpleType name="viewFactory">
|
||||
<xsd:restriction base="template"/>
|
||||
</xsd:simpleType>
|
||||
|
||||
<xsd:simpleType name="transitionCriteria">
|
||||
<xsd:restriction base="template"/>
|
||||
</xsd:simpleType>
|
||||
|
||||
<xsd:simpleType name="targetStateResolver">
|
||||
<xsd:restriction base="template"/>
|
||||
</xsd:simpleType>
|
||||
|
||||
<xsd:simpleType name="flowId">
|
||||
<xsd:restriction base="template"/>
|
||||
</xsd:simpleType>
|
||||
|
||||
<xsd:simpleType name="beanName">
|
||||
<xsd:restriction base="xsd:string"/>
|
||||
</xsd:simpleType>
|
||||
|
||||
<xsd:simpleType name="resource">
|
||||
<xsd:restriction base="xsd:string"/>
|
||||
</xsd:simpleType>
|
||||
|
||||
@@ -656,11 +685,12 @@ If specified and the result is not compatible with the expected type, a type con
|
||||
|
||||
<xsd:element name="render">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="fragments" type="expression" use="required">
|
||||
<xsd:attribute name="fragments" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The fragments of the next view to render. Multiple fragments may be specified by using the comma delimiter.
|
||||
Each fragment is a template expression.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
@@ -723,7 +753,7 @@ Secures this transition.
|
||||
</xsd:element>
|
||||
<xsd:group ref="actionTypes" minOccurs="0" maxOccurs="unbounded" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="on" type="expression">
|
||||
<xsd:attribute name="on" type="transitionCriteria">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
@@ -758,7 +788,7 @@ Superclasses of the configured exception class match by default. Use this attri
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="to" type="expression" use="optional">
|
||||
<xsd:attribute name="to" type="targetStateResolver" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
@@ -854,7 +884,7 @@ Handles exceptions that occur within this state.
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="view" type="expression">
|
||||
<xsd:attribute name="view" type="viewFactory">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
@@ -1013,7 +1043,7 @@ For example:
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="then" type="expression" use="required">
|
||||
<xsd:attribute name="then" type="targetStateResolver" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
@@ -1022,7 +1052,7 @@ The state to transition to if the boolean expression evaluates to true.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="else" type="expression">
|
||||
<xsd:attribute name="else" type="targetStateResolver">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
@@ -1114,7 +1144,7 @@ Handles exceptions that occur within this state.
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="subflow" type="expression" use="required">
|
||||
<xsd:attribute name="subflow" type="flowId" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
@@ -1123,7 +1153,7 @@ The subflow to start.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="subflow-attribute-mapper" type="xsd:string">
|
||||
<xsd:attribute name="subflow-attribute-mapper" type="beanName">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
@@ -1189,7 +1219,7 @@ Handles exceptions that occur in this state.
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="view" type="xsd:string">
|
||||
<xsd:attribute name="view" type="viewFactory">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
@@ -1264,7 +1294,7 @@ All transition actions must execute successfully for the transition itself to ex
|
||||
|
||||
<xsd:element name="exception-handler">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="bean" type="xsd:string" use="required">
|
||||
<xsd:attribute name="bean" type="beanName" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
@@ -1278,7 +1308,7 @@ The bean id of the custom exception handler.
|
||||
|
||||
<xsd:element name="bean-import">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="resource" type="xsd:string" use="required">
|
||||
<xsd:attribute name="resource" type="resource" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
|
||||
@@ -33,8 +33,8 @@ public class AttributeMapperActionTests extends TestCase {
|
||||
|
||||
public void testMapping() throws Exception {
|
||||
DefaultAttributeMapper mapper = new DefaultAttributeMapper();
|
||||
mapper.addMapping(mappingBuilder.source("${externalContext.requestParameterMap.foo}")
|
||||
.target("${flowScope.bar}").value());
|
||||
mapper.addMapping(mappingBuilder.source("externalContext.requestParameterMap.foo").target("flowScope.bar")
|
||||
.value());
|
||||
AttributeMapperAction action = new AttributeMapperAction(mapper);
|
||||
|
||||
MockRequestContext context = new MockRequestContext();
|
||||
|
||||
@@ -50,6 +50,11 @@ public class FlowBuilderServicesBeanDefinitionParserTests extends TestCase {
|
||||
public ViewFactory createViewFactory(Expression viewId, ResourceLoader viewResourceLoader) {
|
||||
throw new UnsupportedOperationException("Auto-generated method stub");
|
||||
}
|
||||
|
||||
public String createViewIdByConvention(String viewStateId) {
|
||||
return viewStateId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class TestConversionService implements ConversionService {
|
||||
|
||||
@@ -67,7 +67,7 @@ public class EndStateTests extends TestCase {
|
||||
EndState state = new EndState(flow, "end");
|
||||
DefaultAttributeMapper mapper = new DefaultAttributeMapper();
|
||||
MappingBuilder builder = new MappingBuilder(DefaultExpressionParserFactory.getExpressionParser());
|
||||
Mapping mapping = builder.source("${flowScope.x}").target("${y}").value();
|
||||
Mapping mapping = builder.source("flowScope.x").target("y").value();
|
||||
mapper.addMapping(mapping);
|
||||
state.setOutputMapper(mapper);
|
||||
MockRequestControlContext context = new MockRequestControlContext(flow);
|
||||
|
||||
@@ -196,7 +196,7 @@ public class FlowTests extends TestCase {
|
||||
public void testStartWithMapper() {
|
||||
DefaultAttributeMapper attributeMapper = new DefaultAttributeMapper();
|
||||
MappingBuilder mapping = new MappingBuilder(DefaultExpressionParserFactory.getExpressionParser());
|
||||
attributeMapper.addMapping(mapping.source("${attr}").target("${flowScope.attr}").value());
|
||||
attributeMapper.addMapping(mapping.source("attr").target("flowScope.attr").value());
|
||||
flow.setInputMapper(attributeMapper);
|
||||
MockRequestControlContext context = new MockRequestControlContext(flow);
|
||||
LocalAttributeMap sessionInput = new LocalAttributeMap();
|
||||
@@ -208,7 +208,7 @@ public class FlowTests extends TestCase {
|
||||
public void testStartWithMapperButNoInput() {
|
||||
DefaultAttributeMapper attributeMapper = new DefaultAttributeMapper();
|
||||
MappingBuilder mapping = new MappingBuilder(DefaultExpressionParserFactory.getExpressionParser());
|
||||
attributeMapper.addMapping(mapping.source("${attr}").target("${flowScope.attr}").value());
|
||||
attributeMapper.addMapping(mapping.source("attr").target("flowScope.attr").value());
|
||||
flow.setInputMapper(attributeMapper);
|
||||
MockRequestControlContext context = new MockRequestControlContext(flow);
|
||||
LocalAttributeMap sessionInput = new LocalAttributeMap();
|
||||
@@ -294,7 +294,7 @@ public class FlowTests extends TestCase {
|
||||
public void testEndWithOutputMapper() {
|
||||
DefaultAttributeMapper attributeMapper = new DefaultAttributeMapper();
|
||||
MappingBuilder mapping = new MappingBuilder(DefaultExpressionParserFactory.getExpressionParser());
|
||||
attributeMapper.addMapping(mapping.source("${flowScope.attr}").target("${attr}").value());
|
||||
attributeMapper.addMapping(mapping.source("flowScope.attr").target("attr").value());
|
||||
flow.setOutputMapper(attributeMapper);
|
||||
MockRequestControlContext context = new MockRequestControlContext(flow);
|
||||
context.getFlowScope().put("attr", "foo");
|
||||
|
||||
@@ -2,21 +2,22 @@
|
||||
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.0.xsd">
|
||||
|
||||
<input name="${foo}" value="${flowScope.foo}"/>
|
||||
<input name="${foo}" value="${flowScope.bar}"/>
|
||||
<input name="${number}" value="${flowScope.baz}" type="integer"/>
|
||||
<input name="${required}" value="${flowScope.boop}" required="true"/>
|
||||
<input name="foo" value="flowScope.foo"/>
|
||||
<input name="foo" value="flowScope.bar"/>
|
||||
<input name="number" type="integer" value="flowScope.baz"/>
|
||||
<input name="required" value="flowScope.boop" required="true"/>
|
||||
|
||||
<end-state id="end">
|
||||
<output name="${foo}" value="${flowScope.foo}" />
|
||||
<output name="foo" value="flowScope.foo" />
|
||||
</end-state>
|
||||
|
||||
<end-state id="notReached">
|
||||
<output name="${notReached}" value="${flowScope.foo}"/>
|
||||
<output name="notReached" value="flowScope.foo"/>
|
||||
</end-state>
|
||||
|
||||
<output name="${differentName}" value="${flowScope.bar}"/>
|
||||
<output name="${number}" value="${flowScope.baz}" type="integer"/>
|
||||
<output name="${required}" value="${flowScope.baz}" type="integer" required="true"/>
|
||||
<output name="${literal}" value="a literal"/>
|
||||
<output name="differentName" value="flowScope.bar"/>
|
||||
<output name="number" type="integer" value="flowScope.baz" />
|
||||
<output name="required" type="integer" value="flowScope.baz" required="true"/>
|
||||
<output name="literal" value="'a literal'"/>
|
||||
|
||||
</flow>
|
||||
@@ -5,23 +5,23 @@
|
||||
|
||||
<view-state id="enterCriteria" view="searchCriteria">
|
||||
<on-render>
|
||||
<evaluate expression="#{formAction.setupForm}" />
|
||||
<evaluate expression="formAction.setupForm" />
|
||||
</on-render>
|
||||
<transition on="search" to="displayResults">
|
||||
<evaluate expression="#{formAction.bindAndValidate}" />
|
||||
<evaluate expression="formAction.bindAndValidate" />
|
||||
</transition>
|
||||
</view-state>
|
||||
|
||||
<view-state id="displayResults" view="searchResults">
|
||||
<on-render>
|
||||
<evaluate expression="#{phonebook.search(searchCriteria)}" result="#{flowScope.results}" />
|
||||
<evaluate expression="phonebook.search(searchCriteria)" result="flowScope.results" />
|
||||
</on-render>
|
||||
<transition on="newSearch" to="enterCriteria"/>
|
||||
<transition on="select" to="browseDetails"/>
|
||||
</view-state>
|
||||
|
||||
<subflow-state id="browseDetails" subflow="detail-flow">
|
||||
<input name="#{id}" value="#{requestParameters.id}" type="long" required="true"/>
|
||||
<input name="id" value="requestParameters.id" type="long" required="true"/>
|
||||
<transition on="finish" to="displayResults"/>
|
||||
</subflow-state>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user