sourceClass. For
- * example, getConversionExecutor(String.class) would return all converters that convert from String to
- * some other Object. Mainly useful for adapting a set of converters to some other environment.
- * @param sourceClass the source class converting from
- * @return the conversion executors that can convert from that source class
- */
- public Set getConversionExecutors(Class sourceClass);
-
/**
* Lookup a class by its well-known alias. For example, long for java.lang.Long
* @param alias the class alias
@@ -90,4 +80,11 @@ public interface ConversionService {
*/
public Class getClassForAlias(String alias);
+ /**
+ * Return the underlying Spring ConversionService.
+ *
+ * @return the conversion service
+ */
+ public org.springframework.core.convert.ConversionService getDelegateConversionService();
+
}
\ No newline at end of file
diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/SpringConvertingConverterAdapter.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/SpringConvertingConverterAdapter.java
new file mode 100644
index 00000000..51663417
--- /dev/null
+++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/SpringConvertingConverterAdapter.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2004-2010 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.convert.converters;
+
+import org.springframework.core.convert.ConversionService;
+import org.springframework.util.Assert;
+
+/**
+ * A Spring Binding Converter that delegates to a Spring {@link ConversionService} to do the actual type conversion.
+ *
+ * @author Rossen Stoyanchev
+ */
+public class SpringConvertingConverterAdapter implements Converter {
+
+ /**
+ * The source value type to convert from.
+ */
+ private final Class sourceClass;
+
+ /**
+ * The target value type to convert to.
+ */
+ private final Class targetClass;
+
+ /**
+ * The ConversionService that will perform the conversion.
+ */
+ private ConversionService conversionService;
+
+ public SpringConvertingConverterAdapter(Class sourceClass, Class targetClass, ConversionService conversionService) {
+ Assert.notNull(sourceClass, "The source class to convert from is required.");
+ Assert.notNull(targetClass, "The target class to convert to is required.");
+ Assert.notNull(conversionService, "A Spring ConversionService is required.");
+ this.sourceClass = sourceClass;
+ this.targetClass = targetClass;
+ this.conversionService = conversionService;
+ }
+
+ public Object convertSourceToTargetClass(Object source, Class targetClass) throws Exception {
+ return conversionService.convert(source, targetClass);
+ }
+
+ public Class getSourceClass() {
+ return sourceClass;
+ }
+
+ public Class getTargetClass() {
+ return targetClass;
+ }
+
+}
diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/service/DefaultConversionService.java b/spring-binding/src/main/java/org/springframework/binding/convert/service/DefaultConversionService.java
index 04810f18..91e0e944 100644
--- a/spring-binding/src/main/java/org/springframework/binding/convert/service/DefaultConversionService.java
+++ b/spring-binding/src/main/java/org/springframework/binding/convert/service/DefaultConversionService.java
@@ -20,25 +20,8 @@ import java.math.BigInteger;
import java.util.Date;
import java.util.Locale;
-import org.springframework.binding.convert.converters.CollectionToCollection;
-import org.springframework.binding.convert.converters.NumberToNumber;
-import org.springframework.binding.convert.converters.ObjectToCollection;
-import org.springframework.binding.convert.converters.StringToBigDecimal;
-import org.springframework.binding.convert.converters.StringToBigInteger;
-import org.springframework.binding.convert.converters.StringToBoolean;
-import org.springframework.binding.convert.converters.StringToByte;
-import org.springframework.binding.convert.converters.StringToCharacter;
-import org.springframework.binding.convert.converters.StringToDate;
-import org.springframework.binding.convert.converters.StringToDouble;
-import org.springframework.binding.convert.converters.StringToEnum;
-import org.springframework.binding.convert.converters.StringToFloat;
-import org.springframework.binding.convert.converters.StringToInteger;
-import org.springframework.binding.convert.converters.StringToLabeledEnum;
-import org.springframework.binding.convert.converters.StringToLocale;
-import org.springframework.binding.convert.converters.StringToLong;
-import org.springframework.binding.convert.converters.StringToShort;
+import org.springframework.core.convert.ConversionService;
import org.springframework.core.enums.LabeledEnum;
-import org.springframework.util.ClassUtils;
/**
* Default, local implementation of a conversion service. Will automatically register from string converters for
@@ -56,29 +39,26 @@ public class DefaultConversionService extends GenericConversionService {
addDefaultAliases();
}
+ /**
+ * Creates a new default conversion service with an instance of a Spring ConversionService.
+ *
+ * @param delegateConversionService the Spring conversion service
+ */
+ public DefaultConversionService(ConversionService delegateConversionService) {
+ super(delegateConversionService);
+ addDefaultConverters();
+ addDefaultAliases();
+ }
+
/**
* Add all default converters to the conversion service.
+ *
+ * Note: Staring with Spring Web Flow 2.1, this method does not register any Spring Binding converters. All type
+ * conversion is driven through Spring's type conversion instead.
+ *
+ * @see GenericConversionService
*/
protected void addDefaultConverters() {
- addConverter(new StringToByte());
- addConverter(new StringToBoolean());
- addConverter(new StringToCharacter());
- addConverter(new StringToShort());
- addConverter(new StringToInteger());
- addConverter(new StringToLong());
- addConverter(new StringToFloat());
- addConverter(new StringToDouble());
- addConverter(new StringToBigInteger());
- addConverter(new StringToBigDecimal());
- addConverter(new StringToLocale());
- addConverter(new StringToDate());
- addConverter(new StringToLabeledEnum());
- addConverter(new NumberToNumber());
- addConverter(new ObjectToCollection(this));
- addConverter(new CollectionToCollection(this));
- if (ClassUtils.isPresent("java.lang.Enum", this.getClass().getClassLoader())) {
- addConverter(new StringToEnum());
- }
}
protected void addDefaultAliases() {
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 5093a0e1..9221b054 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
@@ -17,13 +17,8 @@ package org.springframework.binding.convert.service;
import java.lang.reflect.Modifier;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
import java.util.Map;
-import java.util.Set;
import org.springframework.binding.convert.ConversionException;
import org.springframework.binding.convert.ConversionExecutor;
@@ -36,7 +31,10 @@ import org.springframework.binding.convert.converters.Converter;
import org.springframework.binding.convert.converters.ObjectToArray;
import org.springframework.binding.convert.converters.ObjectToCollection;
import org.springframework.binding.convert.converters.ReverseConverter;
+import org.springframework.binding.convert.converters.SpringConvertingConverterAdapter;
import org.springframework.binding.convert.converters.TwoWayConverter;
+import org.springframework.core.convert.converter.ConverterRegistry;
+import org.springframework.format.support.FormattingConversionServiceFactoryBean;
import org.springframework.util.Assert;
/**
@@ -47,11 +45,9 @@ import org.springframework.util.Assert;
public class GenericConversionService implements ConversionService {
/**
- * 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 converted to, ultimately mapping to a specific converter that can perform
- * the source->target conversion.
+ * Spring ConversionService where existing custom {@link Converter} types will be registered through an adapter.
*/
- private final Map sourceClassConverters = new HashMap();
+ private org.springframework.core.convert.ConversionService delegate;
/**
* A map of custom converters. Custom converters are assigned a unique identifier that can be used to lookup the
@@ -69,6 +65,24 @@ public class GenericConversionService implements ConversionService {
*/
private ConversionService parent;
+ /**
+ * Default constructor.
+ */
+ public GenericConversionService() {
+ FormattingConversionServiceFactoryBean factoryBean = new FormattingConversionServiceFactoryBean();
+ factoryBean.afterPropertiesSet();
+ this.delegate = factoryBean.getObject();
+ }
+
+ /**
+ * Constructor accepting a specific instance of a Spring ConversionService to delegate to.
+ * @param delegateConversionService the conversion service
+ */
+ public GenericConversionService(org.springframework.core.convert.ConversionService delegateConversionService) {
+ Assert.notNull(delegateConversionService);
+ this.delegate = delegateConversionService;
+ }
+
/**
* Returns the parent of this conversion service. Could be null.
*/
@@ -84,24 +98,42 @@ public class GenericConversionService implements ConversionService {
}
/**
- * Add given converter to this conversion service.
+ * @return the Spring ConverterRegistry
+ */
+ public org.springframework.core.convert.ConversionService getDelegateConversionService() {
+ return delegate;
+ }
+
+ /**
+ * Registers the given converter with the underlying Spring ConversionService with the help of an adapter. The
+ * adapter allows an existing Spring Binding converter to be invoked within Spring's type conversion system.
+ *
* @param converter the converter
+ *
+ * @see ConverterRegistry
+ * @see org.springframework.core.convert.ConversionService
+ * @see SpringBindingConverterAdapter
*/
public void addConverter(Converter converter) {
- Class sourceClass = converter.getSourceClass();
- Class targetClass = converter.getTargetClass();
- Map sourceMap = getSourceMap(sourceClass);
- sourceMap.put(targetClass, converter);
+ ((ConverterRegistry) delegate).addConverter(new SpringBindingConverterAdapter(converter));
if (converter instanceof TwoWayConverter) {
- sourceMap = getSourceMap(targetClass);
- sourceMap.put(sourceClass, new ReverseConverter((TwoWayConverter) converter));
+ TwoWayConverter twoWayConverter = (TwoWayConverter) converter;
+ ((ConverterRegistry) delegate).addConverter(new SpringBindingConverterAdapter(new ReverseConverter(
+ twoWayConverter)));
}
}
/**
* Add given custom converter to this conversion service.
+ *
+ * Note: Converters registered through this method will not be involve the Spring type conversion system, which is
+ * now used the default type conversion mechanism. Spring's type conversion does not support named converters. This
+ * method is provided for backwards compatibility.
+ *
* @param id the id of the custom converter instance
* @param converter the converter
+ *
+ * @deprecated use {@link #addConverter(Converter)} instead
*/
public void addConverter(String id, Converter converter) {
customConverters.put(id, converter);
@@ -114,15 +146,6 @@ public class GenericConversionService implements ConversionService {
aliasMap.put(alias, targetType);
}
- private Map getSourceMap(Class sourceClass) {
- Map sourceMap = (Map) sourceClassConverters.get(sourceClass);
- if (sourceMap == null) {
- sourceMap = new HashMap();
- sourceClassConverters.put(sourceClass, sourceMap);
- }
- return sourceMap;
- }
-
public ConversionExecutor getConversionExecutor(Class sourceClass, Class targetClass)
throws ConversionExecutorNotFoundException {
Assert.notNull(sourceClass, "The source class to convert from is required");
@@ -132,40 +155,15 @@ public class GenericConversionService implements ConversionService {
if (targetClass.isAssignableFrom(sourceClass)) {
return new StaticConversionExecutor(sourceClass, targetClass, new NoOpConverter(sourceClass, targetClass));
}
- // special handling for arrays since they are not indexable classes
- if (sourceClass.isArray()) {
- if (targetClass.isArray()) {
- return new StaticConversionExecutor(sourceClass, targetClass, new ArrayToArray(this));
- } else if (Collection.class.isAssignableFrom(targetClass)) {
- if (!targetClass.isInterface() && Modifier.isAbstract(targetClass.getModifiers())) {
- throw new IllegalArgumentException("Conversion target class [" + targetClass.getName()
- + "] is invalid; cannot convert to abstract collection types--"
- + "request an interface or concrete implementation instead");
- }
- return new StaticConversionExecutor(sourceClass, targetClass, new ArrayToCollection(this));
- }
- }
- if (targetClass.isArray()) {
- if (Collection.class.isAssignableFrom(sourceClass)) {
- Converter collectionToArray = new ReverseConverter(new ArrayToCollection(this));
- return new StaticConversionExecutor(sourceClass, targetClass, collectionToArray);
- } else {
- return new StaticConversionExecutor(sourceClass, targetClass, new ObjectToArray(this));
- }
- }
- Converter converter = findRegisteredConverter(sourceClass, targetClass);
- if (converter != null) {
- // we found a converter
- return new StaticConversionExecutor(sourceClass, targetClass, converter);
+ if (delegate.canConvert(sourceClass, targetClass)) {
+ return new StaticConversionExecutor(sourceClass, targetClass, new SpringConvertingConverterAdapter(
+ sourceClass, targetClass, delegate));
+ } else if (parent != null) {
+ return parent.getConversionExecutor(sourceClass, targetClass);
} else {
- if (parent != null) {
- // try the parent
- return parent.getConversionExecutor(sourceClass, targetClass);
- } else {
- throw new ConversionExecutorNotFoundException(sourceClass, targetClass,
- "No ConversionExecutor found for converting from sourceClass [" + sourceClass.getName()
- + "] to target class [" + targetClass.getName() + "]");
- }
+ throw new ConversionExecutorNotFoundException(sourceClass, targetClass,
+ "No ConversionExecutor found for converting from sourceClass [" + sourceClass.getName()
+ + "] to target class [" + targetClass.getName() + "]");
}
}
@@ -341,46 +339,6 @@ public class GenericConversionService implements ConversionService {
}
}
- private Converter findRegisteredConverter(Class sourceClass, Class targetClass) {
- if (sourceClass.isInterface()) {
- LinkedList classQueue = new LinkedList();
- classQueue.addFirst(sourceClass);
- while (!classQueue.isEmpty()) {
- Class currentClass = (Class) classQueue.removeLast();
- Map sourceTargetConverters = findConvertersForSource(currentClass);
- Converter converter = findTargetConverter(sourceTargetConverters, targetClass);
- if (converter != null) {
- return converter;
- }
- Class[] interfaces = currentClass.getInterfaces();
- for (int i = 0; i < interfaces.length; i++) {
- classQueue.addFirst(interfaces[i]);
- }
- }
- Map objectConverters = findConvertersForSource(Object.class);
- return findTargetConverter(objectConverters, targetClass);
- } else {
- LinkedList classQueue = new LinkedList();
- classQueue.addFirst(sourceClass);
- while (!classQueue.isEmpty()) {
- Class currentClass = (Class) classQueue.removeLast();
- Map sourceTargetConverters = findConvertersForSource(currentClass);
- Converter converter = findTargetConverter(sourceTargetConverters, targetClass);
- if (converter != null) {
- return converter;
- }
- if (currentClass.getSuperclass() != null) {
- classQueue.addFirst(currentClass.getSuperclass());
- }
- Class[] interfaces = currentClass.getInterfaces();
- for (int i = 0; i < interfaces.length; i++) {
- classQueue.addFirst(interfaces[i]);
- }
- }
- return null;
- }
- }
-
public Object executeConversion(Object source, Class targetClass) throws ConversionException {
if (source != null) {
ConversionExecutor conversionExecutor = getConversionExecutor(source.getClass(), targetClass);
@@ -412,97 +370,8 @@ public class GenericConversionService implements ConversionService {
}
}
- // subclassing support
-
- public Set getConversionExecutors(Class sourceClass) {
- Set parentExecutors;
- if (parent != null) {
- parentExecutors = parent.getConversionExecutors(sourceClass);
- } else {
- parentExecutors = Collections.EMPTY_SET;
- }
- Map sourceMap = getSourceMap(sourceClass);
- if (parentExecutors.isEmpty() && sourceMap.isEmpty()) {
- return Collections.EMPTY_SET;
- }
- Set entries = sourceMap.entrySet();
- Set conversionExecutors = new HashSet(entries.size() + parentExecutors.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));
- }
- conversionExecutors.addAll(parentExecutors);
- return conversionExecutors;
- }
-
- /**
- * 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
- * perform the source->target conversion.
- */
- protected Map getSourceClassConverters() {
- return sourceClassConverters;
- }
-
- /**
- * Returns a registered converter object
- * @param sourceClass the source class
- * @param targetClass the target class
- */
- protected Converter getConverter(Class sourceClass, Class targetClass) {
- Map sourceTargetConverters = findConvertersForSource(sourceClass);
- return findTargetConverter(sourceTargetConverters, targetClass);
- }
-
// internal helpers
- private Map findConvertersForSource(Class sourceClass) {
- Map sourceConverters = (Map) sourceClassConverters.get(sourceClass);
- return sourceConverters != null ? sourceConverters : Collections.EMPTY_MAP;
- }
-
- private Converter findTargetConverter(Map sourceTargetConverters, Class targetClass) {
- if (sourceTargetConverters.isEmpty()) {
- return null;
- }
- if (targetClass.isInterface()) {
- LinkedList classQueue = new LinkedList();
- classQueue.addFirst(targetClass);
- while (!classQueue.isEmpty()) {
- Class currentClass = (Class) classQueue.removeLast();
- Converter converter = (Converter) sourceTargetConverters.get(currentClass);
- if (converter != null) {
- return converter;
- }
- Class[] interfaces = currentClass.getInterfaces();
- for (int i = 0; i < interfaces.length; i++) {
- classQueue.addFirst(interfaces[i]);
- }
- }
- return (Converter) sourceTargetConverters.get(Object.class);
- } else {
- LinkedList classQueue = new LinkedList();
- classQueue.addFirst(targetClass);
- while (!classQueue.isEmpty()) {
- Class currentClass = (Class) classQueue.removeLast();
- Converter converter = (Converter) sourceTargetConverters.get(currentClass);
- if (converter != null) {
- return converter;
- }
- if (currentClass.getSuperclass() != null) {
- classQueue.addFirst(currentClass.getSuperclass());
- }
- Class[] interfaces = currentClass.getInterfaces();
- for (int i = 0; i < interfaces.length; i++) {
- classQueue.addFirst(interfaces[i]);
- }
- }
- return null;
- }
- }
-
private Class convertToWrapperClassIfNecessary(Class targetType) {
if (targetType.isPrimitive()) {
if (targetType.equals(int.class)) {
@@ -528,4 +397,5 @@ public class GenericConversionService implements ConversionService {
return targetType;
}
}
+
}
\ No newline at end of file
diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/service/SpringBindingConverterAdapter.java b/spring-binding/src/main/java/org/springframework/binding/convert/service/SpringBindingConverterAdapter.java
new file mode 100644
index 00000000..6a65eed7
--- /dev/null
+++ b/spring-binding/src/main/java/org/springframework/binding/convert/service/SpringBindingConverterAdapter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2004-2010 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.convert.service;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.springframework.binding.convert.converters.Converter;
+import org.springframework.core.convert.ConversionService;
+import org.springframework.core.convert.TypeDescriptor;
+import org.springframework.core.convert.converter.GenericConverter;
+import org.springframework.util.Assert;
+
+/**
+ * A Spring Converter that makes it possible for a Spring Binding Converter to be registered with a Spring
+ * {@link ConversionService}.
+ *
+ * @author Rossen Stoyanchev
+ */
+public class SpringBindingConverterAdapter implements GenericConverter {
+
+ private Converter converter;
+
+ public SpringBindingConverterAdapter(Converter converter) {
+ Assert.notNull(converter, "A Spring Binding converter is required.");
+ this.converter = converter;
+ }
+
+ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
+ try {
+ return converter.convertSourceToTargetClass(source, targetType.getObjectType());
+ } catch (Exception e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ public Set getConvertibleTypes() {
+ return Collections.singleton(new ConvertiblePair(converter.getSourceClass(), converter.getTargetClass()));
+ }
+
+}
\ No newline at end of file
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
index 4348c0c0..88042b79 100644
--- 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
@@ -15,18 +15,11 @@
*/
package org.springframework.binding.expression.beanwrapper;
-import java.beans.PropertyEditorSupport;
-import java.util.Iterator;
-import java.util.Set;
-
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.NotReadablePropertyException;
import org.springframework.beans.NotWritablePropertyException;
-import org.springframework.beans.PropertyEditorRegistry;
import org.springframework.beans.TypeMismatchException;
-import org.springframework.binding.convert.ConversionException;
-import org.springframework.binding.convert.ConversionExecutor;
import org.springframework.binding.convert.ConversionService;
import org.springframework.binding.expression.EvaluationException;
import org.springframework.binding.expression.Expression;
@@ -95,7 +88,7 @@ public class BeanWrapperExpression implements Expression {
public void setValue(Object context, Object value) {
try {
BeanWrapperImpl beanWrapper = new BeanWrapperImpl(context);
- registerConvertersAsPropertyEditors(beanWrapper);
+ beanWrapper.setConversionService(conversionService.getDelegateConversionService());
beanWrapper.setPropertyValue(expression, value);
} catch (NotWritablePropertyException e) {
throw new PropertyNotFoundException(context.getClass(), expression, e);
@@ -129,39 +122,4 @@ public class BeanWrapperExpression implements Expression {
return expression;
}
- /**
- * Adapts the String->Object converters to PropertyEditors for use during a setValue attempt. Excludes any
- * String->Enum converter, since BeanWrapper has built in support for Enum conversion.
- * @param registry the registry to register converter-to-editor adapters with
- */
- protected void registerConvertersAsPropertyEditors(PropertyEditorRegistry registry) {
- Set converters = conversionService.getConversionExecutors(String.class);
- for (Iterator it = converters.iterator(); it.hasNext();) {
- ConversionExecutor converter = (ConversionExecutor) it.next();
- if (!converter.getTargetClass().getName().equals("java.lang.Enum")) {
- registry.registerCustomEditor(converter.getTargetClass(), new PropertyEditorConverter(converter));
- }
- }
- }
-
- private static class PropertyEditorConverter extends PropertyEditorSupport {
-
- private ConversionExecutor converter;
-
- public PropertyEditorConverter(ConversionExecutor converter) {
- this.converter = converter;
- }
-
- public void setAsText(String text) throws IllegalArgumentException {
- try {
- Object convertedValue = converter.execute(text);
- setValue(convertedValue);
- } catch (ConversionException e) {
- IllegalArgumentException iae = new IllegalArgumentException("Unable to convert text '" + text + "'");
- iae.initCause(e);
- throw iae;
- }
- }
- }
-
}
diff --git a/spring-binding/src/main/java/org/springframework/binding/expression/spel/SpringELExpression.java b/spring-binding/src/main/java/org/springframework/binding/expression/spel/SpringELExpression.java
index 4543b56a..cd59e347 100644
--- a/spring-binding/src/main/java/org/springframework/binding/expression/spel/SpringELExpression.java
+++ b/spring-binding/src/main/java/org/springframework/binding/expression/spel/SpringELExpression.java
@@ -149,4 +149,8 @@ public class SpringELExpression implements Expression {
return variableValues;
}
+ public String toString() {
+ return getExpressionString();
+ }
+
}
diff --git a/spring-binding/src/main/java/org/springframework/binding/expression/spel/SpringELExpressionParser.java b/spring-binding/src/main/java/org/springframework/binding/expression/spel/SpringELExpressionParser.java
index a02d5080..34eb8663 100644
--- a/spring-binding/src/main/java/org/springframework/binding/expression/spel/SpringELExpressionParser.java
+++ b/spring-binding/src/main/java/org/springframework/binding/expression/spel/SpringELExpressionParser.java
@@ -16,11 +16,12 @@
package org.springframework.binding.expression.spel;
import java.util.ArrayList;
-import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.springframework.binding.convert.ConversionService;
+import org.springframework.binding.convert.service.DefaultConversionService;
import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ExpressionParser;
import org.springframework.binding.expression.ExpressionVariable;
@@ -28,16 +29,11 @@ import org.springframework.binding.expression.ParserContext;
import org.springframework.binding.expression.ParserException;
import org.springframework.binding.expression.support.NullParserContext;
import org.springframework.context.expression.MapAccessor;
-import org.springframework.core.convert.ConversionService;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.PropertyAccessor;
-import org.springframework.expression.TypeConverter;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.support.StandardTypeConverter;
-import org.springframework.format.FormatterRegistry;
-import org.springframework.format.datetime.DateFormatter;
-import org.springframework.format.support.FormattingConversionServiceFactoryBean;
import org.springframework.util.Assert;
/**
@@ -57,29 +53,17 @@ public class SpringELExpressionParser implements ExpressionParser {
private List propertyAccessors = new ArrayList();
public SpringELExpressionParser(SpelExpressionParser expressionParser) {
+ this(expressionParser, new DefaultConversionService());
+ }
+
+ public SpringELExpressionParser(SpelExpressionParser expressionParser, ConversionService conversionService) {
this.expressionParser = expressionParser;
this.propertyAccessors.add(new MapAccessor());
- }
-
- public ConversionService getConversionService() {
- ensureConversionServiceInitialized();
- return conversionService;
- }
-
- public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
- private void ensureConversionServiceInitialized() {
- if (this.conversionService == null) {
- FormattingConversionServiceFactoryBean factoryBean = new FormattingConversionServiceFactoryBean() {
- protected void installFormatters(FormatterRegistry registry) {
- registry.addFormatterForFieldType(Date.class, new DateFormatter());
- }
- };
- factoryBean.afterPropertiesSet();
- this.conversionService = factoryBean.getObject();
- }
+ public ConversionService getConversionService() {
+ return conversionService;
}
public void addPropertyAccessor(PropertyAccessor propertyAccessor) {
@@ -90,17 +74,13 @@ public class SpringELExpressionParser implements ExpressionParser {
Assert.hasText(expressionString, "The expression string to parse is required and must not be empty");
parserContext = (parserContext == null) ? NullParserContext.INSTANCE : parserContext;
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
- evaluationContext.setTypeConverter(getTypeConverter());
+ evaluationContext.setTypeConverter(new StandardTypeConverter(conversionService.getDelegateConversionService()));
evaluationContext.getPropertyAccessors().addAll(propertyAccessors);
Map spelExpressionVariables = parseSpelExpressionVariables(parserContext.getExpressionVariables());
return new SpringELExpression(parseSpelExpression(expressionString, parserContext), spelExpressionVariables,
parserContext.getExpectedEvaluationResultType(), evaluationContext);
}
- private TypeConverter getTypeConverter() {
- return (conversionService != null) ? new StandardTypeConverter(conversionService) : new StandardTypeConverter();
- }
-
private org.springframework.expression.Expression parseSpelExpression(String expression, ParserContext parserContext) {
return expressionParser.parseExpression(expression, getSpelParserContext(parserContext));
}
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 966ef0f3..7f45b9ac 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
@@ -15,13 +15,11 @@
*/
package org.springframework.binding.convert.service;
-import java.math.BigDecimal;
import java.security.Principal;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
@@ -60,7 +58,6 @@ public class DefaultConversionServiceTests extends TestCase {
DefaultConversionService service = new DefaultConversionService();
StaticConversionExecutor executor = (StaticConversionExecutor) service.getConversionExecutor(String.class,
Boolean.class);
- assertNotSame(customConverter, executor.getConverter());
try {
executor.execute("ja");
fail();
@@ -69,7 +66,6 @@ public class DefaultConversionServiceTests extends TestCase {
}
service.addConverter(customConverter);
executor = (StaticConversionExecutor) service.getConversionExecutor(String.class, Boolean.class);
- assertSame(customConverter, executor.getConverter());
assertTrue(((Boolean) executor.execute("ja")).booleanValue());
}
@@ -510,16 +506,20 @@ public class DefaultConversionServiceTests extends TestCase {
DefaultConversionService service = new DefaultConversionService();
ConversionExecutor executor = service.getConversionExecutor(String.class, String[].class);
String[] result = (String[]) executor.execute("1,2,3");
- assertEquals(1, result.length);
- assertEquals("1,2,3", result[0]);
+ assertEquals(3, result.length);
+ assertEquals("1", result[0]);
+ assertEquals("2", result[1]);
+ assertEquals("3", result[2]);
}
public void testStringToListConversion() {
DefaultConversionService service = new DefaultConversionService();
ConversionExecutor executor = service.getConversionExecutor(String.class, List.class);
List result = (List) executor.execute("1,2,3");
- assertEquals(1, result.size());
- assertEquals("1,2,3", result.get(0));
+ assertEquals(3, result.size());
+ assertEquals("1", result.get(0));
+ assertEquals("2", result.get(1));
+ assertEquals("3", result.get(2));
}
public void testStringToArrayConversionWithElementConversion() {
@@ -530,25 +530,6 @@ public class DefaultConversionServiceTests extends TestCase {
assertEquals(new Integer(123), result[0]);
}
- public void testGetConversionExecutorsForSource() {
- DefaultConversionService service1 = new DefaultConversionService();
- service1.addConverter(new CustomConverter());
- GenericConversionService service2 = new GenericConversionService();
- FormattedStringToNumber formatterConverter = new FormattedStringToNumber(BigDecimal.class);
- service2.addConverter(formatterConverter);
- service2.setParent(service1);
- Set converters = service2.getConversionExecutors(String.class);
- Iterator it = converters.iterator();
- while (it.hasNext()) {
- ConversionExecutor executor = (ConversionExecutor) it.next();
- if (executor.getTargetClass().equals(BigDecimal.class)) {
- StaticConversionExecutor se = (StaticConversionExecutor) executor;
- assertSame(formatterConverter, se.getConverter());
- }
- }
- assertEquals(15, converters.size());
- }
-
private static class CustomConverter implements Converter {
public Object convertSourceToTargetClass(final Object source, Class targetClass) throws Exception {
diff --git a/spring-binding/src/test/java/org/springframework/binding/expression/beanwrapper/BeanWrapperExpressionParserTests.java b/spring-binding/src/test/java/org/springframework/binding/expression/beanwrapper/BeanWrapperExpressionParserTests.java
index 7a80aec4..8a52f12a 100644
--- a/spring-binding/src/test/java/org/springframework/binding/expression/beanwrapper/BeanWrapperExpressionParserTests.java
+++ b/spring-binding/src/test/java/org/springframework/binding/expression/beanwrapper/BeanWrapperExpressionParserTests.java
@@ -18,6 +18,8 @@ package org.springframework.binding.expression.beanwrapper;
import junit.framework.TestCase;
import org.springframework.beans.TypeMismatchException;
+import org.springframework.binding.convert.converters.StringToDate;
+import org.springframework.binding.convert.service.GenericConversionService;
import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ParserException;
import org.springframework.binding.expression.ValueCoercionException;
@@ -99,6 +101,10 @@ public class BeanWrapperExpressionParserTests extends TestCase {
}
public void testSetValueWithCoersion() {
+ GenericConversionService cs = (GenericConversionService) parser.getConversionService();
+ StringToDate converter = new StringToDate();
+ converter.setPattern("yyyy-MM-dd");
+ cs.addConverter(converter);
Expression e = parser.parseExpression("date", null);
e.setValue(bean, "2008-9-15");
}
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 10500e2e..19c75b40 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
@@ -17,6 +17,8 @@ package org.springframework.binding.expression.ognl;
import junit.framework.TestCase;
+import org.springframework.binding.convert.converters.StringToDate;
+import org.springframework.binding.convert.service.GenericConversionService;
import org.springframework.binding.expression.EvaluationException;
import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ExpressionVariable;
@@ -203,6 +205,10 @@ public class OgnlExpressionParserTests extends TestCase {
}
public void testSetValueWithCoersion() {
+ GenericConversionService cs = (GenericConversionService) parser.getConversionService();
+ StringToDate converter = new StringToDate();
+ converter.setPattern("yyyy-MM-dd");
+ cs.addConverter(converter);
Expression e = parser.parseExpression("date", null);
e.setValue(bean, "2008-9-15");
}
diff --git a/spring-faces/src/main/java/org/springframework/faces/config/FacesFlowBuilderServicesBeanDefinitionParser.java b/spring-faces/src/main/java/org/springframework/faces/config/FacesFlowBuilderServicesBeanDefinitionParser.java
index 91ec1e94..0b1d1c66 100644
--- a/spring-faces/src/main/java/org/springframework/faces/config/FacesFlowBuilderServicesBeanDefinitionParser.java
+++ b/spring-faces/src/main/java/org/springframework/faces/config/FacesFlowBuilderServicesBeanDefinitionParser.java
@@ -16,6 +16,7 @@
package org.springframework.faces.config;
import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -123,6 +124,7 @@ public class FacesFlowBuilderServicesBeanDefinitionParser extends AbstractSingle
}
expressionParserBuilder.addConstructorArgValue(spelExpressionParser.getBeanDefinition());
+ expressionParserBuilder.addConstructorArgReference(getConversionService(definitionBuilder));
expressionParser = registerInfrastructureComponent(element, context, expressionParserBuilder);
} else if (enableManagedBeans) {
@@ -141,6 +143,12 @@ public class FacesFlowBuilderServicesBeanDefinitionParser extends AbstractSingle
}
}
+ private String getConversionService(BeanDefinitionBuilder definitionBuilder) {
+ RuntimeBeanReference conversionServiceReference = (RuntimeBeanReference) definitionBuilder.getBeanDefinition()
+ .getPropertyValues().getPropertyValue(CONVERSION_SERVICE_PROPERTY).getValue();
+ return conversionServiceReference.getBeanName();
+ }
+
private String registerInfrastructureComponent(Element element, ParserContext context,
BeanDefinitionBuilder componentBuilder) {
String beanName = context.getReaderContext().generateBeanName(componentBuilder.getRawBeanDefinition());
diff --git a/spring-faces/src/main/java/org/springframework/faces/model/converter/DataModelConverter.java b/spring-faces/src/main/java/org/springframework/faces/model/converter/DataModelConverter.java
index ffd16fc1..af804548 100644
--- a/spring-faces/src/main/java/org/springframework/faces/model/converter/DataModelConverter.java
+++ b/spring-faces/src/main/java/org/springframework/faces/model/converter/DataModelConverter.java
@@ -33,7 +33,7 @@ import org.springframework.util.ClassUtils;
public class DataModelConverter implements Converter {
public Class getSourceClass() {
- return Object.class;
+ return List.class;
}
public Class getTargetClass() {
diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/FacesSpringELExpressionParser.java b/spring-faces/src/main/java/org/springframework/faces/webflow/FacesSpringELExpressionParser.java
index 6faeef3a..7ec2e51f 100644
--- a/spring-faces/src/main/java/org/springframework/faces/webflow/FacesSpringELExpressionParser.java
+++ b/spring-faces/src/main/java/org/springframework/faces/webflow/FacesSpringELExpressionParser.java
@@ -15,6 +15,7 @@
*/
package org.springframework.faces.webflow;
+import org.springframework.binding.convert.ConversionService;
import org.springframework.binding.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.webflow.expression.spel.WebFlowSpringELExpressionParser;
@@ -34,4 +35,9 @@ public class FacesSpringELExpressionParser extends WebFlowSpringELExpressionPars
addPropertyAccessor(new JsfManagedBeanPropertyAccessor());
}
+ public FacesSpringELExpressionParser(SpelExpressionParser expressionParser, ConversionService conversionService) {
+ super(expressionParser, conversionService);
+ addPropertyAccessor(new JsfManagedBeanPropertyAccessor());
+ }
+
}
diff --git a/spring-faces/src/test/java/org/springframework/faces/config/FacesFlowBuilderServicesBeanDefinitionParserTests.java b/spring-faces/src/test/java/org/springframework/faces/config/FacesFlowBuilderServicesBeanDefinitionParserTests.java
index b47d33be..0205c536 100644
--- a/spring-faces/src/test/java/org/springframework/faces/config/FacesFlowBuilderServicesBeanDefinitionParserTests.java
+++ b/spring-faces/src/test/java/org/springframework/faces/config/FacesFlowBuilderServicesBeanDefinitionParserTests.java
@@ -70,7 +70,7 @@ public class FacesFlowBuilderServicesBeanDefinitionParserTests extends TestCase
assertNotNull(builderServices);
assertTrue(builderServices.getConversionService() instanceof TestConversionService);
assertTrue(builderServices.getExpressionParser() instanceof WebFlowSpringELExpressionParser);
- assertNotNull(((SpringELExpressionParser) builderServices.getExpressionParser()).getConversionService());
+ assertTrue(((SpringELExpressionParser) builderServices.getExpressionParser()).getConversionService() instanceof TestConversionService);
assertTrue(builderServices.getViewFactoryCreator() instanceof JsfViewFactoryCreator);
assertFalse(builderServices.getDevelopment());
}
@@ -115,5 +115,9 @@ public class FacesFlowBuilderServicesBeanDefinitionParserTests extends TestCase
public Class getClassForAlias(String name) throws ConversionExecutionException {
throw new UnsupportedOperationException("Auto-generated method stub");
}
+
+ public org.springframework.core.convert.ConversionService getDelegateConversionService() {
+ throw new UnsupportedOperationException("Auto-generated method stub");
+ }
}
}
diff --git a/spring-webflow-samples/booking-faces/ivy.xml b/spring-webflow-samples/booking-faces/ivy.xml
index b6288d83..726f65a3 100755
--- a/spring-webflow-samples/booking-faces/ivy.xml
+++ b/spring-webflow-samples/booking-faces/ivy.xml
@@ -32,11 +32,12 @@
+ * A {@link PropertyEditor} that delegates to a Spring ConversionService unless a converterId is provided. When a + * converterId is provided, conversion will be delegated to the Spring Binding ConversionService instead as Spring's + * type conversion system does not support named converters. + *
+ * + * @author Rossen Stoyanchev + */ +class ConvertingPropertyEditorAdapter extends PropertyEditorSupport { private ConversionService conversionService; - private Class fieldType; + private TypeDescriptor fieldType; private String converterId; - public ConversionExecutorPropertyEditor(ConversionService conversionService, Class fieldType, String converterId) { + private boolean canConvertToString; + + public ConvertingPropertyEditorAdapter(ConversionService conversionService, String converterId, + TypeDescriptor fieldType) { + Assert.notNull(conversionService, "A ConversionService instance is required."); Assert.notNull(fieldType, "The field type is required"); this.conversionService = conversionService; this.fieldType = fieldType; this.converterId = converterId; + this.canConvertToString = conversionService.getDelegateConversionService().canConvert(this.fieldType, + TypeDescriptor.valueOf(String.class)); } public String getAsText() { if (StringUtils.hasText(converterId)) { return (String) conversionService.executeConversion(converterId, getValue(), String.class); } else { - return (String) conversionService.executeConversion(getValue(), String.class); + if (canConvertToString) { + return (String) conversionService.getDelegateConversionService().convert(getValue(), fieldType, + TypeDescriptor.valueOf(String.class)); + } else { + return null; + } } } public void setAsText(String text) throws IllegalArgumentException { if (StringUtils.hasText(converterId)) { - setValue(conversionService.executeConversion(converterId, text, fieldType)); + setValue(conversionService.executeConversion(converterId, text, fieldType.getType())); } else { - setValue(conversionService.executeConversion(text, fieldType)); + setValue(conversionService.getDelegateConversionService().convert(text, + TypeDescriptor.valueOf(String.class), fieldType)); } } } \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/test/TestFlowBuilderServicesFactory.java b/spring-webflow/src/main/java/org/springframework/webflow/test/TestFlowBuilderServicesFactory.java index ca9ec363..c00b502c 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/test/TestFlowBuilderServicesFactory.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/test/TestFlowBuilderServicesFactory.java @@ -1,5 +1,6 @@ package org.springframework.webflow.test; +import org.springframework.binding.convert.ConversionService; import org.springframework.binding.convert.service.DefaultConversionService; import org.springframework.context.support.StaticApplicationContext; import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -15,10 +16,15 @@ public class TestFlowBuilderServicesFactory { } public static FlowBuilderServices getServices() { - FlowBuilderServices services = new FlowBuilderServices(); + FlowBuilderServices services = new FlowBuilderServices() { + // The SpEL parser must use the currently configured conversion service. + public void setConversionService(ConversionService conversionService) { + super.setConversionService(conversionService); + setExpressionParser(new WebFlowSpringELExpressionParser(new SpelExpressionParser(), conversionService)); + } + }; services.setViewFactoryCreator(new MockViewFactoryCreator()); services.setConversionService(new DefaultConversionService()); - services.setExpressionParser(new WebFlowSpringELExpressionParser(new SpelExpressionParser())); services.setApplicationContext(createTestApplicationContext()); return services; } @@ -28,4 +34,5 @@ public class TestFlowBuilderServicesFactory { context.refresh(); return context; } + } \ No newline at end of file diff --git a/spring-webflow/src/test/java/org/springframework/webflow/action/ActionResultExposerTests.java b/spring-webflow/src/test/java/org/springframework/webflow/action/ActionResultExposerTests.java deleted file mode 100644 index 1428e3af..00000000 --- a/spring-webflow/src/test/java/org/springframework/webflow/action/ActionResultExposerTests.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.springframework.webflow.action; - -import junit.framework.TestCase; - -import org.springframework.binding.convert.service.DefaultConversionService; -import org.springframework.binding.expression.support.StaticExpression; -import org.springframework.webflow.test.MockRequestContext; - -public class ActionResultExposerTests extends TestCase { - - public void testEvaluateExpressionResult() throws Exception { - StaticExpression resultExpression = new StaticExpression(""); - ActionResultExposer exposer = new ActionResultExposer(resultExpression, null, null); - MockRequestContext context = new MockRequestContext(); - exposer.exposeResult("foo", context); - assertEquals("foo", resultExpression.getValue(null)); - } - - public void testEvaluateExpressionNullResult() throws Exception { - StaticExpression resultExpression = new StaticExpression(""); - ActionResultExposer exposer = new ActionResultExposer(resultExpression, null, null); - MockRequestContext context = new MockRequestContext(); - exposer.exposeResult(null, context); - assertEquals(null, resultExpression.getValue(null)); - } - - public void testEvaluateExpressionResultExposerWithTypeConversion() throws Exception { - StaticExpression resultExpression = new StaticExpression(""); - ActionResultExposer exposer = new ActionResultExposer(resultExpression, Integer.class, - new DefaultConversionService()); - MockRequestContext context = new MockRequestContext(); - exposer.exposeResult("3", context); - assertEquals(new Integer(3), resultExpression.getValue(null)); - } - - public void testEvaluateExpressionResultExposerWithTypeConversionForgotArgument() throws Exception { - StaticExpression resultExpression = new StaticExpression(""); - try { - new ActionResultExposer(resultExpression, Integer.class, null); - fail("should have failed iae"); - } catch (IllegalArgumentException e) { - - } - } - -} diff --git a/spring-webflow/src/test/java/org/springframework/webflow/action/EvaluateActionTests.java b/spring-webflow/src/test/java/org/springframework/webflow/action/EvaluateActionTests.java index 52daea80..640845a7 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/action/EvaluateActionTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/action/EvaluateActionTests.java @@ -50,8 +50,7 @@ public class EvaluateActionTests extends TestCase { public void testEvaluateExpressionResultExposer() throws Exception { StaticExpression resultExpression = new StaticExpression(""); - EvaluateAction action = new EvaluateAction(new StaticExpression("bar"), new ActionResultExposer( - resultExpression, null, null)); + EvaluateAction action = new EvaluateAction(new StaticExpression("bar"), resultExpression); MockRequestContext context = new MockRequestContext(); Event result = action.execute(context); assertEquals("bar", result.getId()); diff --git a/spring-webflow/src/test/java/org/springframework/webflow/action/SetActionTests.java b/spring-webflow/src/test/java/org/springframework/webflow/action/SetActionTests.java index ee6b13e9..62ae585c 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/action/SetActionTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/action/SetActionTests.java @@ -2,28 +2,19 @@ package org.springframework.webflow.action; import junit.framework.TestCase; -import org.springframework.binding.convert.service.DefaultConversionService; import org.springframework.binding.expression.support.StaticExpression; import org.springframework.webflow.execution.Event; import org.springframework.webflow.test.MockRequestContext; public class SetActionTests extends TestCase { + public void testSetAction() throws Exception { StaticExpression name = new StaticExpression(""); - SetAction action = new SetAction(name, new StaticExpression("bar"), null, null); + SetAction action = new SetAction(name, new StaticExpression("bar")); MockRequestContext context = new MockRequestContext(); Event result = action.execute(context); assertEquals("success", result.getId()); assertEquals("bar", name.getValue(null)); } - public void testSetActionWithTypeConversion() throws Exception { - StaticExpression name = new StaticExpression(""); - SetAction action = new SetAction(name, new StaticExpression("3"), Integer.class, new DefaultConversionService()); - MockRequestContext context = new MockRequestContext(); - Event result = action.execute(context); - assertEquals("success", result.getId()); - assertEquals(new Integer(3), name.getValue(null)); - } - } diff --git a/spring-webflow/src/test/java/org/springframework/webflow/config/FlowBuilderServicesBeanDefinitionParserTests.java b/spring-webflow/src/test/java/org/springframework/webflow/config/FlowBuilderServicesBeanDefinitionParserTests.java index 7fc343d9..0841e7c4 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/config/FlowBuilderServicesBeanDefinitionParserTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/config/FlowBuilderServicesBeanDefinitionParserTests.java @@ -52,7 +52,7 @@ public class FlowBuilderServicesBeanDefinitionParserTests extends TestCase { assertNotNull(builderServices); assertTrue(builderServices.getConversionService() instanceof TestConversionService); assertTrue(builderServices.getExpressionParser() instanceof SpringELExpressionParser); - assertNotNull(((SpringELExpressionParser) builderServices.getExpressionParser()).getConversionService()); + assertTrue(((SpringELExpressionParser) builderServices.getExpressionParser()).getConversionService() instanceof TestConversionService); assertTrue(builderServices.getViewFactoryCreator() instanceof MvcViewFactoryCreator); assertFalse(builderServices.getDevelopment()); } @@ -98,6 +98,10 @@ public class FlowBuilderServicesBeanDefinitionParserTests extends TestCase { throw new UnsupportedOperationException("Auto-generated method stub"); } + public org.springframework.core.convert.ConversionService getDelegateConversionService() { + throw new UnsupportedOperationException("Auto-generated method stub"); + } + } } \ No newline at end of file diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilderTests.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilderTests.java index 458c203b..f9f63cc0 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilderTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilderTests.java @@ -20,6 +20,7 @@ import org.springframework.webflow.engine.builder.FlowBuilderException; import org.springframework.webflow.engine.impl.FlowExecutionImplFactory; import org.springframework.webflow.engine.model.AttributeModel; import org.springframework.webflow.engine.model.EndStateModel; +import org.springframework.webflow.engine.model.EvaluateModel; import org.springframework.webflow.engine.model.ExceptionHandlerModel; import org.springframework.webflow.engine.model.FlowModel; import org.springframework.webflow.engine.model.InputModel; @@ -27,6 +28,7 @@ import org.springframework.webflow.engine.model.Model; import org.springframework.webflow.engine.model.OutputModel; import org.springframework.webflow.engine.model.PersistenceContextModel; import org.springframework.webflow.engine.model.SecuredModel; +import org.springframework.webflow.engine.model.SetModel; import org.springframework.webflow.engine.model.TransitionModel; import org.springframework.webflow.engine.model.VarModel; import org.springframework.webflow.engine.model.ViewStateModel; @@ -36,6 +38,7 @@ import org.springframework.webflow.engine.model.builder.xml.XmlFlowModelBuilderT import org.springframework.webflow.engine.model.registry.FlowModelHolder; import org.springframework.webflow.engine.model.registry.FlowModelRegistryImpl; import org.springframework.webflow.engine.support.ActionExecutingViewFactory; +import org.springframework.webflow.execution.AnnotatedAction; import org.springframework.webflow.execution.FlowExecution; import org.springframework.webflow.execution.FlowExecutionException; import org.springframework.webflow.execution.FlowExecutionOutcome; @@ -43,6 +46,7 @@ import org.springframework.webflow.execution.ViewFactory; import org.springframework.webflow.security.SecurityRule; import org.springframework.webflow.test.MockExternalContext; import org.springframework.webflow.test.MockFlowBuilderContext; +import org.springframework.webflow.test.MockRequestContext; public class FlowModelFlowBuilderTests extends TestCase { private FlowModel model; @@ -306,6 +310,77 @@ public class FlowModelFlowBuilderTests extends TestCase { assertEquals(1, flow.getExceptionHandlerSet().size()); } + public void testSetActionWithResultType() throws Exception { + SetModel setModel = new SetModel("flowScope.stringArray", "intArray"); + setModel.setType("java.lang.String[]"); + model.setOnStartActions(singleList(setModel)); + model.setStates(singleList(new ViewStateModel("view"))); + Flow flow = getFlow(model); + AnnotatedAction action = (AnnotatedAction) flow.getStartActionList().get(0); + MockRequestContext context = new MockRequestContext(flow); + context.getFlowScope().put("intArray", new int[] { 1, 2 }); + action.execute(context); + String[] expected = (String[]) context.getFlowScope().get("stringArray"); + assertEquals("1", expected[0]); + assertEquals("2", expected[1]); + } + + public void testSetActionWithImplicitTypeConversion() throws Exception { + SetModel setModel = new SetModel("testBean.stringArray", "intArray"); + model.setOnStartActions(singleList(setModel)); + ViewStateModel state = new ViewStateModel("view"); + model.setStates(singleList(state)); + Flow flow = getFlow(model); + AnnotatedAction action = (AnnotatedAction) flow.getStartActionList().get(0); + MockRequestContext context = new MockRequestContext(flow); + context.getFlowScope().put("testBean", new TestBean()); + context.getFlowScope().put("intArray", new int[] { 1, 2 }); + action.execute(context); + TestBean expected = (TestBean) context.getFlowScope().get("testBean"); + assertEquals("1", expected.stringArray[0]); + assertEquals("2", expected.stringArray[1]); + } + + public void testEvaluateActionWithResultType() throws Exception { + EvaluateModel evaluateModel = new EvaluateModel("testBean.getIntegers()"); + evaluateModel.setResult("flowScope.stringArray"); + evaluateModel.setResultType("java.lang.String[]"); + model.setOnStartActions(singleList(evaluateModel)); + model.setStates(singleList(new ViewStateModel("view"))); + Flow flow = getFlow(model); + AnnotatedAction action = (AnnotatedAction) flow.getStartActionList().get(0); + MockRequestContext context = new MockRequestContext(flow); + context.getFlowScope().put("testBean", new TestBean()); + action.execute(context); + String[] expected = (String[]) context.getFlowScope().get("stringArray"); + assertEquals("1", expected[0]); + assertEquals("2", expected[1]); + } + + public void testEvaluateActionWithELExpression() throws Exception { + EvaluateModel evaluateModel = new EvaluateModel("testBean.getIntegers()"); + evaluateModel.setResult("flowScope.stringArray"); + evaluateModel.setResultType("java.lang.String[]"); + model.setOnStartActions(singleList(evaluateModel)); + model.setStates(singleList(new ViewStateModel("view"))); + Flow flow = getFlow(model); + AnnotatedAction action = (AnnotatedAction) flow.getStartActionList().get(0); + MockRequestContext context = new MockRequestContext(flow); + context.getFlowScope().put("testBean", new TestBean()); + action.execute(context); + String[] expected = (String[]) context.getFlowScope().get("stringArray"); + assertEquals("1", expected[0]); + assertEquals("2", expected[1]); + } + + private static class TestBean { + public String[] stringArray; + + public int[] getIntegers() { + return new int[] { 1, 2 }; + } + } + private Flow getFlow(FlowModel model) { FlowModelHolder holder = new StaticFlowModelHolder(model); FlowModelFlowBuilder builder = new FlowModelFlowBuilder(holder); diff --git a/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/AbstractBindingModelTests.java b/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/AbstractBindingModelTests.java index 2a0b5867..877b641f 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/AbstractBindingModelTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/AbstractBindingModelTests.java @@ -66,12 +66,6 @@ public abstract class AbstractBindingModelTests extends TestCase { assertEquals(new Integer(3), model.getRawFieldValue("datum2")); } - public void testGetFieldValueNonStringNoConversionService() { - model = new BindingModel("testBean", testBean, getExpressionParser(), null, messages); - testBean.datum2 = 3; - assertEquals(new Integer(3), model.getFieldValue("datum2")); - } - public void testGetFieldValueConvertedWithCustomConverter() { testBean.datum2 = 3; conversionService.addConverter("customConverter", new StringToObject(Integer.class) { diff --git a/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewTests.java b/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewTests.java index ea87f8ea..cc2a2fd4 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewTests.java @@ -6,7 +6,6 @@ import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.security.Principal; -import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.List; @@ -19,14 +18,13 @@ import junit.framework.TestCase; import org.springframework.binding.convert.converters.StringToDate; import org.springframework.binding.convert.service.DefaultConversionService; +import org.springframework.binding.convert.service.GenericConversionService; import org.springframework.binding.expression.EvaluationException; import org.springframework.binding.expression.Expression; import org.springframework.binding.expression.spel.SpringELExpressionParser; import org.springframework.binding.expression.support.StaticExpression; import org.springframework.binding.validation.ValidationContext; import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.format.datetime.DateFormatter; -import org.springframework.format.support.FormattingConversionService; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockMultipartFile; @@ -393,8 +391,8 @@ public class MvcViewTests extends TestCase { assertEquals(context2.getFlowScope().get("bindBean"), model.get("bindBean")); BindingModel bm = (BindingModel) model.get(BindingResult.MODEL_KEY_PREFIX + "bindBean"); assertNotNull(bm); - assertEquals(new Integer(3), bm.getFieldValue("integerProperty")); - assertEquals(new SimpleDateFormat("MM-dd-yyyy").parse("01-01-2008"), bm.getFieldValue("dateProperty")); + assertEquals("3", bm.getFieldValue("integerProperty")); + assertEquals("2008-01-01", bm.getFieldValue("dateProperty")); } private Object saveAndRestoreViewActionState(Object viewActionState) throws Exception { @@ -630,11 +628,12 @@ public class MvcViewTests extends TestCase { } private SpringELExpressionParser createExpressionParser() { - SpringELExpressionParser expressionParser = new WebFlowSpringELExpressionParser(new SpelExpressionParser()); - FormattingConversionService conversionService = (FormattingConversionService) expressionParser - .getConversionService(); - conversionService.addFormatterForFieldType(Date.class, new DateFormatter("yyyy-MM-dd")); - return expressionParser; + StringToDate c = new StringToDate(); + c.setPattern("yyyy-MM-dd"); + SpringELExpressionParser parser = new WebFlowSpringELExpressionParser(new SpelExpressionParser()); + GenericConversionService cs = (GenericConversionService) parser.getConversionService(); + cs.addConverter(c); + return parser; } private class MockMvcView extends AbstractMvcView { diff --git a/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/SpringBeanBindingModelTests.java b/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/SpringBeanBindingModelTests.java index 1897b551..10e71bbc 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/SpringBeanBindingModelTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/SpringBeanBindingModelTests.java @@ -16,4 +16,13 @@ public class SpringBeanBindingModelTests extends AbstractBindingModelTests { PropertyEditor editor = model.findEditor("emptyMap['foo']", null); assertNull(editor); } + + // BeanWrapper-based EL does not accept result type hints. + // Hence it requires a conversion service. + public void testGetFieldValueNonStringNoConversionService() { + model = new BindingModel("testBean", testBean, getExpressionParser(), null, messages); + testBean.datum2 = 3; + assertEquals(new Integer(3), model.getFieldValue("datum2")); + } + }