diff --git a/spring-faces/src/main/java/org/springframework/faces/expression/CompositeFlowPropertyResolver.java b/spring-faces/src/main/java/org/springframework/faces/expression/CompositeFlowPropertyResolver.java index 1106d0b7..50e017e2 100644 --- a/spring-faces/src/main/java/org/springframework/faces/expression/CompositeFlowPropertyResolver.java +++ b/spring-faces/src/main/java/org/springframework/faces/expression/CompositeFlowPropertyResolver.java @@ -3,6 +3,7 @@ package org.springframework.faces.expression; import javax.el.CompositeELResolver; import javax.faces.el.PropertyResolver; +import org.springframework.binding.expression.el.MapAdaptableELResolver; import org.springframework.webflow.core.expression.el.RequestContextELResolver; import org.springframework.webflow.core.expression.el.ScopeSearchingELResolver; @@ -19,6 +20,7 @@ public class CompositeFlowPropertyResolver extends ELDelegatingPropertyResolver static { composite.add(new RequestContextELResolver()); composite.add(new ScopeSearchingELResolver()); + composite.add(new MapAdaptableELResolver()); } public CompositeFlowPropertyResolver(PropertyResolver nextResolver) { diff --git a/spring-faces/src/main/java/org/springframework/faces/expression/CompositeFlowVariableResolver.java b/spring-faces/src/main/java/org/springframework/faces/expression/CompositeFlowVariableResolver.java index 377e3f9e..2c173346 100644 --- a/spring-faces/src/main/java/org/springframework/faces/expression/CompositeFlowVariableResolver.java +++ b/spring-faces/src/main/java/org/springframework/faces/expression/CompositeFlowVariableResolver.java @@ -3,6 +3,7 @@ package org.springframework.faces.expression; import javax.el.CompositeELResolver; import javax.faces.el.VariableResolver; +import org.springframework.binding.expression.el.MapAdaptableELResolver; import org.springframework.webflow.core.expression.el.RequestContextELResolver; import org.springframework.webflow.core.expression.el.ScopeSearchingELResolver; @@ -18,7 +19,9 @@ public class CompositeFlowVariableResolver extends ELDelegatingVariableResolver static { composite.add(new RequestContextELResolver()); + composite.add(new ImplicitFlowVariableELResolver()); composite.add(new ScopeSearchingELResolver()); + composite.add(new MapAdaptableELResolver()); } public CompositeFlowVariableResolver(VariableResolver nextResolver) { diff --git a/spring-faces/src/main/java/org/springframework/faces/expression/ImplicitFlowVariableELResolver.java b/spring-faces/src/main/java/org/springframework/faces/expression/ImplicitFlowVariableELResolver.java new file mode 100644 index 00000000..9fb435b3 --- /dev/null +++ b/spring-faces/src/main/java/org/springframework/faces/expression/ImplicitFlowVariableELResolver.java @@ -0,0 +1,116 @@ +package org.springframework.faces.expression; + +import java.util.Iterator; + +import javax.el.ELContext; +import javax.el.ELResolver; +import javax.el.PropertyNotFoundException; +import javax.el.PropertyNotWritableException; + +import org.springframework.core.enums.StaticLabeledEnum; +import org.springframework.core.enums.StaticLabeledEnumResolver; +import org.springframework.webflow.execution.RequestContext; +import org.springframework.webflow.execution.RequestContextHolder; + +public class ImplicitFlowVariableELResolver extends ELResolver { + + public Class getCommonPropertyType(ELContext context, Object base) { + return Object.class; + } + + public Iterator getFeatureDescriptors(ELContext context, Object base) { + return null; + } + + public Class getType(ELContext context, Object base, Object property) { + if (base == null) { + Object resolved = resolveVariable(property.toString()); + if (resolved != null) { + context.setPropertyResolved(true); + return resolved.getClass(); + } + } + return null; + } + + public Object getValue(ELContext context, Object base, Object property) { + if (base == null) { + Object resolved = resolveVariable(property.toString()); + if (resolved != null) { + context.setPropertyResolved(true); + return resolved; + } + } + return null; + } + + public boolean isReadOnly(ELContext context, Object base, Object property) { + if (base == null) { + Object resolved = resolveVariable(property.toString()); + if (resolved != null) { + context.setPropertyResolved(true); + return true; + } + } + return false; + } + + public void setValue(ELContext context, Object base, Object property, Object value) { + if (base == null) { + Object resolved = resolveVariable(property.toString()); + if (resolved != null) { + context.setPropertyResolved(true); + throw new PropertyNotWritableException("The implicit flow variable " + property + + " cannot be overwritten."); + } + } + } + + private Object resolveVariable(String variable) { + try { + ImplicitFlowVariable flowVariable = (ImplicitFlowVariable) StaticLabeledEnumResolver.instance() + .getLabeledEnumByLabel(ImplicitFlowVariable.class, variable); + return flowVariable.resolve(); + } catch (IllegalArgumentException ex) { + return null; + } + } + + public abstract static class ImplicitFlowVariable extends StaticLabeledEnum { + + private ImplicitFlowVariable(int code, String label) { + super(code, label); + } + + public static final ImplicitFlowVariable FLASH_SCOPE = new ImplicitFlowVariable(1, "flashScope") { + public Object doResolve(RequestContext context) { + return context.getFlashScope(); + } + }; + + public static final ImplicitFlowVariable FLOW_SCOPE = new ImplicitFlowVariable(2, "flowScope") { + public Object doResolve(RequestContext context) { + return context.getFlowScope(); + } + }; + + public static final ImplicitFlowVariable CONVERSATION_SCOPE = new ImplicitFlowVariable(3, "conversationScope") { + public Object doResolve(RequestContext context) { + return context.getConversationScope(); + } + }; + + public Object resolve() { + RequestContext context = RequestContextHolder.getRequestContext(); + if (context != null) { + return doResolve(context); + } else { + throw new PropertyNotFoundException("Implicit flow variable " + this.getLabel() + + " could not be resolved. No active flow found."); + } + } + + public abstract Object doResolve(RequestContext context); + } + +} diff --git a/spring-faces/src/test/java/org/springframework/faces/expression/ImplicitFlowVariableELResolverTests.java b/spring-faces/src/test/java/org/springframework/faces/expression/ImplicitFlowVariableELResolverTests.java new file mode 100644 index 00000000..677cbfdc --- /dev/null +++ b/spring-faces/src/test/java/org/springframework/faces/expression/ImplicitFlowVariableELResolverTests.java @@ -0,0 +1,47 @@ +package org.springframework.faces.expression; + +import javax.el.ELContext; + +import junit.framework.TestCase; + +import org.springframework.binding.expression.el.DefaultELContext; +import org.springframework.webflow.execution.RequestContext; +import org.springframework.webflow.execution.RequestContextHolder; +import org.springframework.webflow.test.MockRequestContext; + +public class ImplicitFlowVariableELResolverTests extends TestCase { + + RequestContext requestContext = new MockRequestContext(); + ELContext elContext = new DefaultELContext(new ImplicitFlowVariableELResolver(), null, null); + + public void setUp() { + RequestContextHolder.setRequestContext(requestContext); + } + + public void testGetValue_UnknownVariable() { + Object result = elContext.getELResolver().getValue(elContext, null, "foo"); + assertNull(result); + assertFalse(elContext.isPropertyResolved()); + } + + public void testGetValue_FlashScope() { + Object result = elContext.getELResolver().getValue(elContext, null, "flashScope"); + assertNotNull(result); + assertTrue(elContext.isPropertyResolved()); + assertSame(requestContext.getFlashScope(), result); + } + + public void testGetValue_FlowScope() { + Object result = elContext.getELResolver().getValue(elContext, null, "flowScope"); + assertNotNull(result); + assertTrue(elContext.isPropertyResolved()); + assertSame(requestContext.getFlowScope(), result); + } + + public void testGetValue_ConversationScope() { + Object result = elContext.getELResolver().getValue(elContext, null, "conversationScope"); + assertNotNull(result); + assertTrue(elContext.isPropertyResolved()); + assertSame(requestContext.getConversationScope(), result); + } +}