diff --git a/spring-binding/.classpath b/spring-binding/.classpath
index 20822dbe..4ccd1929 100644
--- a/spring-binding/.classpath
+++ b/spring-binding/.classpath
@@ -11,9 +11,10 @@
-
+
+
diff --git a/spring-binding/.settings/org.eclipse.jdt.core.prefs b/spring-binding/.settings/org.eclipse.jdt.core.prefs
index 127f96ce..93ca0d5e 100644
--- a/spring-binding/.settings/org.eclipse.jdt.core.prefs
+++ b/spring-binding/.settings/org.eclipse.jdt.core.prefs
@@ -1,4 +1,4 @@
-#Mon Jun 30 01:12:48 EDT 2008
+#Thu Jul 03 00:22:38 EDT 2008
eclipse.preferences.version=1
org.eclipse.jdt.core.codeComplete.argumentPrefixes=
org.eclipse.jdt.core.codeComplete.argumentSuffixes=
diff --git a/spring-binding/ivy.xml b/spring-binding/ivy.xml
index 848bc48f..8d8ebc13 100644
--- a/spring-binding/ivy.xml
+++ b/spring-binding/ivy.xml
@@ -23,7 +23,7 @@
-
+
diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/ConversionService.java b/spring-binding/src/main/java/org/springframework/binding/convert/ConversionService.java
index e6f171ef..fb924342 100644
--- a/spring-binding/src/main/java/org/springframework/binding/convert/ConversionService.java
+++ b/spring-binding/src/main/java/org/springframework/binding/convert/ConversionService.java
@@ -46,4 +46,6 @@ public interface ConversionService {
*/
public Class getClassForAlias(String alias);
+ public ConversionExecutor[] getConversionExecutors(Class sourceClass);
+
}
\ No newline at end of file
diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/service/GenericConversionService.java b/spring-binding/src/main/java/org/springframework/binding/convert/service/GenericConversionService.java
index 96e5c676..d4845585 100644
--- a/spring-binding/src/main/java/org/springframework/binding/convert/service/GenericConversionService.java
+++ b/spring-binding/src/main/java/org/springframework/binding/convert/service/GenericConversionService.java
@@ -16,11 +16,15 @@
package org.springframework.binding.convert.service;
import java.lang.reflect.Modifier;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
+import java.util.Set;
import org.springframework.binding.convert.ConversionExecutor;
import org.springframework.binding.convert.ConversionExecutorNotFoundException;
@@ -174,6 +178,22 @@ public class GenericConversionService implements ConversionService {
// subclassing support
+ public ConversionExecutor[] getConversionExecutors(Class sourceClass) {
+ Map sourceMap = getSourceMap(sourceClass);
+ if (sourceMap.isEmpty()) {
+ return new ConversionExecutor[0];
+ }
+ Set entries = sourceMap.entrySet();
+ List conversionExecutors = new ArrayList(entries.size());
+ for (Iterator it = entries.iterator(); it.hasNext();) {
+ Map.Entry entry = (Map.Entry) it.next();
+ Class targetClass = (Class) entry.getKey();
+ Converter converter = (Converter) entry.getValue();
+ conversionExecutors.add(new StaticConversionExecutor(sourceClass, targetClass, converter));
+ }
+ return (ConversionExecutor[]) conversionExecutors.toArray(new ConversionExecutor[entries.size()]);
+ }
+
/**
* Returns an indexed map of converters. Each entry key is a source class that can be converted from, and each entry
* value is a map of target classes that can be convertered to, ultimately mapping to a specific converter that can
diff --git a/spring-binding/src/main/java/org/springframework/binding/expression/beanwrapper/BeanWrapperExpression.java b/spring-binding/src/main/java/org/springframework/binding/expression/beanwrapper/BeanWrapperExpression.java
new file mode 100644
index 00000000..297c00e8
--- /dev/null
+++ b/spring-binding/src/main/java/org/springframework/binding/expression/beanwrapper/BeanWrapperExpression.java
@@ -0,0 +1,96 @@
+package org.springframework.binding.expression.beanwrapper;
+
+import java.beans.PropertyEditorSupport;
+
+import org.springframework.beans.BeanWrapperImpl;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.InvalidPropertyException;
+import org.springframework.binding.convert.ConversionExecutor;
+import org.springframework.binding.convert.ConversionService;
+import org.springframework.binding.expression.EvaluationAttempt;
+import org.springframework.binding.expression.EvaluationException;
+import org.springframework.binding.expression.Expression;
+import org.springframework.binding.expression.PropertyNotFoundException;
+
+public class BeanWrapperExpression implements Expression {
+
+ private String expression;
+
+ private ConversionService conversionService;
+
+ public BeanWrapperExpression(String expression, ConversionService conversionService) {
+ this.expression = expression;
+ this.conversionService = conversionService;
+ }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof BeanWrapperExpression)) {
+ return false;
+ }
+ BeanWrapperExpression other = (BeanWrapperExpression) o;
+ return expression.equals(other.expression);
+ }
+
+ public int hashCode() {
+ return expression.hashCode();
+ }
+
+ public Object getValue(Object context) throws EvaluationException {
+ try {
+ BeanWrapperImpl beanWrapper = new BeanWrapperImpl(context);
+ return beanWrapper.getPropertyValue(expression);
+ } catch (InvalidPropertyException e) {
+ throw new PropertyNotFoundException(new EvaluationAttempt(this, context), e);
+ } catch (BeansException e) {
+ throw new EvaluationException(new EvaluationAttempt(this, context), e);
+ }
+ }
+
+ public void setValue(Object context, Object value) {
+ try {
+ BeanWrapperImpl beanWrapper = new BeanWrapperImpl(context);
+ ConversionExecutor[] converters = conversionService.getConversionExecutors(String.class);
+ for (int i = 0; i < converters.length; i++) {
+ ConversionExecutor converter = converters[i];
+ beanWrapper.registerCustomEditor(converter.getTargetClass(), new PropertyEditorConverter(converter));
+ }
+ beanWrapper.setPropertyValue(expression, value);
+ } catch (InvalidPropertyException e) {
+ throw new PropertyNotFoundException(new EvaluationAttempt(this, context), e);
+ } catch (BeansException e) {
+ throw new EvaluationException(new EvaluationAttempt(this, context), e);
+ }
+ }
+
+ public Class getValueType(Object context) {
+ try {
+ BeanWrapperImpl beanWrapper = new BeanWrapperImpl(context);
+ return beanWrapper.getPropertyType(expression);
+ } catch (InvalidPropertyException e) {
+ throw new PropertyNotFoundException(new EvaluationAttempt(this, context), e);
+ } catch (BeansException e) {
+ throw new EvaluationException(new EvaluationAttempt(this, context), e);
+ }
+ }
+
+ public String getExpressionString() {
+ return expression;
+ }
+
+ public String toString() {
+ return expression;
+ }
+
+ public static class PropertyEditorConverter extends PropertyEditorSupport {
+
+ private ConversionExecutor converter;
+
+ public PropertyEditorConverter(ConversionExecutor converter) {
+ this.converter = converter;
+ }
+
+ public void setAsText(String text) throws IllegalArgumentException {
+ setValue(converter.execute(text));
+ }
+ }
+}
diff --git a/spring-binding/src/main/java/org/springframework/binding/expression/beanwrapper/BeanWrapperExpressionParser.java b/spring-binding/src/main/java/org/springframework/binding/expression/beanwrapper/BeanWrapperExpressionParser.java
new file mode 100644
index 00000000..0a344249
--- /dev/null
+++ b/spring-binding/src/main/java/org/springframework/binding/expression/beanwrapper/BeanWrapperExpressionParser.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.binding.expression.beanwrapper;
+
+import org.springframework.binding.convert.ConversionService;
+import org.springframework.binding.expression.Expression;
+import org.springframework.binding.expression.ParserContext;
+import org.springframework.binding.expression.ParserException;
+import org.springframework.binding.expression.support.AbstractExpressionParser;
+
+/**
+ * An expression parser that parses Ognl expressions.
+ *
+ * @author Keith Donald
+ */
+public class BeanWrapperExpressionParser extends AbstractExpressionParser {
+
+ private ConversionService conversionService;
+
+ public ConversionService getConversionService() {
+ return conversionService;
+ }
+
+ public void setConversionService(ConversionService conversionService) {
+ this.conversionService = conversionService;
+ }
+
+ protected Expression doParseExpression(String expressionString, ParserContext context) throws ParserException {
+ return new BeanWrapperExpression(expressionString, conversionService);
+ }
+}
\ No newline at end of file
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 0e9e2b26..af22e7f9 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
@@ -40,24 +40,12 @@ import org.springframework.binding.expression.SetValueAttempt;
*/
class OgnlExpression implements Expression {
- /**
- * The expression.
- */
private Object expression;
- /**
- * Expression variable initial values.
- */
private Map variableExpressions;
- /**
- * The expected type of object returned from evaluating the expression.
- */
private Class expectedResultType;
- /**
- * The original expression string.
- */
private String expressionString;
/**
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 34f28094..8a65a421 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,233 +15,21 @@
*/
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.CompositeStringExpression;
-import org.springframework.binding.expression.support.LiteralExpression;
-import org.springframework.binding.expression.support.NullParserContext;
-import org.springframework.util.Assert;
+import org.springframework.binding.expression.support.AbstractExpressionParser;
/**
* An expression parser that parses Ognl expressions.
*
* @author Keith Donald
*/
-public class OgnlExpressionParser implements ExpressionParser {
-
- /**
- * 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 delimited OGNL eval expressions like "#{foo.bar}"? If not, evalutable OGNL expressions must not
- * be enclosed in delimiters like ${foo.bar} else an exception is thrown. Only here for compatability reasons, as
- * Web Flow 1.0 allows delimited OGNL eval expressions while 2.x does not.
- */
- private boolean allowDelimitedEvalExpressions;
-
- /**
- * 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 allows delimited OGNL eval expressions like ${foo.bar}.
- */
- public boolean getAllowDelimitedEvalExpressions() {
- return allowDelimitedEvalExpressions;
- }
-
- /**
- * Sets if this parser allows OGNL eval expressions like ${foo.bar}.
- */
- public void setAllowDelimitedEvalExpressions(boolean allowDelmitedEvalExpressions) {
- this.allowDelimitedEvalExpressions = allowDelmitedEvalExpressions;
- }
-
- /**
- * Add a property access strategy for the given class.
- * @param clazz the class that contains properties needing access
- * @param propertyAccessor the property access strategy
- */
- 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");
- if (context == null) {
- context = NullParserContext.INSTANCE;
- }
- if (context.isTemplate()) {
- return parseTemplate(expressionString, context);
- } else {
- if (expressionString.startsWith(getExpressionPrefix()) && expressionString.endsWith(getExpressionSuffix())) {
- if (!allowDelimitedEvalExpressions) {
- throw new ParserException(
- expressionString,
- "The expression '"
- + expressionString
- + "' being parsed is expected be a standard OGNL expression. Do not attempt to enclose such expression strings in ${} delimiters--this is redundant. If you need to parse a template that mixes literal text with evaluatable blocks, set the 'template' parser context attribute to true.",
- null);
- } else {
- int lastIndex = expressionString.length() - getExpressionSuffix().length();
- String ognlExpression = expressionString.substring(getExpressionPrefix().length(), lastIndex);
- return doParseExpression(ognlExpression, context);
- }
- } else {
- return doParseExpression(expressionString, context);
- }
- }
- }
-
- private Expression parseTemplate(String expressionString, ParserContext context) throws ParserException {
- Assert.notNull(expressionString, "The expression string to parse is required");
- if (expressionString.length() == 0) {
- return parseEmptyExpressionString(context);
- }
- Expression[] expressions = parseExpressions(expressionString, context);
- if (expressions.length == 1) {
- return expressions[0];
- } else {
- return new CompositeStringExpression(expressions);
- }
- }
-
- // helper methods
-
- /**
- * Helper that handles a empty expression string.
- */
- private Expression parseEmptyExpressionString(ParserContext context) {
- if (allowDelimitedEvalExpressions) {
- // let the parser handle it
- return doParseExpression("", context);
- } else {
- // return a literal expression containing the empty string
- return new LiteralExpression("");
- }
- }
-
- /**
- * Helper that parses given expression string using the configured parser. The expression string can contain any
- * number of expressions all contained in "${...}" markers. For instance: "foo${expr0}bar${expr1}". The static
- * pieces of text will also be returned as Expressions that just return that static piece of text. As a result,
- * evaluating all returned expressions and concatenating the results produces the complete evaluated string.
- * @param expressionString the expression string
- * @return the parsed expressions
- * @throws ParserException when the expressions cannot be parsed
- */
- private Expression[] parseExpressions(String expressionString, ParserContext context) throws ParserException {
- List expressions = new LinkedList();
- int startIdx = 0;
- while (startIdx < expressionString.length()) {
- int prefixIndex = expressionString.indexOf(getExpressionPrefix(), startIdx);
- if (prefixIndex >= startIdx) {
- // a inner expression was found - this is a composite
- if (prefixIndex > startIdx) {
- expressions.add(new LiteralExpression(expressionString.substring(startIdx, prefixIndex)));
- startIdx = prefixIndex;
- }
- int nextPrefixIndex = expressionString.indexOf(getExpressionPrefix(), prefixIndex
- + getExpressionPrefix().length());
- int suffixIndex;
- if (nextPrefixIndex == -1) {
- // this is the last expression in the expression string
- suffixIndex = expressionString.lastIndexOf(getExpressionSuffix());
- } else {
- // another expression exists after this one in the expression string
- suffixIndex = expressionString.lastIndexOf(getExpressionSuffix(), nextPrefixIndex);
- }
- if (suffixIndex < (prefixIndex + getExpressionPrefix().length())) {
- throw new ParserException(expressionString, "No ending suffix '" + getExpressionSuffix()
- + "' for expression starting at character " + prefixIndex + ": "
- + expressionString.substring(prefixIndex), null);
- } else if (suffixIndex == prefixIndex + getExpressionPrefix().length()) {
- throw new ParserException(expressionString, "No expression defined within delimiter '"
- + getExpressionPrefix() + getExpressionSuffix() + "' at character " + prefixIndex, null);
- } else {
- String expr = expressionString.substring(prefixIndex + getExpressionPrefix().length(), suffixIndex);
- expressions.add(doParseExpression(expr, context));
- startIdx = suffixIndex + 1;
- }
- } else {
- if (startIdx == 0) {
- // treat the entire string as one expression
- if (allowDelimitedEvalExpressions) {
- expressions.add(doParseExpression(expressionString, context));
- } else {
- // treat entire string as a literal
- expressions.add(new LiteralExpression(expressionString));
- }
- } else {
- // no more ${expressions} found in string, add rest as static text
- expressions.add(new LiteralExpression(expressionString.substring(startIdx)));
- }
- startIdx = expressionString.length();
- }
- }
- return (Expression[]) expressions.toArray(new Expression[expressions.size()]);
- }
-
- private Expression doParseExpression(String expressionString, ParserContext context) throws ParserException {
- if (context == null) {
- context = NullParserContext.INSTANCE;
- }
+public class OgnlExpressionParser extends AbstractExpressionParser {
+ protected Expression doParseExpression(String expressionString, ParserContext context) throws ParserException {
try {
return new OgnlExpression(Ognl.parseExpression(expressionString), parseVariableExpressions(context
.getExpressionVariables()), context.getExpectedEvaluationResultType(), expressionString);
@@ -249,16 +37,4 @@ public class OgnlExpressionParser implements ExpressionParser {
throw new ParserException(expressionString, e);
}
}
-
- private Map parseVariableExpressions(ExpressionVariable[] variables) throws OgnlException {
- if (variables == null || variables.length == 0) {
- return null;
- }
- Map variableExpressions = new HashMap(variables.length, 1);
- for (int i = 0; i < variables.length; i++) {
- ExpressionVariable var = variables[i];
- variableExpressions.put(var.getName(), parseExpression(var.getValueExpression(), var.getParserContext()));
- }
- return variableExpressions;
- }
}
\ No newline at end of file
diff --git a/spring-binding/src/main/java/org/springframework/binding/expression/support/AbstractExpressionParser.java b/spring-binding/src/main/java/org/springframework/binding/expression/support/AbstractExpressionParser.java
new file mode 100644
index 00000000..242c9b79
--- /dev/null
+++ b/spring-binding/src/main/java/org/springframework/binding/expression/support/AbstractExpressionParser.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.binding.expression.support;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+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.util.Assert;
+
+/**
+ * An expression parser that parses Ognl expressions.
+ *
+ * @author Keith Donald
+ */
+public abstract class AbstractExpressionParser implements ExpressionParser {
+
+ /**
+ * The expression prefix.
+ */
+ private static final String DEFAULT_EXPRESSION_PREFIX = "${";
+
+ /**
+ * The expression suffix.
+ */
+ private static final String DEFAULT_EXPRESSION_SUFFIX = "}";
+
+ /**
+ * The marked expression delimiter prefix.
+ */
+ private String expressionPrefix = DEFAULT_EXPRESSION_PREFIX;
+
+ /**
+ * The marked expression delimiter suffix.
+ */
+ private String expressionSuffix = DEFAULT_EXPRESSION_SUFFIX;
+
+ /**
+ * Should we allow delimited eval expressions like "${foo.bar}"? If not, evalutable expressions must not be enclosed
+ * in delimiters like ${foo.bar} else an exception is thrown. Only here for compatability reasons, as Web Flow 1.0
+ * allows delimited eval expressions while 2.x does not.
+ */
+ private boolean allowDelimitedEvalExpressions;
+
+ /**
+ * 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 allows delimited eval expressions like ${foo.bar}.
+ */
+ public boolean getAllowDelimitedEvalExpressions() {
+ return allowDelimitedEvalExpressions;
+ }
+
+ /**
+ * Sets if this parser allows eval expressions like ${foo.bar}.
+ */
+ public void setAllowDelimitedEvalExpressions(boolean allowDelmitedEvalExpressions) {
+ this.allowDelimitedEvalExpressions = allowDelmitedEvalExpressions;
+ }
+
+ // expression parser
+
+ public Expression parseExpression(String expressionString, ParserContext context) throws ParserException {
+ Assert.notNull(expressionString, "The expression string to parse is required");
+ if (context == null) {
+ context = NullParserContext.INSTANCE;
+ }
+ if (context.isTemplate()) {
+ return parseTemplate(expressionString, context);
+ } else {
+ if (expressionString.startsWith(getExpressionPrefix()) && expressionString.endsWith(getExpressionSuffix())) {
+ if (!allowDelimitedEvalExpressions) {
+ throw new ParserException(
+ expressionString,
+ "The expression '"
+ + expressionString
+ + "' being parsed is expected be a standard OGNL expression. Do not attempt to enclose such expression strings in ${} delimiters--this is redundant. If you need to parse a template that mixes literal text with evaluatable blocks, set the 'template' parser context attribute to true.",
+ null);
+ } else {
+ int lastIndex = expressionString.length() - getExpressionSuffix().length();
+ String ognlExpression = expressionString.substring(getExpressionPrefix().length(), lastIndex);
+ return doParseExpression(ognlExpression, context);
+ }
+ } else {
+ return doParseExpression(expressionString, context);
+ }
+ }
+ }
+
+ private Expression parseTemplate(String expressionString, ParserContext context) throws ParserException {
+ Assert.notNull(expressionString, "The expression string to parse is required");
+ if (expressionString.length() == 0) {
+ return parseEmptyExpressionString(context);
+ }
+ Expression[] expressions = parseExpressions(expressionString, context);
+ if (expressions.length == 1) {
+ return expressions[0];
+ } else {
+ return new CompositeStringExpression(expressions);
+ }
+ }
+
+ // helper methods
+
+ /**
+ * Helper that handles a empty expression string.
+ */
+ private Expression parseEmptyExpressionString(ParserContext context) {
+ if (allowDelimitedEvalExpressions) {
+ // let the parser handle it
+ return doParseExpression("", context);
+ } else {
+ // return a literal expression containing the empty string
+ return new LiteralExpression("");
+ }
+ }
+
+ /**
+ * Helper that parses given expression string using the configured parser. The expression string can contain any
+ * number of expressions all contained in "${...}" markers. For instance: "foo${expr0}bar${expr1}". The static
+ * pieces of text will also be returned as Expressions that just return that static piece of text. As a result,
+ * evaluating all returned expressions and concatenating the results produces the complete evaluated string.
+ * @param expressionString the expression string
+ * @return the parsed expressions
+ * @throws ParserException when the expressions cannot be parsed
+ */
+ private Expression[] parseExpressions(String expressionString, ParserContext context) throws ParserException {
+ List expressions = new LinkedList();
+ int startIdx = 0;
+ while (startIdx < expressionString.length()) {
+ int prefixIndex = expressionString.indexOf(getExpressionPrefix(), startIdx);
+ if (prefixIndex >= startIdx) {
+ // a inner expression was found - this is a composite
+ if (prefixIndex > startIdx) {
+ expressions.add(new LiteralExpression(expressionString.substring(startIdx, prefixIndex)));
+ startIdx = prefixIndex;
+ }
+ int nextPrefixIndex = expressionString.indexOf(getExpressionPrefix(), prefixIndex
+ + getExpressionPrefix().length());
+ int suffixIndex;
+ if (nextPrefixIndex == -1) {
+ // this is the last expression in the expression string
+ suffixIndex = expressionString.lastIndexOf(getExpressionSuffix());
+ } else {
+ // another expression exists after this one in the expression string
+ suffixIndex = expressionString.lastIndexOf(getExpressionSuffix(), nextPrefixIndex);
+ }
+ if (suffixIndex < (prefixIndex + getExpressionPrefix().length())) {
+ throw new ParserException(expressionString, "No ending suffix '" + getExpressionSuffix()
+ + "' for expression starting at character " + prefixIndex + ": "
+ + expressionString.substring(prefixIndex), null);
+ } else if (suffixIndex == prefixIndex + getExpressionPrefix().length()) {
+ throw new ParserException(expressionString, "No expression defined within delimiter '"
+ + getExpressionPrefix() + getExpressionSuffix() + "' at character " + prefixIndex, null);
+ } else {
+ String expr = expressionString.substring(prefixIndex + getExpressionPrefix().length(), suffixIndex);
+ expressions.add(doParseExpression(expr, context));
+ startIdx = suffixIndex + 1;
+ }
+ } else {
+ if (startIdx == 0) {
+ // treat the entire string as one expression
+ if (allowDelimitedEvalExpressions) {
+ expressions.add(doParseExpression(expressionString, context));
+ } else {
+ // treat entire string as a literal
+ expressions.add(new LiteralExpression(expressionString));
+ }
+ } else {
+ // no more ${expressions} found in string, add rest as static text
+ expressions.add(new LiteralExpression(expressionString.substring(startIdx)));
+ }
+ startIdx = expressionString.length();
+ }
+ }
+ return (Expression[]) expressions.toArray(new Expression[expressions.size()]);
+ }
+
+ protected Map parseVariableExpressions(ExpressionVariable[] variables) throws ParserException {
+ if (variables == null || variables.length == 0) {
+ return null;
+ }
+ Map variableExpressions = new HashMap(variables.length, 1);
+ for (int i = 0; i < variables.length; i++) {
+ ExpressionVariable var = variables[i];
+ variableExpressions.put(var.getName(), parseExpression(var.getValueExpression(), var.getParserContext()));
+ }
+ return variableExpressions;
+ }
+
+ protected abstract Expression doParseExpression(String expressionString, ParserContext context)
+ throws ParserException;
+
+}
\ No newline at end of file
diff --git a/spring-binding/src/main/java/org/springframework/binding/mapping/impl/DefaultMapping.java b/spring-binding/src/main/java/org/springframework/binding/mapping/impl/DefaultMapping.java
index 96613c03..5b15be97 100644
--- a/spring-binding/src/main/java/org/springframework/binding/mapping/impl/DefaultMapping.java
+++ b/spring-binding/src/main/java/org/springframework/binding/mapping/impl/DefaultMapping.java
@@ -17,7 +17,6 @@ package org.springframework.binding.mapping.impl;
import org.springframework.binding.convert.ConversionExecutionException;
import org.springframework.binding.convert.ConversionExecutor;
-import org.springframework.binding.convert.ConversionService;
import org.springframework.binding.expression.EvaluationException;
import org.springframework.binding.expression.Expression;
import org.springframework.binding.mapping.Mapping;
@@ -106,7 +105,7 @@ public class DefaultMapping implements Mapping {
* Execute this mapping.
* @param context the mapping context
*/
- void map(DefaultMappingContext context) {
+ public void map(DefaultMappingContext context) {
context.setCurrentMapping(this);
Object sourceValue;
try {
@@ -128,27 +127,6 @@ public class DefaultMapping implements Mapping {
context.setTypeConversionErrorResult(e);
return;
}
- } else {
- ConversionService conversionService = context.getConversionService();
- if (conversionService != null) {
- Class targetType;
- try {
- targetType = targetExpression.getValueType(context.getTarget());
- } catch (EvaluationException e) {
- context.setTargetAccessError(sourceValue, e);
- return;
- }
- if (targetType != null && !targetType.isInstance(targetValue)) {
- ConversionExecutor typeConverter = conversionService.getConversionExecutor(sourceValue
- .getClass(), targetType);
- try {
- targetValue = typeConverter.execute(sourceValue);
- } catch (ConversionExecutionException e) {
- context.setTypeConversionErrorResult(e);
- return;
- }
- }
- }
}
}
try {
diff --git a/spring-binding/src/main/java/org/springframework/binding/mapping/results/SourceAccessError.java b/spring-binding/src/main/java/org/springframework/binding/mapping/results/SourceAccessError.java
index 0727ed9d..9412b833 100644
--- a/spring-binding/src/main/java/org/springframework/binding/mapping/results/SourceAccessError.java
+++ b/spring-binding/src/main/java/org/springframework/binding/mapping/results/SourceAccessError.java
@@ -15,6 +15,9 @@
*/
package org.springframework.binding.mapping.results;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
import org.springframework.binding.expression.EvaluationException;
import org.springframework.binding.expression.PropertyNotFoundException;
import org.springframework.binding.mapping.Result;
@@ -65,7 +68,11 @@ public class SourceAccessError extends Result {
}
public String toString() {
- return new ToStringCreator(this).append("errorCode", getErrorCode()).append("details", error.getMessage())
- .toString();
+ ToStringCreator creator = new ToStringCreator(this).append("errorCode", getErrorCode());
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter writer = new PrintWriter(stringWriter);
+ error.printStackTrace(writer);
+ creator.append("exceptionStackTrace", stringWriter.toString());
+ return creator.toString();
}
}
diff --git a/spring-binding/src/main/java/org/springframework/binding/mapping/results/TargetAccessError.java b/spring-binding/src/main/java/org/springframework/binding/mapping/results/TargetAccessError.java
index 4e0a2df4..e0685950 100644
--- a/spring-binding/src/main/java/org/springframework/binding/mapping/results/TargetAccessError.java
+++ b/spring-binding/src/main/java/org/springframework/binding/mapping/results/TargetAccessError.java
@@ -15,6 +15,9 @@
*/
package org.springframework.binding.mapping.results;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
import org.springframework.binding.expression.EvaluationException;
import org.springframework.binding.expression.PropertyNotFoundException;
import org.springframework.binding.mapping.Result;
@@ -69,7 +72,11 @@ public class TargetAccessError extends Result {
}
public String toString() {
- return new ToStringCreator(this).append("errorCode", getErrorCode()).append("details", error.getMessage())
- .toString();
+ ToStringCreator creator = new ToStringCreator(this).append("errorCode", getErrorCode());
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter writer = new PrintWriter(stringWriter);
+ error.printStackTrace(writer);
+ creator.append("stackTrace", stringWriter.toString());
+ return creator.toString();
}
}
diff --git a/spring-binding/src/main/java/org/springframework/binding/mapping/results/TypeConversionError.java b/spring-binding/src/main/java/org/springframework/binding/mapping/results/TypeConversionError.java
index 30100999..32593483 100644
--- a/spring-binding/src/main/java/org/springframework/binding/mapping/results/TypeConversionError.java
+++ b/spring-binding/src/main/java/org/springframework/binding/mapping/results/TypeConversionError.java
@@ -15,6 +15,9 @@
*/
package org.springframework.binding.mapping.results;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
import org.springframework.binding.convert.ConversionExecutionException;
import org.springframework.binding.mapping.Result;
import org.springframework.core.style.ToStringCreator;
@@ -73,7 +76,12 @@ public class TypeConversionError extends Result {
}
public String toString() {
- return new ToStringCreator(this).append("originalValue", originalValue).append("targetType", targetType)
- .append("details", exception.getMessage()).toString();
+ ToStringCreator creator = new ToStringCreator(this).append("originalValue", originalValue).append("targetType",
+ targetType);
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter writer = new PrintWriter(stringWriter);
+ exception.printStackTrace(writer);
+ creator.append("exceptionStackTrace", stringWriter.toString());
+ return creator.toString();
}
}
diff --git a/spring-binding/src/test/java/org/springframework/binding/convert/service/DefaultConversionServiceTests.java b/spring-binding/src/test/java/org/springframework/binding/convert/service/DefaultConversionServiceTests.java
index 3101f48f..024a5db0 100644
--- a/spring-binding/src/test/java/org/springframework/binding/convert/service/DefaultConversionServiceTests.java
+++ b/spring-binding/src/test/java/org/springframework/binding/convert/service/DefaultConversionServiceTests.java
@@ -208,87 +208,357 @@ public class DefaultConversionServiceTests extends TestCase {
assertEquals("1,2,3", result);
}
- /*
- *
- * public void testArrayListConversionWithElementConversion() throws Exception { DefaultConversionService service =
- * new DefaultConversionService(); ConversionExecutor executor = service.getConversionExecutor(String[].class,
- * IntegerArrayList.class); List result = (List) executor.execute(new String[] { "1", "2", "3" }); assertEquals(new
- * Integer(1), result.get(0)); assertEquals(new Integer(2), result.get(1)); assertEquals(new Integer(3),
- * result.get(2)); }
- *
- *
- * public static class IntegerArrayList implements List {
- *
- * private ArrayList realList = new ArrayList();
- *
- * public IntegerArrayList() { }
- *
- * public void add(int index, Integer element) { // TODO Auto-generated method stub throw new
- * UnsupportedOperationException("Auto-generated method stub"); }
- *
- * public boolean add(Integer o) { return realList.add(o); }
- *
- * public boolean addAll(Collection extends Integer> c) { // TODO Auto-generated method stub throw new
- * UnsupportedOperationException("Auto-generated method stub"); }
- *
- * public boolean addAll(int index, Collection extends Integer> c) { // TODO Auto-generated method stub throw new
- * UnsupportedOperationException("Auto-generated method stub"); }
- *
- * public void clear() { // TODO Auto-generated method stub throw new UnsupportedOperationException("Auto-generated
- * method stub"); }
- *
- * public boolean contains(Object o) { // TODO Auto-generated method stub throw new
- * UnsupportedOperationException("Auto-generated method stub"); }
- *
- * public boolean containsAll(Collection> c) { // TODO Auto-generated method stub throw new
- * UnsupportedOperationException("Auto-generated method stub"); }
- *
- * public Integer get(int index) { return (Integer) realList.get(index); }
- *
- * public int indexOf(Object o) { // TODO Auto-generated method stub throw new
- * UnsupportedOperationException("Auto-generated method stub"); }
- *
- * public boolean isEmpty() { // TODO Auto-generated method stub throw new
- * UnsupportedOperationException("Auto-generated method stub"); }
- *
- * public Iterator iterator() { // TODO Auto-generated method stub throw new
- * UnsupportedOperationException("Auto-generated method stub"); }
- *
- * public int lastIndexOf(Object o) { // TODO Auto-generated method stub throw new
- * UnsupportedOperationException("Auto-generated method stub"); }
- *
- * public ListIterator listIterator() { // TODO Auto-generated method stub throw new
- * UnsupportedOperationException("Auto-generated method stub"); }
- *
- * public ListIterator listIterator(int index) { // TODO Auto-generated method stub throw new
- * UnsupportedOperationException("Auto-generated method stub"); }
- *
- * public Integer remove(int index) { // TODO Auto-generated method stub throw new
- * UnsupportedOperationException("Auto-generated method stub"); }
- *
- * public boolean remove(Object o) { // TODO Auto-generated method stub throw new
- * UnsupportedOperationException("Auto-generated method stub"); }
- *
- * public boolean removeAll(Collection> c) { // TODO Auto-generated method stub throw new
- * UnsupportedOperationException("Auto-generated method stub"); }
- *
- * public boolean retainAll(Collection> c) { // TODO Auto-generated method stub throw new
- * UnsupportedOperationException("Auto-generated method stub"); }
- *
- * public Integer set(int index, Integer element) { // TODO Auto-generated method stub throw new
- * UnsupportedOperationException("Auto-generated method stub"); }
- *
- * public int size() { // TODO Auto-generated method stub throw new UnsupportedOperationException("Auto-generated
- * method stub"); }
- *
- * public List subList(int fromIndex, int toIndex) { // TODO Auto-generated method stub throw new
- * UnsupportedOperationException("Auto-generated method stub"); }
- *
- * public Object[] toArray() { // TODO Auto-generated method stub throw new
- * UnsupportedOperationException("Auto-generated method stub"); }
- *
- * public T[] toArray(T[] a) { // TODO Auto-generated method stub throw new
- * UnsupportedOperationException("Auto-generated method stub"); } }
- */
+ // public void testGenericTypeConversionOGNL() {
+ // DefaultConversionService service = new DefaultConversionService();
+ // Map map = new HashMap();
+ // map.put("stringArray", new String[] { "1", "2", "3" });
+ // map.put("string", "1");
+ // TestBean bean = new TestBean();
+ // OgnlExpressionParser parser = new OgnlExpressionParser();
+ // Expression stringArray = parser.parseExpression("stringArray", null);
+ // Expression integerList = parser.parseExpression("integerList", null);
+ // DefaultMappingContext context = new DefaultMappingContext(map, bean, service);
+ // DefaultMapping mapping = new DefaultMapping(stringArray, integerList);
+ // mapping.map(context);
+ // assertEquals(new Integer(1), bean.getIntegerList().get(0));
+ // assertEquals(new Integer(2), bean.getIntegerList().get(1));
+ // assertEquals(new Integer(3), bean.getIntegerList().get(2));
+ // }
+ //
+ // public void testGenericTypeConversionBeanWrapper() {
+ // DefaultConversionService service = new DefaultConversionService();
+ // Map map = new HashMap();
+ // map.put("stringArray", new String[] { "1", "2", "3" });
+ // map.put("string", "1");
+ // CollectionWrapperBean wrapper = new CollectionWrapperBean(map);
+ // TestBean bean = new TestBean();
+ // BeanWrapperExpressionParser parser = new BeanWrapperExpressionParser();
+ // Expression stringArray = parser.parseExpression("map[stringArray]", null);
+ // Expression integerList = parser.parseExpression("integerList", null);
+ // DefaultMappingContext context = new DefaultMappingContext(wrapper, bean, service);
+ // DefaultMapping mapping = new DefaultMapping(stringArray, integerList);
+ // mapping.map(context);
+ // assertEquals(new Integer(1), bean.getIntegerList().get(0));
+ // assertEquals(new Integer(2), bean.getIntegerList().get(1));
+ // assertEquals(new Integer(3), bean.getIntegerList().get(2));
+ // }
+ //
+ // public void testGenericTypeConversionBeanWrapperWithCustomConversion() {
+ // DefaultConversionService service = new DefaultConversionService();
+ // service.addConverter(new StringToAuthority());
+ // Map map = new HashMap();
+ // map.put("authorityArray", new String[] { "keith", "keri", "annabelle" });
+ // CollectionWrapperBean wrapper = new CollectionWrapperBean(map);
+ // TestBean bean = new TestBean();
+ // BeanWrapperExpressionParser parser = new BeanWrapperExpressionParser();
+ // parser.setConversionService(service);
+ // Expression authorityArray = parser.parseExpression("map[authorityArray]", null);
+ // Expression authorityList = parser.parseExpression("authorityList", null);
+ // DefaultMappingContext context = new DefaultMappingContext(wrapper, bean, service);
+ // DefaultMapping mapping = new DefaultMapping(authorityArray, authorityList);
+ // mapping.map(context);
+ // assertEquals(new Authority("keith"), bean.getAuthorityList().get(0));
+ // assertEquals(new Authority("keri"), bean.getAuthorityList().get(1));
+ // assertEquals(new Authority("annabelle"), bean.getAuthorityList().get(2));
+ // }
+ //
+ // public void testGenericTypeConversionBeanWrapperWithNestedCustomConversion() {
+ // DefaultConversionService service = new DefaultConversionService();
+ // service.addConverter(new StringToAuthority());
+ // Map map = new HashMap();
+ // map.put("foo", "keith");
+ // CollectionWrapperBean wrapper = new CollectionWrapperBean(map);
+ // TestBean bean = new TestBean();
+ // BeanWrapperExpressionParser parser = new BeanWrapperExpressionParser();
+ // parser.setConversionService(service);
+ // Expression keith = parser.parseExpression("map[foo]", null);
+ // Expression nestedMapEntry = parser.parseExpression("nested[0][0]", null);
+ // DefaultMappingContext context = new DefaultMappingContext(wrapper, bean, service);
+ // DefaultMapping mapping = new DefaultMapping(keith, nestedMapEntry);
+ // mapping.map(context);
+ // assertEquals(new Authority("keith"), bean.getNested().get(0).get(0));
+ // }
+ //
+ // public void testGenericTypeConversionEL() {
+ // DefaultConversionService service = new DefaultConversionService();
+ // Map map = new HashMap();
+ // map.put("stringArray", new String[] { "1", "2", "3" });
+ // map.put("string", "1");
+ // TestBean bean = new TestBean();
+ // ELExpressionParser parser = new ELExpressionParser(new ExpressionFactoryImpl());
+ // Expression stringArray = parser.parseExpression("stringArray", null);
+ // Expression integerList = parser.parseExpression("integerList", null);
+ // DefaultMappingContext context = new DefaultMappingContext(map, bean, service);
+ // DefaultMapping mapping = new DefaultMapping(stringArray, integerList);
+ // mapping.map(context);
+ // assertEquals(new Integer(1), bean.getIntegerList().get(0));
+ // assertEquals(new Integer(2), bean.getIntegerList().get(1));
+ // assertEquals(new Integer(3), bean.getIntegerList().get(2));
+ // }
+ //
+ // public void testArrayConversionOGNL() {
+ // DefaultConversionService service = new DefaultConversionService();
+ // Map map = new HashMap();
+ // map.put("integerArray", new Integer[] { 1, 2, 3 });
+ // map.put("string", "1");
+ // TestBean bean = new TestBean();
+ // OgnlExpressionParser parser = new OgnlExpressionParser();
+ // Expression stringArray = parser.parseExpression("integerArray", null);
+ // Expression integerList = parser.parseExpression("primitiveArray", null);
+ // DefaultMappingContext context = new DefaultMappingContext(map, bean, service);
+ // DefaultMapping mapping = new DefaultMapping(stringArray, integerList);
+ // mapping.map(context);
+ // }
+ //
+ // public void testArrayConversionEL() {
+ // DefaultConversionService service = new DefaultConversionService();
+ // Map map = new HashMap();
+ // map.put("stringArray", new String[] { "1", "2", "3" });
+ // map.put("string", "1");
+ // TestBean bean = new TestBean();
+ // ELExpressionParser parser = new ELExpressionParser(new ExpressionFactoryImpl());
+ // Expression stringArray = parser.parseExpression("stringArray", null);
+ // Expression integerList = parser.parseExpression("primitiveArray", null);
+ // DefaultMappingContext context = new DefaultMappingContext(map, bean, service);
+ // DefaultMapping mapping = new DefaultMapping(stringArray, integerList);
+ // mapping.map(context);
+ // }
+ //
+ // public static class CollectionWrapperBean {
+ //
+ // private Collection collection;
+ //
+ // private Map map;
+ //
+ // public CollectionWrapperBean(Map map) {
+ // this.map = map;
+ // }
+ //
+ // public CollectionWrapperBean(Collection collection) {
+ // this.collection = collection;
+ // }
+ //
+ // public Collection getCollection() {
+ // return collection;
+ // }
+ //
+ // public Map getMap() {
+ // return map;
+ // }
+ // }
+ //
+ // public static class TestBean {
+ //
+ // private List integerList;
+ //
+ // private int primitive;
+ //
+ // private int[] primitiveArray;
+ //
+ // private List authorityList;
+ //
+ // private List