separated out the concept of a template

normalized EL and OGNL to respect this concept
This commit is contained in:
Keith Donald
2008-03-06 22:03:23 +00:00
parent 5730d46905
commit fd7f35113f
10 changed files with 76 additions and 62 deletions

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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());
}

View File

@@ -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[

View File

@@ -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();

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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");

View File

@@ -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>

View File

@@ -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>