diff --git a/spring-binding/src/main/java/org/springframework/binding/expression/ExpressionParser.java b/spring-binding/src/main/java/org/springframework/binding/expression/ExpressionParser.java
index 882e52e2..82a146bb 100644
--- a/spring-binding/src/main/java/org/springframework/binding/expression/ExpressionParser.java
+++ b/spring-binding/src/main/java/org/springframework/binding/expression/ExpressionParser.java
@@ -23,16 +23,6 @@ package org.springframework.binding.expression;
*/
public interface ExpressionParser {
- /**
- * Is the provided string an explicitly delimited expression this parser knows how to parse? For example, this
- * method may return true if the string provided is enclosed in "${}". It may also return true if the string
- * provided is a mix of literal text and delimited expression syntax, for example "hello world ${name}!" The exact
- * semantics are determined by the parser implementation.
- * @param string the string
- * @return true if the string is a delimited expression, false otherwise.
- */
- public boolean hasDelimitedExpression(String string);
-
/**
* Parse the provided expression string, returning an expression evaluator capable of evaluating it.
* @param expressionString the parseable expression string; cannot be null (required)
diff --git a/spring-binding/src/main/java/org/springframework/binding/expression/ExpressionVariable.java b/spring-binding/src/main/java/org/springframework/binding/expression/ExpressionVariable.java
index 840409b9..dcbc6347 100644
--- a/spring-binding/src/main/java/org/springframework/binding/expression/ExpressionVariable.java
+++ b/spring-binding/src/main/java/org/springframework/binding/expression/ExpressionVariable.java
@@ -9,21 +9,22 @@ import org.springframework.util.Assert;
public class ExpressionVariable {
private String name;
- private Object value;
+ private String valueExpression;
/**
* Creates a new expression variable
* @param name the name of the variable, acting as an convenient alias
- * @param value the initial value of the variable
+ * @param value the value expression
*/
- public ExpressionVariable(String name, Object value) {
+ public ExpressionVariable(String name, String value) {
Assert.hasText(name, "The expression variable must be named");
+ Assert.hasText(value, "The expression variable's value expression is required");
this.name = name;
- this.value = value;
+ this.valueExpression = value;
}
/**
- * Returns the variable name, typically vary simple like "index".
+ * Returns the variable name.
* @return the variable name
*/
public String getName() {
@@ -34,8 +35,8 @@ public class ExpressionVariable {
* Returns the expression that will be evaluated when the variable is referenced by its name in another expression.
* @return the expression value.
*/
- public Object getValue() {
- return value;
+ public String getValueExpression() {
+ return valueExpression;
}
public boolean equals(Object o) {
diff --git a/spring-binding/src/main/java/org/springframework/binding/expression/el/DefaultElContextFactory.java b/spring-binding/src/main/java/org/springframework/binding/expression/el/DefaultElContextFactory.java
index a19b748b..a8f96fdb 100644
--- a/spring-binding/src/main/java/org/springframework/binding/expression/el/DefaultElContextFactory.java
+++ b/spring-binding/src/main/java/org/springframework/binding/expression/el/DefaultElContextFactory.java
@@ -1,10 +1,9 @@
package org.springframework.binding.expression.el;
import javax.el.ELContext;
-import javax.el.VariableMapper;
public class DefaultElContextFactory implements ELContextFactory {
- public ELContext getELContext(Object target, VariableMapper variableMapper) {
- return new DefaultELContext(new DefaultELResolver(target, null), variableMapper, null);
+ public ELContext getELContext(Object target) {
+ return new DefaultELContext(new DefaultELResolver(target, null), null, null);
}
}
diff --git a/spring-binding/src/main/java/org/springframework/binding/expression/el/ELContextFactory.java b/spring-binding/src/main/java/org/springframework/binding/expression/el/ELContextFactory.java
index 01508de7..76b8347d 100644
--- a/spring-binding/src/main/java/org/springframework/binding/expression/el/ELContextFactory.java
+++ b/spring-binding/src/main/java/org/springframework/binding/expression/el/ELContextFactory.java
@@ -2,7 +2,6 @@ package org.springframework.binding.expression.el;
import javax.el.ELContext;
import javax.el.ELResolver;
-import javax.el.VariableMapper;
/**
* A factory for creating a EL context object that will be used to evaluate a target object of an EL expression.
@@ -16,9 +15,8 @@ public interface ELContextFactory {
* object. In certain environments the target will be null and the base object of the expression is expected to be
* resolved via the ELContext's {@link ELResolver} chain.
* @param target The base object for the expression evaluation
- * @param variableMapper The mapping storing variables needed during expression evaluation
* @return ELContext The configured ELContext instance for evaluating expressions.
*/
- public ELContext getELContext(Object target, VariableMapper variableMapper);
+ public ELContext getELContext(Object target);
}
\ No newline at end of file
diff --git a/spring-binding/src/main/java/org/springframework/binding/expression/el/ELExpression.java b/spring-binding/src/main/java/org/springframework/binding/expression/el/ELExpression.java
index 1aeea15d..49e7b137 100644
--- a/spring-binding/src/main/java/org/springframework/binding/expression/el/ELExpression.java
+++ b/spring-binding/src/main/java/org/springframework/binding/expression/el/ELExpression.java
@@ -3,7 +3,6 @@ package org.springframework.binding.expression.el;
import javax.el.ELContext;
import javax.el.ELException;
import javax.el.ValueExpression;
-import javax.el.VariableMapper;
import org.springframework.binding.expression.EvaluationAttempt;
import org.springframework.binding.expression.EvaluationException;
@@ -21,24 +20,20 @@ public class ELExpression implements Expression {
private ValueExpression valueExpression;
- private VariableMapper variableMapper;
-
/**
* Creates a new el expression
* @param factory the el context factory for creating the EL context that will be used during expression evaluation
* @param valueExpression the value expression to evaluate
- * @param variableMapper the variable mapper containing variables needed during expression evaluation
*/
- public ELExpression(ELContextFactory factory, ValueExpression valueExpression, VariableMapper variableMapper) {
+ public ELExpression(ELContextFactory factory, ValueExpression valueExpression) {
Assert.notNull(factory, "The ELContextFactory is required to evaluate EL expressions");
Assert.notNull(valueExpression, "The EL value expression is required for evaluation");
this.elContextFactory = factory;
this.valueExpression = valueExpression;
- this.variableMapper = variableMapper;
}
public Object getValue(Object context) throws EvaluationException {
- ELContext ctx = elContextFactory.getELContext(context, variableMapper);
+ ELContext ctx = elContextFactory.getELContext(context);
try {
return valueExpression.getValue(ctx);
} catch (ELException ex) {
@@ -47,7 +42,7 @@ public class ELExpression implements Expression {
}
public void setValue(Object context, Object value) throws EvaluationException {
- ELContext ctx = elContextFactory.getELContext(context, variableMapper);
+ ELContext ctx = elContextFactory.getELContext(context);
try {
valueExpression.setValue(ctx, value);
} catch (ELException ex) {
diff --git a/spring-binding/src/main/java/org/springframework/binding/expression/el/ELExpressionParser.java b/spring-binding/src/main/java/org/springframework/binding/expression/el/ELExpressionParser.java
index 74cbcea8..6b7b0574 100644
--- a/spring-binding/src/main/java/org/springframework/binding/expression/el/ELExpressionParser.java
+++ b/spring-binding/src/main/java/org/springframework/binding/expression/el/ELExpressionParser.java
@@ -26,21 +26,6 @@ import org.springframework.util.Assert;
*/
public class ELExpressionParser implements ExpressionParser {
- /**
- * The expression prefix.
- */
- private static final String EXPRESSION_PREFIX_IMMEDIATE = "${";
-
- /**
- * The expression prefix.
- */
- private static final String EXPRESSION_PREFIX_DEFERRED = "#{";
-
- /**
- * The expression suffix.
- */
- private static final String EXPRESSION_SUFFIX = "}";
-
/**
* The ExpressionFactory for constructing EL expressions
*/
@@ -65,12 +50,6 @@ public class ELExpressionParser implements ExpressionParser {
contextFactories.put(contextType, contextFactory);
}
- public boolean hasDelimitedExpression(String expressionString) {
- return (expressionString.startsWith(EXPRESSION_PREFIX_DEFERRED) && expressionString.endsWith(EXPRESSION_SUFFIX))
- || (expressionString.startsWith(EXPRESSION_PREFIX_IMMEDIATE) && expressionString
- .endsWith(EXPRESSION_SUFFIX));
- }
-
public Expression parseExpression(String expressionString, ParserContext context) throws ParserException {
Assert.notNull(expressionString, "The expression string to parse is required");
if (context == null) {
@@ -82,7 +61,7 @@ public class ELExpressionParser implements ExpressionParser {
ValueExpression expression = expressionFactory.createValueExpression(elContext, expressionString,
getExpectedType(context));
ELContextFactory contextFactory = getContextFactory(context.getEvaluationContextType(), expressionString);
- return new ELExpression(contextFactory, expression, elContext.getVariableMapper());
+ return new ELExpression(contextFactory, expression);
} catch (ELException e) {
throw new ParserException(expressionString, e);
}
@@ -132,8 +111,8 @@ public class ELExpressionParser implements ExpressionParser {
variableMapper = new VariableMapperImpl();
for (int i = 0; i < variables.length; i++) {
ExpressionVariable var = variables[i];
- ValueExpression expr = expressionFactory.createValueExpression(this,
- String.valueOf(var.getValue()), Object.class);
+ ValueExpression expr = expressionFactory.createValueExpression(this, var.getValueExpression(),
+ Object.class);
variableMapper.setVariable(var.getName(), expr);
}
}
diff --git a/spring-binding/src/main/java/org/springframework/binding/expression/ognl/OgnlExpression.java b/spring-binding/src/main/java/org/springframework/binding/expression/ognl/OgnlExpression.java
index 9ec4e2a6..b639a8d6 100644
--- a/spring-binding/src/main/java/org/springframework/binding/expression/ognl/OgnlExpression.java
+++ b/spring-binding/src/main/java/org/springframework/binding/expression/ognl/OgnlExpression.java
@@ -17,6 +17,7 @@ package org.springframework.binding.expression.ognl;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
import ognl.Ognl;
@@ -25,7 +26,6 @@ import ognl.OgnlException;
import org.springframework.binding.expression.EvaluationAttempt;
import org.springframework.binding.expression.EvaluationException;
import org.springframework.binding.expression.Expression;
-import org.springframework.binding.expression.ExpressionVariable;
import org.springframework.binding.expression.SetValueAttempt;
import org.springframework.util.Assert;
@@ -44,14 +44,21 @@ class OgnlExpression implements Expression {
/**
* Expression variable initial values.
*/
- private Map variableMap;;
+ private Map variableExpressions;
+
+ /**
+ * The expected type of object returned from evaluating the expression.
+ */
+ private Class expectedResultType;
/**
* Creates a new OGNL expression.
* @param expression the parsed expression
*/
- public OgnlExpression(Object expression, ExpressionVariable[] variables) {
- init(expression, variables);
+ public OgnlExpression(Object expression, Map variableExpressions, Class expectedResultType) {
+ this.expression = expression;
+ this.variableExpressions = variableExpressions;
+ this.expectedResultType = expectedResultType;
}
public int hashCode() {
@@ -68,7 +75,7 @@ class OgnlExpression implements Expression {
public Object getValue(Object context) throws EvaluationException {
try {
- return Ognl.getValue(expression, variableMap, context);
+ return Ognl.getValue(expression, getVariables(context), context, expectedResultType);
} catch (OgnlException e) {
if (e.getReason() != null && e.getReason() != e) {
// unwrap the OgnlException since the actual exception is wrapped inside it
@@ -83,28 +90,23 @@ class OgnlExpression implements Expression {
public void setValue(Object context, Object value) {
Assert.notNull(context, "The context to set the provided value in is required");
try {
- Ognl.setValue(expression, variableMap, context, value);
+ Ognl.setValue(expression, getVariables(context), context, value);
} catch (OgnlException e) {
throw new EvaluationException(new SetValueAttempt(this, context, value), e);
}
}
- private void init(Object expression, ExpressionVariable[] variables) {
- this.expression = expression;
- variableMap = createVariableMap(variables);
- }
-
- private Map createVariableMap(ExpressionVariable[] variables) {
- if (variables != null && variables.length > 0) {
- Map variableMap = new HashMap(variables.length);
- for (int i = 0; i < variables.length; i++) {
- ExpressionVariable var = variables[i];
- variableMap.put(var.getName(), var.getValue());
- }
- return variableMap;
- } else {
+ private Map getVariables(Object context) {
+ if (variableExpressions == null) {
return Collections.EMPTY_MAP;
}
+ Map variables = new HashMap(variableExpressions.size(), 1);
+ for (Iterator it = variableExpressions.entrySet().iterator(); it.hasNext();) {
+ Map.Entry var = (Map.Entry) it.next();
+ Expression valueExpression = (Expression) var.getValue();
+ variables.put(var.getKey(), valueExpression.getValue(context));
+ }
+ return variables;
}
public String toString() {
diff --git a/spring-binding/src/main/java/org/springframework/binding/expression/ognl/OgnlExpressionParser.java b/spring-binding/src/main/java/org/springframework/binding/expression/ognl/OgnlExpressionParser.java
index 5e716355..6f4e05eb 100644
--- a/spring-binding/src/main/java/org/springframework/binding/expression/ognl/OgnlExpressionParser.java
+++ b/spring-binding/src/main/java/org/springframework/binding/expression/ognl/OgnlExpressionParser.java
@@ -15,33 +15,109 @@
*/
package org.springframework.binding.expression.ognl;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
import ognl.Ognl;
import ognl.OgnlException;
import ognl.OgnlRuntime;
import ognl.PropertyAccessor;
import org.springframework.binding.expression.Expression;
+import org.springframework.binding.expression.ExpressionParser;
+import org.springframework.binding.expression.ExpressionVariable;
import org.springframework.binding.expression.ParserContext;
import org.springframework.binding.expression.ParserException;
-import org.springframework.binding.expression.support.AbstractExpressionParser;
+import org.springframework.binding.expression.support.CompositeStringExpression;
import org.springframework.binding.expression.support.NullParserContext;
+import org.springframework.binding.expression.support.StaticExpression;
+import org.springframework.util.Assert;
/**
* An expression parser that parses Ognl expressions.
*
* @author Keith Donald
*/
-public class OgnlExpressionParser extends AbstractExpressionParser {
+public class OgnlExpressionParser implements ExpressionParser {
- public Expression doParseExpression(String expressionString, ParserContext context) throws ParserException {
- if (context == null) {
- context = NullParserContext.INSTANCE;
- }
- try {
- return new OgnlExpression(Ognl.parseExpression(expressionString), context.getExpressionVariables());
- } catch (OgnlException e) {
- throw new ParserException(expressionString, e);
- }
+ /**
+ * The expression prefix.
+ */
+ private static final String DEFAULT_EXPRESSION_PREFIX = "${";
+
+ /**
+ * The expression suffix.
+ */
+ private static final String DEFAULT_EXPRESSION_SUFFIX = "}";
+
+ /**
+ * The marked expression delimter prefix.
+ */
+ private String expressionPrefix = DEFAULT_EXPRESSION_PREFIX;
+
+ /**
+ * The marked expression delimiter suffix.
+ */
+ private String expressionSuffix = DEFAULT_EXPRESSION_SUFFIX;
+
+ /**
+ * Should we allow undelimited OGNL eval expressions like "foo.bar"? If not, evalutable OGNL expressions must be
+ * enclosed in delimiters like ${foo.bar} else they are treated as literal expressions. Mainly here for
+ * compatability reasons, as Web Flow 1.0 allows undelimited OGNL eval expressions by default.
+ */
+ private boolean allowUndelimitedEvalExpressions;
+
+ /**
+ * Returns the configured expression delimiter prefix. Defaults to "${".
+ */
+ public String getExpressionPrefix() {
+ return expressionPrefix;
+ }
+
+ /**
+ * Sets the expression delimiter prefix.
+ */
+ public void setExpressionPrefix(String expressionPrefix) {
+ this.expressionPrefix = expressionPrefix;
+ }
+
+ /**
+ * Returns the expression delimiter suffix. Defaults to "}".
+ */
+ public String getExpressionSuffix() {
+ return expressionSuffix;
+ }
+
+ /**
+ * Sets the expression delimiter suffix.
+ */
+ public void setExpressionSuffix(String expressionSuffix) {
+ this.expressionSuffix = expressionSuffix;
+ }
+
+ /**
+ * Returns if this parser should we allow undelimited OGNL eval expressions like foo.bar.
+ */
+ public boolean getAllowUndelimitedEvalExpressions() {
+ return allowUndelimitedEvalExpressions;
+ }
+
+ /**
+ * Sets if this parser should allow undelimited OGNL eval expressions like "foo.bar"? If not, evalutable OGNL
+ * expressions must be enclosed in delimiters like ${foo.bar}, else they are treated as literal expressions.
+ */
+ public void setAllowUndelimitedEvalExpressions(boolean allowUndelmitedEvalExpressions) {
+ this.allowUndelimitedEvalExpressions = allowUndelmitedEvalExpressions;
+ }
+
+ public static String getDEFAULT_EXPRESSION_PREFIX() {
+ return DEFAULT_EXPRESSION_PREFIX;
+ }
+
+ public static String getDEFAULT_EXPRESSION_SUFFIX() {
+ return DEFAULT_EXPRESSION_SUFFIX;
}
/**
@@ -52,4 +128,152 @@ public class OgnlExpressionParser extends AbstractExpressionParser {
public void addPropertyAccessor(Class clazz, PropertyAccessor propertyAccessor) {
OgnlRuntime.setPropertyAccessor(clazz, propertyAccessor);
}
+
+ // expression parser
+
+ public Expression parseExpression(String expressionString, ParserContext context) throws ParserException {
+ Assert.notNull(expressionString, "The expression string to parse is required");
+ Expression[] expressions = parseExpressions(expressionString, context);
+ if (expressions.length == 1) {
+ return expressions[0];
+ } else {
+ return new CompositeStringExpression(expressions);
+ }
+ }
+
+ /**
+ * Is the provided string a template expression this parser can parse? Always returns true if this
+ * OGNL expression parser is configured to not allow undelimited OGNL expressions. If undelimited OGNL
+ * expressions are allowed like "foo.bar", this method only returns true if an explicitly delimited expression is
+ * present in the string like "hello my name is ${name}" or "${foo.bar}".
+ *
+ * In general, a template expression is either:
+ *