diff --git a/spring-binding/src/main/java/org/springframework/binding/method/MethodInvoker.java b/spring-binding/src/main/java/org/springframework/binding/method/MethodInvoker.java index b5894259..4653f238 100644 --- a/spring-binding/src/main/java/org/springframework/binding/method/MethodInvoker.java +++ b/spring-binding/src/main/java/org/springframework/binding/method/MethodInvoker.java @@ -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 + "]"); } diff --git a/spring-binding/src/main/java/org/springframework/binding/method/MethodSignature.java b/spring-binding/src/main/java/org/springframework/binding/method/MethodSignature.java index e63df0fb..b8f4ee96 100644 --- a/spring-binding/src/main/java/org/springframework/binding/method/MethodSignature.java +++ b/spring-binding/src/main/java/org/springframework/binding/method/MethodSignature.java @@ -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; diff --git a/spring-binding/src/test/java/org/springframework/binding/method/MethodInvokerTests.java b/spring-binding/src/test/java/org/springframework/binding/method/MethodInvokerTests.java index 12c2ea53..289bbc32 100644 --- a/spring-binding/src/test/java/org/springframework/binding/method/MethodInvokerTests.java +++ b/spring-binding/src/test/java/org/springframework/binding/method/MethodInvokerTests.java @@ -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; } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/action/ActionResultExposer.java b/spring-webflow/src/main/java/org/springframework/webflow/action/ActionResultExposer.java index 6791b7e6..d8ad0318 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/action/ActionResultExposer.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/action/ActionResultExposer.java @@ -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); + } } \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/action/EvaluateAction.java b/spring-webflow/src/main/java/org/springframework/webflow/action/EvaluateAction.java index 27c8b40e..4175a6eb 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/action/EvaluateAction.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/action/EvaluateAction.java @@ -31,6 +31,7 @@ import org.springframework.webflow.execution.RequestContext; * @see ResultEventFactorySelector * * @author Keith Donald + * @author Jeremy Grelle */ public class EvaluateAction extends AbstractAction { diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilder.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilder.java index ec1a4b8f..b4fe433d 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilder.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilder.java @@ -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) { diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/spring-webflow-2.0.xsd b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/spring-webflow-2.0.xsd index 6f138c7d..cae581b3 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/spring-webflow-2.0.xsd +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/spring-webflow-2.0.xsd @@ -1139,6 +1139,16 @@ If not specified the name attribute must be a fully resolvable expression such a ]]> + + + + + + + diff --git a/spring-webflow/src/test/java/org/springframework/webflow/action/ActionResultExposerTests.java b/spring-webflow/src/test/java/org/springframework/webflow/action/ActionResultExposerTests.java index 88a8c62d..ee884be5 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/action/ActionResultExposerTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/action/ActionResultExposerTests.java @@ -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(); diff --git a/spring-webflow/src/test/java/org/springframework/webflow/action/EvaluateActionTests.java b/spring-webflow/src/test/java/org/springframework/webflow/action/EvaluateActionTests.java index a6ca6f0d..215ea069 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/action/EvaluateActionTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/action/EvaluateActionTests.java @@ -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());