SWF-466 - Add a "type" attribute to "evaluation-result" to support type conversion of the action result.

This commit is contained in:
Jeremy Grelle
2008-02-05 17:53:30 +00:00
parent 763d5addeb
commit ab526797c9
9 changed files with 117 additions and 48 deletions

View File

@@ -91,7 +91,7 @@ public class MethodInvoker {
+ StylerUtils.style(arguments) + " on bean [" + bean + "]");
}
Object returnValue = applyTypeConversion(method.invoke(bean, arguments), signature.getDesiredResultType());
Object returnValue = method.invoke(bean, arguments);
if (logger.isDebugEnabled()) {
logger.debug("Invoked method with signature [" + key + "] returned value [" + returnValue + "]");
}

View File

@@ -38,17 +38,12 @@ public class MethodSignature {
*/
private Parameters parameters;
/**
* The desired result type of the method invocation
*/
private Class desiredResultType;
/**
* Creates a method signature with no parameters.
* @param methodName the name of the method
*/
public MethodSignature(String methodName) {
this(methodName, Parameters.NONE, null);
this(methodName, Parameters.NONE);
}
/**
@@ -57,21 +52,19 @@ public class MethodSignature {
* @param parameter the method parameter
*/
public MethodSignature(String methodName, Parameter parameter) {
this(methodName, new Parameters(parameter), null);
this(methodName, new Parameters(parameter));
}
/**
* Creates a method signature with a list of parameters.
* @param methodName the name of the method
* @param parameters the method parameters
* @param desiredResultType the desired result type of the method invocation
*/
public MethodSignature(String methodName, Parameters parameters, Class desiredResultType) {
public MethodSignature(String methodName, Parameters parameters) {
Assert.notNull(methodName, "The method name is required");
Assert.notNull(parameters, "The parameters are required");
this.methodName = methodName;
this.parameters = parameters;
this.desiredResultType = desiredResultType;
}
/**
@@ -88,14 +81,6 @@ public class MethodSignature {
return parameters;
}
/**
* Returns the desired result type of the method invocation
* @return the desired result type
*/
public Class getDesiredResultType() {
return desiredResultType;
}
public boolean equals(Object obj) {
if (!(obj instanceof MethodSignature)) {
return false;

View File

@@ -56,23 +56,17 @@ public class MethodInvokerTests extends TestCase {
Parameters parameters = new Parameters();
Bean bean = new Bean();
parameters.add(new Parameter(Bean.class, new StaticExpression(bean)));
MethodSignature method = new MethodSignature("testBeanArg", parameters, null);
MethodSignature method = new MethodSignature("testBeanArg", parameters);
assertSame(bean, methodInvoker.invoke(method, new TestObject(), null));
}
public void testPrimitiveArg() {
Parameters parameters = new Parameters();
parameters.add(new Parameter(Boolean.class, new StaticExpression(Boolean.TRUE)));
MethodSignature method = new MethodSignature("testPrimitiveArg", parameters, null);
MethodSignature method = new MethodSignature("testPrimitiveArg", parameters);
assertEquals(Boolean.TRUE, methodInvoker.invoke(method, new TestObject(), null));
}
public void testResultConversion() {
Parameters parameters = Parameters.NONE;
MethodSignature method = new MethodSignature("testConvertResult", parameters, Class.class);
assertEquals(Object.class, methodInvoker.invoke(method, new TestObject(), null));
}
private static class TestObject {
public void test() {
@@ -83,10 +77,6 @@ public class MethodInvokerTests extends TestCase {
return bean;
}
public String testConvertResult() {
return "java.lang.Object";
}
public boolean testPrimitiveArg(boolean primitive) {
return primitive;
}

View File

@@ -17,6 +17,8 @@ package org.springframework.webflow.action;
import java.io.Serializable;
import org.springframework.binding.convert.ConversionService;
import org.springframework.binding.convert.support.DefaultConversionService;
import org.springframework.binding.expression.Expression;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.Assert;
@@ -45,15 +47,27 @@ public class ActionResultExposer implements Serializable {
*/
private ScopeType resultScope;
/**
* The desired type to expose the result as
*/
private Class desiredResultType;
/**
* The {@link ConversionService} to use to convert the result to the desired type
*/
private ConversionService conversionService = new DefaultConversionService();
/**
* Creates a action result exposer
* @param nameExpression the result name
* @param resultScope the result scope
* @param desiredResultType the desired result type
*/
public ActionResultExposer(Expression nameExpression, ScopeType resultScope) {
public ActionResultExposer(Expression nameExpression, ScopeType resultScope, Class desiredResultType) {
Assert.notNull(nameExpression, "The result name is required");
this.nameExpression = nameExpression;
this.resultScope = resultScope;
this.desiredResultType = desiredResultType;
}
/**
@@ -70,6 +84,13 @@ public class ActionResultExposer implements Serializable {
return resultScope;
}
/**
* Returns the desired result type to be exposed
*/
public Class getDesiredResultType() {
return desiredResultType;
}
/**
* Expose given bean method return value in given flow execution request context.
* @param result the return value
@@ -78,9 +99,9 @@ public class ActionResultExposer implements Serializable {
public void exposeResult(Object result, RequestContext context) {
if (resultScope != null) {
MutableAttributeMap scopeMap = resultScope.getScope(context);
nameExpression.setValue(scopeMap, result);
nameExpression.setValue(scopeMap, applyTypeConversion(result, desiredResultType));
} else {
nameExpression.setValue(context, result);
nameExpression.setValue(context, applyTypeConversion(result, desiredResultType));
}
}
@@ -88,4 +109,18 @@ public class ActionResultExposer implements Serializable {
return new ToStringCreator(this).append("resultName", nameExpression).append("resultScope", resultScope)
.toString();
}
/**
* Apply type conversion on the supplied value
*
* @param value the raw value to be converted
* @param targetType the target type for the conversion
* @return the converted result
*/
protected Object applyTypeConversion(Object value, Class targetType) {
if (value == null || targetType == null) {
return value;
}
return conversionService.getConversionExecutor(value.getClass(), targetType).execute(value);
}
}

View File

@@ -31,6 +31,7 @@ import org.springframework.webflow.execution.RequestContext;
* @see ResultEventFactorySelector
*
* @author Keith Donald
* @author Jeremy Grelle
*/
public class EvaluateAction extends AbstractAction {

View File

@@ -754,8 +754,7 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde
String beanId = element.getAttribute(BEAN_ATTRIBUTE);
String methodName = element.getAttribute(METHOD_ATTRIBUTE);
Parameters parameters = parseMethodParameters(element);
Class desiredResultType = parseMethodResultType(element);
MethodSignature methodSignature = new MethodSignature(methodName, parameters, desiredResultType);
MethodSignature methodSignature = new MethodSignature(methodName, parameters);
ActionResultExposer resultExposer = parseMethodResultExposer(element);
return getLocalContext().getBeanInvokingActionFactory().createBeanInvokingAction(beanId,
getLocalContext().getBeanFactory(), methodSignature, resultExposer,
@@ -784,8 +783,7 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde
return parameters;
}
private Class parseMethodResultType(Element element) {
Element resultElement = DomUtils.getChildElementByTagName(element, METHOD_RESULT_ELEMENT);
private Class parseResultType(Element resultElement) {
Class type = null;
if (resultElement != null) {
if (resultElement.hasAttribute(TYPE_ATTRIBUTE)) {
@@ -815,7 +813,7 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde
nameExpression = getExpressionParser().parseExpression(nameExpressionString,
new ParserContextImpl().eval(RequestContext.class));
}
return new ActionResultExposer(nameExpression, scope);
return new ActionResultExposer(nameExpression, scope, parseResultType(element));
}
private AnnotatedAction parseAnnotatedEvaluateAction(Element element) {

View File

@@ -1139,6 +1139,16 @@ If not specified the name attribute must be a fully resolvable expression such a
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="type" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
The desired evaluation result type. Optional. If specified and the method return value is not of the
desired result type, a type conversion will be attempted.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>

View File

@@ -23,7 +23,7 @@ public class ActionResultExposerTests extends TestCase {
Expression nameExpression = parser.parseExpression("#{foo}", new ParserContextImpl()
.eval(MutableAttributeMap.class));
ActionResultExposer exposer = new ActionResultExposer(nameExpression, ScopeType.REQUEST);
ActionResultExposer exposer = new ActionResultExposer(nameExpression, ScopeType.REQUEST, null);
RequestContext context = new MockRequestContext();
@@ -33,6 +33,24 @@ public class ActionResultExposerTests extends TestCase {
assertEquals("Value stored at key 'foo' is incorrect", valueToSet, context.getRequestScope().get("foo"));
}
public void testExposeResult_ScopeSpecifiedWithTypeConversion() {
String valueToSet = "true";
ExpressionParser parser = new WebFlowELExpressionParser(new ExpressionFactoryImpl());
Expression nameExpression = parser.parseExpression("#{foo}", new ParserContextImpl()
.eval(MutableAttributeMap.class));
ActionResultExposer exposer = new ActionResultExposer(nameExpression, ScopeType.REQUEST, Boolean.class);
RequestContext context = new MockRequestContext();
exposer.exposeResult(valueToSet, context);
assertTrue("Key 'foo' not found in request scope", context.getRequestScope().contains("foo"));
assertEquals("Value stored at key 'foo' is incorrect", Boolean.TRUE, context.getRequestScope().get("foo"));
}
public void testExposeResult_ScopeExpression() {
String valueToSet = "myValue";
@@ -41,7 +59,7 @@ public class ActionResultExposerTests extends TestCase {
Expression nameExpression = parser.parseExpression("#{requestScope.foo}", new ParserContextImpl()
.eval(RequestContext.class));
ActionResultExposer exposer = new ActionResultExposer(nameExpression, null);
ActionResultExposer exposer = new ActionResultExposer(nameExpression, null, null);
RequestContext context = new MockRequestContext();
@@ -51,6 +69,24 @@ public class ActionResultExposerTests extends TestCase {
assertEquals("Value stored at key 'foo' is incorrect", valueToSet, context.getRequestScope().get("foo"));
}
public void testExposeResult_ScopeExpressionWithTypeConversion() {
String valueToSet = "true";
ExpressionParser parser = new WebFlowELExpressionParser(new ExpressionFactoryImpl());
Expression nameExpression = parser.parseExpression("#{requestScope.foo}", new ParserContextImpl()
.eval(RequestContext.class));
ActionResultExposer exposer = new ActionResultExposer(nameExpression, null, Boolean.class);
RequestContext context = new MockRequestContext();
exposer.exposeResult(valueToSet, context);
assertTrue("Key 'foo' not found in request scope", context.getRequestScope().contains("foo"));
assertEquals("Value stored at key 'foo' is incorrect", Boolean.TRUE, context.getRequestScope().get("foo"));
}
public void testExposeResult_SearchExpression() {
String valueToSet = "myValue";
@@ -59,7 +95,7 @@ public class ActionResultExposerTests extends TestCase {
Expression nameExpression = parser.parseExpression("#{bean.foo}", new ParserContextImpl()
.eval(RequestContext.class));
ActionResultExposer exposer = new ActionResultExposer(nameExpression, null);
ActionResultExposer exposer = new ActionResultExposer(nameExpression, null, null);
RequestContext context = new MockRequestContext();
TestBean bean = new TestBean();
@@ -78,7 +114,7 @@ public class ActionResultExposerTests extends TestCase {
Expression nameExpression = parser.parseExpression("${foo}", new ParserContextImpl()
.eval(MutableAttributeMap.class));
ActionResultExposer exposer = new ActionResultExposer(nameExpression, ScopeType.REQUEST);
ActionResultExposer exposer = new ActionResultExposer(nameExpression, ScopeType.REQUEST, null);
RequestContext context = new MockRequestContext();
@@ -96,7 +132,7 @@ public class ActionResultExposerTests extends TestCase {
Expression nameExpression = parser.parseExpression("${requestScope.foo}", new ParserContextImpl()
.eval(RequestContext.class));
ActionResultExposer exposer = new ActionResultExposer(nameExpression, null);
ActionResultExposer exposer = new ActionResultExposer(nameExpression, null, null);
RequestContext context = new MockRequestContext();

View File

@@ -31,6 +31,8 @@ import org.springframework.webflow.test.MockRequestContext;
/**
* Unit tests for {@link EvaluateAction}.
*
* @author Jeremy Grelle
*/
public class EvaluateActionTests extends TestCase {
@@ -54,7 +56,7 @@ public class EvaluateActionTests extends TestCase {
.eval(MutableAttributeMap.class));
EvaluateAction action = new EvaluateAction(new StaticExpression("bar"), new ActionResultExposer(nameExpression,
ScopeType.FLOW));
ScopeType.FLOW, null));
Event result = action.execute(context);
assertEquals("bar", result.getId());
assertEquals("bar", context.getFlowScope().get("baz"));
@@ -68,19 +70,31 @@ public class EvaluateActionTests extends TestCase {
TestBean bean = new TestBean();
EvaluateAction action = new EvaluateAction(new StaticExpression(bean), new ActionResultExposer(nameExpression,
ScopeType.FLOW));
ScopeType.FLOW, null));
Event result = action.execute(context);
assertEquals("success", result.getId());
assertEquals(bean, context.getFlowScope().get("baz"));
}
public void testStringResult_ScopeSpecifiedWithTypeConversion() throws Exception {
ExpressionParser parser = new WebFlowELExpressionParser(new ExpressionFactoryImpl());
Expression nameExpression = parser.parseExpression("#{baz}", new ParserContextImpl()
.eval(MutableAttributeMap.class));
EvaluateAction action = new EvaluateAction(new StaticExpression("true"), new ActionResultExposer(
nameExpression, ScopeType.FLOW, Boolean.class));
Event result = action.execute(context);
assertEquals("true", result.getId());
assertEquals(Boolean.TRUE, context.getFlowScope().get("baz"));
}
public void testEvaluateExpressionResult_ScopeExpression() throws Exception {
ExpressionParser parser = new WebFlowELExpressionParser(new ExpressionFactoryImpl());
Expression nameExpression = parser.parseExpression("#{flowScope.baz}", new ParserContextImpl()
.eval(MutableAttributeMap.class));
EvaluateAction action = new EvaluateAction(new StaticExpression("bar"), new ActionResultExposer(nameExpression,
null));
null, null));
Event result = action.execute(context);
assertEquals("bar", result.getId());
assertEquals("bar", context.getFlowScope().get("baz"));
@@ -95,7 +109,7 @@ public class EvaluateActionTests extends TestCase {
context.getFlowScope().put("baz", bean);
EvaluateAction action = new EvaluateAction(new StaticExpression("bar"), new ActionResultExposer(nameExpression,
null));
null, null));
Event result = action.execute(context);
assertEquals("bar", result.getId());
assertEquals("bar", bean.getFoo());