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 1f4d8111..ac2a346f 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
@@ -26,12 +26,21 @@ package org.springframework.binding.convert;
public interface ConversionService {
/**
- * Return the default conversion executor capable of converting source objects of the specified
- * sourceClass to instances of the targetClass.
+ * Execute a conversion of the source to the specified target class.
+ * @param source the source to convert from (may be null)
+ * @param targetClass the target class to convert to
+ * @return the converted object
+ * @throws ConversionException if an exception occurred during the conversion process
+ */
+ public Object executeConversion(Object source, Class targetClass) throws ConversionException;
+
+ /**
+ * Return the conversion executor capable of converting source objects of the specified sourceClass
+ * to instances of the targetClass.
*
* The returned ConversionExecutor is thread-safe and may safely be cached for use in client code. - * @param sourceClass the source class to convert from - * @param targetClass the target class to convert to + * @param sourceClass the source class to convert from (required) + * @param targetClass the target class to convert to (required) * @return the executor that can execute instance type conversion, never null * @throws ConversionExecutorNotFoundException when no suitable conversion executor could be found */ 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 d4845585..3157a158 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 @@ -26,6 +26,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.springframework.binding.convert.ConversionException; import org.springframework.binding.convert.ConversionExecutor; import org.springframework.binding.convert.ConversionExecutorNotFoundException; import org.springframework.binding.convert.ConversionService; @@ -163,6 +164,15 @@ public class GenericConversionService implements ConversionService { } } + public Object executeConversion(Object source, Class targetClass) throws ConversionException { + if (source != null) { + ConversionExecutor conversionExecutor = getConversionExecutor(source.getClass(), targetClass); + return conversionExecutor.execute(source); + } else { + return null; + } + } + public Class getClassForAlias(String name) throws IllegalArgumentException { Class clazz = (Class) aliasMap.get(name); if (clazz != null) { 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 index 7ab4a407..b5f26889 100644 --- 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 @@ -19,6 +19,7 @@ import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.propertyeditors.PropertiesEditor; import org.springframework.binding.convert.ConversionExecutor; import org.springframework.binding.convert.ConversionService; +import org.springframework.binding.convert.service.DefaultConversionService; import org.springframework.binding.expression.Expression; import org.springframework.binding.expression.ParserContext; import org.springframework.binding.expression.ParserException; @@ -31,13 +32,13 @@ import org.springframework.binding.expression.support.AbstractExpressionParser; */ public class BeanWrapperExpressionParser extends AbstractExpressionParser { - private ConversionService conversionService; + private ConversionService conversionService = new DefaultConversionService(); /** * The conversion service to use to obtain {@link ConversionExecutor conversion executors} that will be adapted to * {@link PropertiesEditor property editors} for use during a - * {@link BeanWrapperImpl#setPropertyValue(String, Object) set value} call. - * @return the conversion service + * {@link BeanWrapperImpl#setPropertyValue(String, Object) set value} call. The default if not specified is an + * instance of {@link DefaultConversionService}. */ public ConversionService getConversionService() { return conversionService; 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 c3292039..5d91b428 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 @@ -44,15 +44,12 @@ import org.springframework.util.Assert; */ public class ELExpressionParser implements ExpressionParser { - /** - * The ExpressionFactory for constructing EL expressions - */ private ExpressionFactory expressionFactory; - private ConversionService conversionService = new DefaultConversionService(); - private Map contextFactories = new HashMap(); + private ConversionService conversionService = new DefaultConversionService(); + /** * Creates a new EL expression parser for standalone usage. */ @@ -60,6 +57,22 @@ public class ELExpressionParser implements ExpressionParser { init(expressionFactory); } + /** + * The conversion service to use to perform type conversions as needed by the Unified EL system. If not specified, + * the default is an instance of {@link DefaultConversionService}. + */ + public ConversionService getConversionService() { + return conversionService; + } + + /** + * Sets the conversion service to use to perform type conversions as needed by the Unified EL system. + * @param conversionService the conversion service to use + */ + public void setConversionService(ConversionService conversionService) { + this.conversionService = conversionService; + } + /** * Register the ELContextFactory for expressions that evaluate the given class of context object. * @param contextType the expression context class 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 d70afd59..de25f56c 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 @@ -15,6 +15,7 @@ */ package org.springframework.binding.expression.ognl; +import java.lang.reflect.Member; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -23,10 +24,12 @@ import java.util.Map; import ognl.NoSuchPropertyException; import ognl.Ognl; import ognl.OgnlException; +import ognl.TypeConverter; import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.BeansException; import org.springframework.beans.InvalidPropertyException; +import org.springframework.binding.convert.ConversionService; import org.springframework.binding.expression.EvaluationException; import org.springframework.binding.expression.Expression; import org.springframework.binding.expression.PropertyNotFoundException; @@ -46,14 +49,18 @@ class OgnlExpression implements Expression { private String expressionString; + private ConversionService conversionService; + /** * Creates a new OGNL expression. */ - public OgnlExpression(Object expression, Map variableExpressions, Class expectedResultType, String expressionString) { + public OgnlExpression(Object expression, Map variableExpressions, Class expectedResultType, + String expressionString, ConversionService conversionService) { this.expression = expression; this.variableExpressions = variableExpressions; this.expectedResultType = expectedResultType; this.expressionString = expressionString; + this.conversionService = conversionService; } public boolean equals(Object o) { @@ -84,7 +91,7 @@ class OgnlExpression implements Expression { public void setValue(Object context, Object value) { try { Map evaluationContext = Ognl.addDefaultContext(context, getVariables(context)); - // TODO set TypeConverter here to invoke our ConversionService + Ognl.setTypeConverter(evaluationContext, createTypeConverter()); Ognl.setValue(expression, evaluationContext, context, value); } catch (NoSuchPropertyException e) { throw new PropertyNotFoundException(context.getClass(), getExpressionString(), e); @@ -95,6 +102,15 @@ class OgnlExpression implements Expression { } } + private TypeConverter createTypeConverter() { + return new TypeConverter() { + public Object convertValue(Map context, Object target, Member member, String propertyName, Object value, + Class toType) { + return conversionService.executeConversion(value, toType); + } + }; + } + public Class getValueType(Object context) { try { // OGNL has no native way to get this information 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 38c660c8..7fb28a5c 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 @@ -20,6 +20,8 @@ import ognl.OgnlException; import ognl.OgnlRuntime; import ognl.PropertyAccessor; +import org.springframework.binding.convert.ConversionService; +import org.springframework.binding.convert.service.DefaultConversionService; import org.springframework.binding.expression.Expression; import org.springframework.binding.expression.ParserContext; import org.springframework.binding.expression.ParserException; @@ -32,6 +34,24 @@ import org.springframework.binding.expression.support.AbstractExpressionParser; */ public class OgnlExpressionParser extends AbstractExpressionParser { + private ConversionService conversionService = new DefaultConversionService(); + + /** + * The conversion service to use to perform type conversions as needed by the OGNL system. If not specified, the + * default is an instance of {@link DefaultConversionService}. + */ + public ConversionService getConversionService() { + return conversionService; + } + + /** + * Sets the conversion service to use to perform type conversions as needed by the OGNL system. + * @param conversionService the conversion service to use + */ + public void setConversionService(ConversionService conversionService) { + this.conversionService = conversionService; + } + /** * Add a property access strategy for the given class. * @param clazz the class that contains properties needing access @@ -44,7 +64,8 @@ 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); + .getExpressionVariables()), context.getExpectedEvaluationResultType(), expressionString, + conversionService); } catch (OgnlException e) { throw new ParserException(expressionString, e); } diff --git a/spring-binding/src/main/java/org/springframework/binding/mapping/impl/DefaultMapper.java b/spring-binding/src/main/java/org/springframework/binding/mapping/impl/DefaultMapper.java index 36efeae7..ddd00df0 100644 --- a/spring-binding/src/main/java/org/springframework/binding/mapping/impl/DefaultMapper.java +++ b/spring-binding/src/main/java/org/springframework/binding/mapping/impl/DefaultMapper.java @@ -40,23 +40,6 @@ public class DefaultMapper implements Mapper { private List mappings = new ArrayList(); - private ConversionService conversionService; - - /** - * Returns the conversion service to use to perform type conversions as needed by mapping operations. May be null. - */ - public ConversionService getConversionService() { - return conversionService; - } - - /** - * Sets the conversion service to use to perform type conversions as needed by mapping operations. Optional. - * @param conversionService the conversion service; - */ - public void setConversionService(ConversionService conversionService) { - this.conversionService = conversionService; - } - /** * Add a mapping to this mapper. * @param mapping the mapping to add (required) @@ -80,7 +63,7 @@ public class DefaultMapper implements Mapper { logger.debug("Beginning mapping between source [" + source.getClass().getName() + "] and target [" + target.getClass().getName() + "]"); } - DefaultMappingContext context = new DefaultMappingContext(source, target, conversionService); + DefaultMappingContext context = new DefaultMappingContext(source, target); Iterator it = mappings.iterator(); while (it.hasNext()) { DefaultMapping mapping = (DefaultMapping) it.next(); diff --git a/spring-binding/src/main/java/org/springframework/binding/mapping/impl/DefaultMappingContext.java b/spring-binding/src/main/java/org/springframework/binding/mapping/impl/DefaultMappingContext.java index a39ff302..5e8fcd4d 100644 --- a/spring-binding/src/main/java/org/springframework/binding/mapping/impl/DefaultMappingContext.java +++ b/spring-binding/src/main/java/org/springframework/binding/mapping/impl/DefaultMappingContext.java @@ -21,7 +21,6 @@ import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.binding.convert.ConversionExecutionException; -import org.springframework.binding.convert.ConversionService; import org.springframework.binding.expression.EvaluationException; import org.springframework.binding.mapping.Mapping; import org.springframework.binding.mapping.MappingResult; @@ -48,12 +47,9 @@ public class DefaultMappingContext { private List mappingResults; - private ConversionService conversionService; - - public DefaultMappingContext(Object source, Object target, ConversionService conversionService) { + public DefaultMappingContext(Object source, Object target) { this.source = source; this.target = target; - this.conversionService = conversionService; this.mappingResults = new ArrayList(); } @@ -71,14 +67,6 @@ public class DefaultMappingContext { return target; } - /** - * Returns the conversion service that can be used to perform type conversions during the mapping process. May be - * null if no externally managed conversion service is provided. - */ - public ConversionService getConversionService() { - return conversionService; - } - /** * Returns the current mapping. * @return the current mapping diff --git a/spring-binding/src/test/java/org/springframework/binding/expression/ognl/OgnlExpressionParserTests.java b/spring-binding/src/test/java/org/springframework/binding/expression/ognl/OgnlExpressionParserTests.java index 7c196b18..5005e162 100644 --- a/spring-binding/src/test/java/org/springframework/binding/expression/ognl/OgnlExpressionParserTests.java +++ b/spring-binding/src/test/java/org/springframework/binding/expression/ognl/OgnlExpressionParserTests.java @@ -161,4 +161,11 @@ public class OgnlExpressionParserTests extends TestCase { assertEquals(boolean.class, e.getValueType(bean)); } + public void testSetComplexDateType() { + String value = "2008-07-04"; + Expression e = parser.parseExpression("date", null); + e.setValue(bean, value); + assertEquals(1215144000000L, bean.getDate().getTime()); + } + } \ No newline at end of file diff --git a/spring-binding/src/test/java/org/springframework/binding/expression/ognl/TestBean.java b/spring-binding/src/test/java/org/springframework/binding/expression/ognl/TestBean.java index d95697a8..7c30d101 100644 --- a/spring-binding/src/test/java/org/springframework/binding/expression/ognl/TestBean.java +++ b/spring-binding/src/test/java/org/springframework/binding/expression/ognl/TestBean.java @@ -16,6 +16,7 @@ package org.springframework.binding.expression.ognl; import java.util.ArrayList; +import java.util.Date; import java.util.List; public class TestBean { @@ -24,6 +25,8 @@ public class TestBean { private int number; + private Date date; + private List list = new ArrayList(); public boolean isFlag() { @@ -49,4 +52,13 @@ public class TestBean { public void setList(List list) { this.list = list; } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + } diff --git a/spring-binding/src/test/java/org/springframework/binding/mapping/DefaultMapperTests.java b/spring-binding/src/test/java/org/springframework/binding/mapping/DefaultMapperTests.java index e7ab5c29..585a4ef3 100644 --- a/spring-binding/src/test/java/org/springframework/binding/mapping/DefaultMapperTests.java +++ b/spring-binding/src/test/java/org/springframework/binding/mapping/DefaultMapperTests.java @@ -4,11 +4,9 @@ import java.util.Locale; import junit.framework.TestCase; -import org.springframework.binding.convert.service.DefaultConversionService; import org.springframework.binding.expression.ExpressionParser; import org.springframework.binding.expression.el.DefaultExpressionFactoryUtils; import org.springframework.binding.expression.el.ELExpressionParser; -import org.springframework.binding.expression.support.FluentParserContext; import org.springframework.binding.mapping.impl.DefaultMapper; import org.springframework.binding.mapping.impl.DefaultMapping; @@ -45,38 +43,6 @@ public class DefaultMapperTests extends TestCase { }).size()); } - public void testMappingWithAutomaticConversion() { - DefaultMapping mapping1 = new DefaultMapping(parser.parseExpression("foo", new FluentParserContext() - .expectResult(Integer.class)), parser.parseExpression("boop", null)); - mapper.addMapping(mapping1); - TestBean bean1 = new TestBean(); - bean1.foo = "12345"; - TestBean2 bean2 = new TestBean2(); - MappingResults results = mapper.map(bean1, bean2); - assertSame(bean1, results.getSource()); - assertSame(bean2, results.getTarget()); - assertEquals(1, results.getAllResults().size()); - assertEquals(0, results.getErrorResults().size()); - assertEquals(new Integer(12345), bean2.boop); - } - - public void testMappingWithCustomConversionService() { - DefaultConversionService conversionService = new DefaultConversionService(); - mapper.setConversionService(conversionService); - DefaultMapping mapping1 = new DefaultMapping(parser.parseExpression("foo", null), parser.parseExpression( - "beep", null)); - mapper.addMapping(mapping1); - TestBean bean1 = new TestBean(); - bean1.foo = "en"; - TestBean2 bean2 = new TestBean2(); - MappingResults results = mapper.map(bean1, bean2); - assertSame(bean1, results.getSource()); - assertSame(bean2, results.getTarget()); - assertEquals(1, results.getAllResults().size()); - assertEquals(0, results.getErrorResults().size()); - assertEquals(Locale.ENGLISH, bean2.beep); - } - public static class TestBean { private String foo;