SWF-1234 Integration with Spring 3 type conversion

This commit is contained in:
Rossen Stoyanchev
2010-05-04 11:12:32 +00:00
parent 7372b31184
commit de75aefc63
51 changed files with 630 additions and 743 deletions

View File

@@ -44,6 +44,12 @@
<version>1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>1.6</version>
<optional>true</optional>
</dependency>
</dependencies>
<repositories>
<!-- Where the el-api is located -->

View File

@@ -15,7 +15,6 @@
*/
package org.springframework.binding.convert;
import java.util.Set;
/**
* A service interface for retrieving type conversion executors. The returned command objects are thread-safe and may be
@@ -74,15 +73,6 @@ public interface ConversionService {
public ConversionExecutor getConversionExecutor(String id, Class sourceClass, Class targetClass)
throws ConversionExecutorNotFoundException;
/**
* Return all conversion executors capable of converting <i>from</i> the provided <code>sourceClass</code>. For
* example, <code>getConversionExecutor(String.class)</code> 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, <code>long</code> for <code>java.lang.Long</code>
* @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();
}

View File

@@ -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;
}
}

View File

@@ -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 <i>from string</i> 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() {

View File

@@ -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;
}
}
}

View File

@@ -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()));
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -149,4 +149,8 @@ public class SpringELExpression implements Expression {
return variableValues;
}
public String toString() {
return getExpressionString();
}
}

View File

@@ -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));
}

View File

@@ -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 {

View File

@@ -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");
}

View File

@@ -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");
}

View File

@@ -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());

View File

@@ -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() {

View File

@@ -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());
}
}

View File

@@ -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");
}
}
}

View File

@@ -32,11 +32,12 @@
<dependency org="org.hibernate" name="com.springsource.org.hibernate.annotations" rev="3.3.0.ga" conf="compile->runtime"/>
<dependency org="org.hibernate" name="com.springsource.org.hibernate.ejb" rev="3.3.1.ga" conf="compile->runtime"/>
<dependency org="org.hsqldb" name="com.springsource.org.hsqldb" rev="1.8.0.9" conf="compile->runtime"/>
<dependency org="org.jboss.el" name="com.springsource.org.jboss.el" rev="2.0.0.GA" conf="compile->runtime"/>
<dependency org="org.joda" name="com.springsource.org.joda.time" rev="1.6.0" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.aop" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.beans" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.context" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.core" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.expression" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.jdbc" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.orm" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.transaction" rev="3.0.2.RELEASE" conf="compile->runtime"/>

View File

@@ -53,9 +53,9 @@
<version>1.8.0.9</version>
</dependency>
<dependency>
<groupId>org.jboss.el</groupId>
<artifactId>com.springsource.org.jboss.el</artifactId>
<version>2.0.0.GA</version>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
@@ -77,6 +77,11 @@
<artifactId>org.springframework.core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.jdbc</artifactId>

View File

@@ -36,12 +36,13 @@
<dependency org="org.hibernate" name="com.springsource.org.hibernate.annotations" rev="3.3.0.ga" conf="compile->runtime"/>
<dependency org="org.hibernate" name="com.springsource.org.hibernate.ejb" rev="3.3.1.ga" conf="compile->runtime"/>
<dependency org="org.hsqldb" name="com.springsource.org.hsqldb" rev="1.8.0.9" conf="compile->runtime"/>
<dependency org="org.ognl" name="com.springsource.org.ognl" rev="2.6.9" conf="compile->runtime"/>
<dependency org="org.joda" name="com.springsource.org.joda.time" rev="1.6.0" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.aop" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.beans" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.context" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.context.support" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.core" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.expression" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.jdbc" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.orm" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.transaction" rev="3.0.2.RELEASE" conf="compile->runtime"/>

View File

@@ -73,9 +73,9 @@
<version>1.8.0.9</version>
</dependency>
<dependency>
<groupId>org.ognl</groupId>
<artifactId>com.springsource.org.ognl</artifactId>
<version>2.6.9</version>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
@@ -102,6 +102,11 @@
<artifactId>org.springframework.core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.jdbc</artifactId>

View File

@@ -1,18 +0,0 @@
package org.springframework.webflow.samples.booking;
import org.springframework.binding.convert.converters.StringToDate;
import org.springframework.binding.convert.service.DefaultConversionService;
import org.springframework.stereotype.Component;
@Component("conversionService")
public class ApplicationConversionService extends DefaultConversionService {
@Override
protected void addDefaultConverters() {
super.addDefaultConverters();
StringToDate dateConverter = new StringToDate();
dateConverter.setPattern("MM-dd-yyyy");
addConverter("shortDate", dateConverter);
}
}

View File

@@ -20,6 +20,7 @@ import javax.persistence.Transient;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.binding.validation.ValidationContext;
import org.springframework.format.annotation.DateTimeFormat;
/**
* A Hotel Booking made by a User.
@@ -32,8 +33,10 @@ public class Booking implements Serializable {
private Hotel hotel;
@DateTimeFormat(pattern = "MM-dd-yyyy")
private Date checkinDate;
@DateTimeFormat(pattern = "MM-dd-yyyy")
private Date checkoutDate;
private String creditCard;
@@ -73,7 +76,7 @@ public class Booking implements Serializable {
if (checkinDate == null || checkoutDate == null) {
return 0;
} else {
return (int) (checkoutDate.getTime() - checkinDate.getTime()) / 1000 / 60 / 60 / 24;
return (int) ((checkoutDate.getTime() - checkinDate.getTime()) / 1000 / 60 / 60 / 24);
}
}

View File

@@ -22,8 +22,8 @@
</webflow:flow-registry>
<!-- Plugs in a custom creator for Web Flow views -->
<webflow:flow-builder-services id="flowBuilderServices" view-factory-creator="mvcViewFactoryCreator" conversion-service="conversionService" development="true" />
<webflow:flow-builder-services id="flowBuilderServices" view-factory-creator="mvcViewFactoryCreator" development="true" />
<!-- Configures Web Flow to use Tiles to create views for rendering; Tiles allows for applying consistent layouts to your views -->
<bean id="mvcViewFactoryCreator" class="org.springframework.webflow.mvc.builder.MvcViewFactoryCreator">
<property name="viewResolvers" ref="tilesViewResolver"/>

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- Maps request paths to flows in the flowRegistry; e.g. a path of /hotels/booking looks for a flow with id "hotels/booking" -->
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">

View File

@@ -10,13 +10,13 @@
<input name="hotelId" required="true" />
<on-start>
<evaluate expression="bookingService.createBooking(hotelId, currentUser.name)" result="flowScope.booking" />
<evaluate expression="bookingService.createBooking(hotelId, currentUser.name)" result="flowScope.booking" />
</on-start>
<view-state id="enterBookingDetails" model="booking">
<binder>
<binding property="checkinDate" converter="shortDate" required="true" />
<binding property="checkoutDate" converter="shortDate" required="true" />
<binding property="checkinDate" required="true" />
<binding property="checkoutDate" required="true" />
<binding property="beds" required="true" />
<binding property="smoking" required="true" />
<binding property="creditCard" required="true" />

View File

@@ -34,11 +34,12 @@
<dependency org="org.hibernate" name="com.springsource.org.hibernate.annotations" rev="3.3.0.ga" conf="compile->compile"/>
<dependency org="org.hibernate" name="com.springsource.org.hibernate.ejb" rev="3.3.1.ga" conf="compile->compile"/>
<dependency org="org.hsqldb" name="com.springsource.org.hsqldb" rev="1.8.0.9" conf="compile->compile"/>
<dependency org="org.jboss.el" name="com.springsource.org.jboss.el" rev="2.0.0.GA" conf="compile->compile"/>
<dependency org="org.joda" name="com.springsource.org.joda.time" rev="1.6.0" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.aop" rev="3.0.2.RELEASE" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.beans" rev="3.0.2.RELEASE" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.context" rev="3.0.2.RELEASE" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.core" rev="3.0.2.RELEASE" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.expression" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.jdbc" rev="3.0.2.RELEASE" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.orm" rev="3.0.2.RELEASE" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.transaction" rev="3.0.2.RELEASE" conf="compile->compile"/>

View File

@@ -58,9 +58,9 @@
<version>1.8.0.9</version>
</dependency>
<dependency>
<groupId>org.jboss.el</groupId>
<artifactId>com.springsource.org.jboss.el</artifactId>
<version>2.0.0.GA</version>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
@@ -82,6 +82,11 @@
<artifactId>org.springframework.core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.jdbc</artifactId>

View File

@@ -21,7 +21,6 @@
<dependencies>
<!-- core dependencies -->
<dependency org="javax.el" name="com.springsource.javax.el" rev="1.0.0" conf="provided->compile"/>
<dependency org="javax.persistence" name="com.springsource.javax.persistence" rev="1.0.0" conf="compile->compile"/>
<dependency org="javax.portlet" name="com.springsource.javax.portlet" rev="2.0.0" conf="provided->compile"/>
<dependency org="javax.servlet" name="com.springsource.javax.servlet" rev="2.4.0" conf="provided->compile"/>
@@ -34,11 +33,12 @@
<dependency org="org.hibernate" name="com.springsource.org.hibernate.annotations" rev="3.3.0.ga" conf="compile->compile"/>
<dependency org="org.hibernate" name="com.springsource.org.hibernate.ejb" rev="3.3.1.ga" conf="compile->compile"/>
<dependency org="org.hsqldb" name="com.springsource.org.hsqldb" rev="1.8.0.9" conf="compile->compile"/>
<dependency org="org.jboss.el" name="com.springsource.org.jboss.el" rev="2.0.0.GA" conf="compile->compile"/>
<dependency org="org.joda" name="com.springsource.org.joda.time" rev="1.6.0" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.aop" rev="3.0.2.RELEASE" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.beans" rev="3.0.2.RELEASE" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.context" rev="3.0.2.RELEASE" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.core" rev="3.0.2.RELEASE" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.expression" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.jdbc" rev="3.0.2.RELEASE" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.orm" rev="3.0.2.RELEASE" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.transaction" rev="3.0.2.RELEASE" conf="compile->compile"/>

View File

@@ -48,9 +48,9 @@
<version>1.8.0.9</version>
</dependency>
<dependency>
<groupId>org.jboss.el</groupId>
<artifactId>com.springsource.org.jboss.el</artifactId>
<version>2.0.0.GA</version>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
@@ -72,6 +72,11 @@
<artifactId>org.springframework.core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.jdbc</artifactId>
@@ -119,12 +124,6 @@
<version>2.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>com.springsource.javax.el</artifactId>
<version>1.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>com.springsource.javax.servlet</artifactId>

View File

@@ -16,6 +16,8 @@ import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import org.springframework.format.annotation.DateTimeFormat;
/**
* A Hotel Booking made by a User.
*/
@@ -27,8 +29,10 @@ public class Booking implements Serializable {
private Hotel hotel;
@DateTimeFormat(pattern = "MM-dd-yyyy")
private Date checkinDate;
@DateTimeFormat(pattern = "MM-dd-yyyy")
private Date checkoutDate;
private String creditCard;

View File

@@ -21,32 +21,33 @@
<dependencies>
<!-- core dependencies -->
<dependency org="com.sun.facelets" name="com.springsource.com.sun.facelets" rev="1.1.14" conf="compile->compile"/>
<dependency org="com.sun.faces" name="com.springsource.com.sun.faces" rev="1.2.0.09" conf="compile->compile" />
<dependency org="javax.el" name="com.springsource.javax.el" rev="1.0.0" conf="provided->compile"/>
<dependency org="javax.faces" name="com.springsource.javax.faces" rev="1.2.0.09" conf="compile->compile" />
<dependency org="javax.persistence" name="com.springsource.javax.persistence" rev="1.0.0" conf="compile->compile"/>
<dependency org="javax.servlet" name="com.springsource.javax.servlet" rev="2.4.0" conf="provided->compile"/>
<dependency org="javax.servlet" name="com.springsource.javax.servlet.jsp.jstl" rev="1.1.2" conf="compile->compile"/>
<dependency org="javax.transaction" name="com.springsource.javax.transaction" rev="1.1.0" conf="compile->compile"/>
<dependency org="org.apache.log4j" name="com.springsource.org.apache.log4j" rev="1.2.15" conf="compile->compile"/>
<dependency org="org.hibernate" name="com.springsource.org.hibernate" rev="3.2.6.ga" conf="compile->compile"/>
<dependency org="org.hibernate" name="com.springsource.org.hibernate.annotations" rev="3.3.0.ga" conf="compile->compile"/>
<dependency org="org.hibernate" name="com.springsource.org.hibernate.ejb" rev="3.3.1.ga" conf="compile->compile"/>
<dependency org="org.hsqldb" name="com.springsource.org.hsqldb" rev="1.8.0.9" conf="compile->compile"/>
<dependency org="org.jboss.el" name="com.springsource.org.jboss.el" rev="2.0.0.GA" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.aop" rev="3.0.2.RELEASE" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.beans" rev="3.0.2.RELEASE" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.context" rev="3.0.2.RELEASE" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.core" rev="3.0.2.RELEASE" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.jdbc" rev="3.0.2.RELEASE" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.orm" rev="3.0.2.RELEASE" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.transaction" rev="3.0.2.RELEASE" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.web" rev="3.0.2.RELEASE" conf="compile->compile"/>
<dependency org="com.sun.facelets" name="com.springsource.com.sun.facelets" rev="1.1.14" conf="compile->runtime"/>
<dependency org="javax.el" name="com.springsource.javax.el" rev="1.0.0" conf="provided->runtime"/>
<dependency org="com.sun.faces" name="com.springsource.com.sun.faces" rev="1.2.0.09" conf="compile->runtime" />
<dependency org="javax.faces" name="com.springsource.javax.faces" rev="1.2.0.09" conf="compile->runtime" />
<dependency org="javax.persistence" name="com.springsource.javax.persistence" rev="1.0.0" conf="compile->runtime"/>
<dependency org="javax.servlet" name="com.springsource.javax.servlet" rev="2.4.0" conf="provided->runtime"/>
<dependency org="javax.servlet" name="com.springsource.javax.servlet.jsp.jstl" rev="1.1.2" conf="compile->runtime"/>
<dependency org="javax.transaction" name="com.springsource.javax.transaction" rev="1.1.0" conf="compile->runtime"/>
<dependency org="org.apache.log4j" name="com.springsource.org.apache.log4j" rev="1.2.15" conf="compile->runtime"/>
<dependency org="org.hibernate" name="com.springsource.org.hibernate" rev="3.2.6.ga" conf="compile->runtime"/>
<dependency org="org.hibernate" name="com.springsource.org.hibernate.annotations" rev="3.3.0.ga" conf="compile->runtime"/>
<dependency org="org.hibernate" name="com.springsource.org.hibernate.ejb" rev="3.3.1.ga" conf="compile->runtime"/>
<dependency org="org.hsqldb" name="com.springsource.org.hsqldb" rev="1.8.0.9" conf="compile->runtime"/>
<dependency org="org.joda" name="com.springsource.org.joda.time" rev="1.6.0" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.aop" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.beans" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.context" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.core" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.expression" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.jdbc" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.orm" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.transaction" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.web" rev="3.0.2.RELEASE" conf="compile->runtime"/>
<!-- test-time only dependencies -->
<dependency org="org.junit" name="com.springsource.junit" rev="3.8.2" conf="test->compile"/>
<dependency org="org.springframework" name="org.springframework.test" rev="3.0.2.RELEASE" conf="test->compile"/>
<dependency org="org.junit" name="com.springsource.junit" rev="3.8.2" conf="test->runtime"/>
<dependency org="org.springframework" name="org.springframework.test" rev="3.0.2.RELEASE" conf="test->runtime"/>
</dependencies>

View File

@@ -63,9 +63,9 @@
<version>1.8.0.9</version>
</dependency>
<dependency>
<groupId>org.jboss.el</groupId>
<artifactId>com.springsource.org.jboss.el</artifactId>
<version>2.0.0.GA</version>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
@@ -87,6 +87,11 @@
<artifactId>org.springframework.core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.jdbc</artifactId>

View File

@@ -1,106 +0,0 @@
/*
* 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.webflow.action;
import java.io.Serializable;
import org.springframework.binding.convert.ConversionService;
import org.springframework.binding.expression.Expression;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.Assert;
import org.springframework.webflow.execution.RequestContext;
/**
* Specifies how an action result value should be exposed to an executing flow. The return value is exposed as an
* attribute in a configured scope.
*
* @see EvaluateAction
*
* @author Keith Donald
*/
public class ActionResultExposer implements Serializable {
/**
* The expression to set the result to.
*/
private Expression resultExpression;
/**
* The desired type to expose the result as
*/
private Class expectedResultType;
/**
* The {@link ConversionService} to use to convert the result to the desired type
*/
private ConversionService conversionService;
/**
* Creates a action result exposer
* @param resultExpression the result expression
* @param expectedResultType the expected result type
*/
public ActionResultExposer(Expression resultExpression, Class expectedResultType,
ConversionService conversionService) {
Assert.notNull(resultExpression, "The result expression is required");
if (expectedResultType != null) {
Assert.notNull(conversionService, "A conversionService is required with an expectedResultType");
}
this.resultExpression = resultExpression;
this.expectedResultType = expectedResultType;
this.conversionService = conversionService;
}
/**
* Returns name of the attribute to index the return value with.
*/
public Expression getNameExpression() {
return resultExpression;
}
/**
* Returns the desired result type to be exposed
*/
public Class getExpectedResultType() {
return expectedResultType;
}
/**
* Expose given bean method return value in given flow execution request context.
* @param result the return value
* @param context the request context
*/
public void exposeResult(Object result, RequestContext context) {
resultExpression.setValue(context, applyTypeConversionIfNecessary(result));
}
/**
* Apply type conversion on the supplied value if necessary.
* @param value the raw value to be converted
*/
private Object applyTypeConversionIfNecessary(Object value) {
if (value == null || expectedResultType == null) {
return value;
} else {
return conversionService.getConversionExecutor(value.getClass(), expectedResultType).execute(value);
}
}
public String toString() {
return new ToStringCreator(this).append("result", resultExpression).append("resultType", expectedResultType)
.toString();
}
}

View File

@@ -30,7 +30,6 @@ import org.springframework.webflow.execution.RequestContext;
* {@link Event}.
*
* @see Expression
* @see ActionResultExposer
* @see ResultEventFactory
*
* @author Keith Donald
@@ -44,9 +43,9 @@ public class EvaluateAction extends AbstractAction {
private Expression expression;
/**
* The helper that will expose the expression evaluation result. Optional.
* The expression to evaluate to set the result of the action. Optional.
*/
private ActionResultExposer evaluationResultExposer;
private Expression resultExpression;
/**
* The selector for the factory that will create the action result event callers can respond to.
@@ -56,10 +55,10 @@ public class EvaluateAction extends AbstractAction {
/**
* Create a new evaluate action.
* @param expression the expression to evaluate (required)
* @param evaluationResultExposer the strategy for how the expression result will be exposed to the flow (optional)
* @param resultExpression the expression to evaluate the result (optional)
*/
public EvaluateAction(Expression expression, ActionResultExposer evaluationResultExposer) {
init(expression, evaluationResultExposer, null);
public EvaluateAction(Expression expression, Expression resultExpression) {
init(expression, resultExpression, null);
}
/**
@@ -68,9 +67,8 @@ public class EvaluateAction extends AbstractAction {
* @param evaluationResultExposer the strategy for how the expression result will be exposed to the flow (optional)
* @param resultEventFactory the factory that will map the evaluation result to a Web Flow event (optional)
*/
public EvaluateAction(Expression expression, ActionResultExposer evaluationResultExposer,
ResultEventFactory resultEventFactory) {
init(expression, evaluationResultExposer, resultEventFactory);
public EvaluateAction(Expression expression, Expression resultExpression, ResultEventFactory resultEventFactory) {
init(expression, resultExpression, resultEventFactory);
}
protected Event doExecute(RequestContext context) throws Exception {
@@ -78,25 +76,24 @@ public class EvaluateAction extends AbstractAction {
if (result instanceof Action) {
return ActionExecutor.execute((Action) result, context);
} else {
if (evaluationResultExposer != null) {
evaluationResultExposer.exposeResult(result, context);
if (resultExpression != null) {
resultExpression.setValue(context, result);
}
return resultEventFactory.createResultEvent(this, result, context);
}
}
public String toString() {
return new ToStringCreator(this).append("expression", expression).append("resultExposer",
evaluationResultExposer).toString();
return new ToStringCreator(this).append("expression", expression).append("resultExpression", resultExpression)
.toString();
}
// internal helpers
private void init(Expression expression, ActionResultExposer evaluationResultExposer,
ResultEventFactory resultEventFactory) {
private void init(Expression expression, Expression resultExpression, ResultEventFactory resultEventFactory) {
Assert.notNull(expression, "The expression this action should evaluate is required");
this.expression = expression;
this.evaluationResultExposer = evaluationResultExposer;
this.resultExpression = resultExpression;
this.resultEventFactory = resultEventFactory != null ? resultEventFactory : new DefaultResultEventFactory();
}

View File

@@ -15,7 +15,6 @@
*/
package org.springframework.webflow.action;
import org.springframework.binding.convert.ConversionService;
import org.springframework.binding.expression.Expression;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.Assert;
@@ -40,58 +39,26 @@ public class SetAction extends AbstractAction {
*/
private Expression valueExpression;
/**
* The expected value type.
*/
private Class expectedType;
/**
* The service to perform the type conversion if the actual value type does not match the expected.
*/
private ConversionService conversionService;
/**
* Creates a new set attribute action.
* @param nameExpression the name of the property to set (required)
* @param valueExpression the expression to obtain the new property value (required)
* @param expectedType the expected value type
* @param conversionService the service to perform the type conversion if the actual value type does not match the
* expected
* @param valueExpression the expression to obtain the new property value (required) expected
*/
public SetAction(Expression nameExpression, Expression valueExpression, Class expectedType,
ConversionService conversionService) {
public SetAction(Expression nameExpression, Expression valueExpression) {
Assert.notNull(nameExpression, "The name expression is required");
Assert.notNull(valueExpression, "The value expression is required");
if (expectedType != null) {
Assert.notNull(conversionService, "The conversion service is required if the expectedType is provided");
}
this.nameExpression = nameExpression;
this.valueExpression = valueExpression;
this.expectedType = expectedType;
this.conversionService = conversionService;
}
protected Event doExecute(RequestContext context) throws Exception {
Object value = valueExpression.getValue(context);
nameExpression.setValue(context, applyTypeConversionIfNecessary(value));
nameExpression.setValue(context, value);
return success();
}
/**
* Apply type conversion on the supplied value if necessary.
* @param value the raw value to be converted
*/
private Object applyTypeConversionIfNecessary(Object value) {
if (value == null || expectedType == null) {
return value;
} else {
return conversionService.getConversionExecutor(value.getClass(), expectedType).execute(value);
}
}
public String toString() {
return new ToStringCreator(this).append("name", nameExpression).append("value", valueExpression).append("type",
expectedType).toString();
return new ToStringCreator(this).append("name", nameExpression).append("value", valueExpression).toString();
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.webflow.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;
@@ -88,6 +89,7 @@ class FlowBuilderServicesBeanDefinitionParser extends AbstractSingleBeanDefiniti
.genericBeanDefinition(WEB_FLOW_SPRING_EL_EXPRESSION_PARSER_CLASS_NAME);
webFlowElExpressionParserBuilder
.addConstructorArgValue(springElExpressionParserBuilder.getBeanDefinition());
webFlowElExpressionParserBuilder.addConstructorArgReference(getConversionService(definitionBuilder));
expressionParser = registerInfrastructureComponent(element, context, webFlowElExpressionParserBuilder);
}
definitionBuilder.addPropertyReference(EXPRESSION_PARSER_PROPERTY, expressionParser);
@@ -110,6 +112,12 @@ class FlowBuilderServicesBeanDefinitionParser extends AbstractSingleBeanDefiniti
}
}
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());

View File

@@ -50,6 +50,22 @@ public class BinderConfiguration {
return null;
}
/**
* Gets the converterId for the binding with the specified name. Returns null if either a binding or a converterId
* for the given name is not found.
*
* @param name the name of the binding.
* @return the binding
*/
public String getConverterId(String name) {
Binding binding = getBinding(name);
if (binding != null) {
return binding.getConverter();
} else {
return null;
}
}
/**
* A binding that provides the information needed to connect an element of the view to a property of the model.
*

View File

@@ -48,7 +48,6 @@ import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestScope;
import org.springframework.web.context.support.GenericWebApplicationContext;
import org.springframework.webflow.action.ActionResultExposer;
import org.springframework.webflow.action.EvaluateAction;
import org.springframework.webflow.action.ExternalRedirectAction;
import org.springframework.webflow.action.FlowDefinitionRedirectAction;
@@ -342,7 +341,7 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
flowContext.getBeanFactory().registerScope("conversation", new ConversationScope());
Resource flowResource = flowModelHolder.getFlowModelResource();
flowContext.setResourceLoader(new FlowRelativeResourceLoader(flowResource));
if (JdkVersion.isAtLeastJava15()) {
if (JdkVersion.getMajorJavaVersion() >= JdkVersion.JAVA_15) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(flowContext);
}
new XmlBeanDefinitionReader(flowContext).loadBeanDefinitions(resources);
@@ -852,25 +851,18 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
}
private Action parseEvaluateAction(EvaluateModel evaluate) {
String expressionString = evaluate.getExpression();
Expression expression = getLocalContext().getExpressionParser().parseExpression(expressionString,
new FluentParserContext().evaluate(RequestContext.class));
return new EvaluateAction(expression, parseEvaluationActionResultExposer(evaluate));
}
private ActionResultExposer parseEvaluationActionResultExposer(EvaluateModel evaluate) {
if (StringUtils.hasText(evaluate.getResult())) {
Expression resultExpression = getLocalContext().getExpressionParser().parseExpression(evaluate.getResult(),
new FluentParserContext().evaluate(RequestContext.class));
Class expectedResultType = null;
if (StringUtils.hasText(evaluate.getResultType())) {
expectedResultType = toClass(evaluate.getResultType());
}
return new ActionResultExposer(resultExpression, expectedResultType, getLocalContext()
.getConversionService());
} else {
return null;
FluentParserContext evaluateExpressionParserContext = new FluentParserContext().evaluate(RequestContext.class);
if (StringUtils.hasText(evaluate.getResultType())) {
evaluateExpressionParserContext.expectResult(toClass(evaluate.getResultType()));
}
Expression evaluateExpression = getLocalContext().getExpressionParser().parseExpression(
evaluate.getExpression(), evaluateExpressionParserContext);
Expression resultExpression = null;
if (StringUtils.hasText(evaluate.getResult())) {
resultExpression = getLocalContext().getExpressionParser().parseExpression(evaluate.getResult(),
new FluentParserContext().evaluate(RequestContext.class));
}
return new EvaluateAction(evaluateExpression, resultExpression);
}
private Action parseRenderAction(RenderModel render) {
@@ -889,13 +881,13 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
private Action parseSetAction(SetModel set) {
Expression nameExpression = getLocalContext().getExpressionParser().parseExpression(set.getName(),
new FluentParserContext().evaluate(RequestContext.class));
Expression valueExpression = getLocalContext().getExpressionParser().parseExpression(set.getValue(),
new FluentParserContext().evaluate(RequestContext.class));
Class expectedType = null;
FluentParserContext valueParserContext = new FluentParserContext().evaluate(RequestContext.class);
if (StringUtils.hasText(set.getType())) {
expectedType = toClass(set.getType());
valueParserContext.expectResult(toClass(set.getType()));
}
return new SetAction(nameExpression, valueExpression, expectedType, getLocalContext().getConversionService());
Expression valueExpression = getLocalContext().getExpressionParser().parseExpression(set.getValue(),
valueParserContext);
return new SetAction(nameExpression, valueExpression);
}
private MutableAttributeMap parseMetaAttributes(List attributeModels) {

View File

@@ -15,8 +15,6 @@
*/
package org.springframework.webflow.engine.builder.support;
import java.util.Set;
import org.springframework.binding.convert.ConversionException;
import org.springframework.binding.convert.ConversionExecutionException;
import org.springframework.binding.convert.ConversionExecutor;
@@ -113,7 +111,8 @@ public class FlowBuilderContextImpl implements FlowBuilderContext {
* @return the flow builder conversion service
*/
protected ConversionService createConversionService() {
GenericConversionService service = new GenericConversionService();
GenericConversionService service = new GenericConversionService(getFlowBuilderServices().getConversionService()
.getDelegateConversionService());
service.addConverter(new TextToTransitionCriteria(this));
service.addConverter(new TextToTargetStateResolver(this));
service.setParent(new ParentConversionServiceProxy());
@@ -145,10 +144,6 @@ public class FlowBuilderContextImpl implements FlowBuilderContext {
return getFlowBuilderServices().getConversionService().getConversionExecutor(sourceClass, targetClass);
}
public Set getConversionExecutors(Class sourceClass) {
return getFlowBuilderServices().getConversionService().getConversionExecutors(sourceClass);
}
public ConversionExecutor getConversionExecutor(String id, Class sourceClass, Class targetClass)
throws ConversionExecutorNotFoundException {
return getFlowBuilderServices().getConversionService().getConversionExecutor(id, sourceClass, targetClass);
@@ -158,6 +153,10 @@ public class FlowBuilderContextImpl implements FlowBuilderContext {
return getFlowBuilderServices().getConversionService().getClassForAlias(name);
}
public org.springframework.core.convert.ConversionService getDelegateConversionService() {
return getFlowBuilderServices().getConversionService().getDelegateConversionService();
}
}
}

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.webflow.expression.spel;
import org.springframework.binding.convert.ConversionService;
import org.springframework.binding.expression.spel.SpringELExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
@@ -28,6 +29,15 @@ public class WebFlowSpringELExpressionParser extends SpringELExpressionParser {
public WebFlowSpringELExpressionParser(SpelExpressionParser expressionParser) {
super(expressionParser);
addDefaultPropertyAccessors();
}
public WebFlowSpringELExpressionParser(SpelExpressionParser expressionParser, ConversionService conversionService) {
super(expressionParser, conversionService);
addDefaultPropertyAccessors();
}
private void addDefaultPropertyAccessors() {
addPropertyAccessor(new MessageSourcePropertyAccessor());
addPropertyAccessor(new FlowVariablePropertyAccessor());
addPropertyAccessor(new MapAdaptablePropertyAccessor());

View File

@@ -22,8 +22,9 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.PropertyEditorRegistry;
import org.springframework.binding.convert.ConversionExecutor;
import org.springframework.binding.convert.ConversionService;
import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ExpressionParser;
@@ -35,6 +36,7 @@ import org.springframework.binding.message.Message;
import org.springframework.binding.message.MessageContext;
import org.springframework.binding.message.MessageCriteria;
import org.springframework.binding.message.Severity;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.util.Assert;
import org.springframework.validation.AbstractErrors;
import org.springframework.validation.BindingResult;
@@ -42,7 +44,6 @@ import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.webflow.engine.builder.BinderConfiguration;
import org.springframework.webflow.engine.builder.BinderConfiguration.Binding;
/**
* Makes the properties of the "model" object available to Spring views during rendering. Also makes data binding (aka
@@ -126,7 +127,7 @@ public class BindingModel extends AbstractErrors implements BindingResult {
}
public Class getFieldType(String field) {
return parseFieldExpression(fixedField(field)).getValueType(boundObject);
return parseFieldExpression(fixedField(field), false).getValueType(boundObject);
}
public Object getFieldValue(String field) {
@@ -138,7 +139,7 @@ public class BindingModel extends AbstractErrors implements BindingResult {
return fieldError.getOriginalValue();
}
}
return getFormattedValue(parseFieldExpression(field));
return getFormattedValue(field);
}
// not typically used by mvc views, but implemented to be on the safe side
@@ -172,32 +173,14 @@ public class BindingModel extends AbstractErrors implements BindingResult {
}
public Object getRawFieldValue(String field) {
return parseFieldExpression(fixedField(field)).getValue(boundObject);
return parseFieldExpression(fixedField(field), false).getValue(boundObject);
}
public PropertyEditor findEditor(String field, Class valueType) {
if (conversionService != null) {
String converterId = null;
if (field != null) {
field = fixedField(field);
if (binderConfiguration != null) {
Binding binding = binderConfiguration.getBinding(field);
if (binding != null) {
converterId = binding.getConverter();
}
}
if (valueType == null) {
valueType = parseFieldExpression(field).getValueType(boundObject);
}
}
if (valueType != null) {
return new ConversionExecutorPropertyEditor(conversionService, valueType, converterId);
} else {
return null;
}
} else {
return null;
if (field != null) {
field = fixedField(field);
}
return findSpringConvertingPropertyEditor(field, valueType);
}
// never expected to be called by mvc views
@@ -228,39 +211,69 @@ public class BindingModel extends AbstractErrors implements BindingResult {
// internal helpers
private Expression parseFieldExpression(String field) {
return expressionParser.parseExpression(field, new FluentParserContext().evaluate(boundObject.getClass()));
}
private Object getFormattedValue(Expression fieldExpression) {
ConversionExecutor converter = getConverter(fieldExpression);
if (converter != null) {
return converter.execute(fieldExpression.getValue(boundObject));
} else {
return fieldExpression.getValue(boundObject);
private Expression parseFieldExpression(String field, boolean useResultTypeHint) {
FluentParserContext parserContext = new FluentParserContext().evaluate(boundObject.getClass());
if (useResultTypeHint) {
parserContext.expectResult(String.class);
}
return expressionParser.parseExpression(field, parserContext);
}
private ConversionExecutor getConverter(Expression fieldExpression) {
if (conversionService != null) {
Class valueType = fieldExpression.getValueType(boundObject);
// special handling for array, collection, map types
// necessary as getFieldValue is called by form tags for non-formattable properties, too
// TODO - investigate how to improve this in Spring MVC
if (valueType == null || valueType.isArray() || Collection.class.isAssignableFrom(valueType)
|| Map.class.isAssignableFrom(valueType)) {
return null;
}
if (binderConfiguration != null) {
Binding binding = binderConfiguration.getBinding(fieldExpression.getExpressionString());
if (binding != null) {
String converterId = binding.getConverter();
if (converterId != null) {
return conversionService.getConversionExecutor(converterId, valueType, String.class);
}
private Object getFormattedValue(String field) {
Expression fieldExpression = parseFieldExpression(field, true);
Class valueType = fieldExpression.getValueType(boundObject);
if (isCustomConverterConfigured(field) || avoidConversion(valueType)) {
fieldExpression = parseFieldExpression(fieldExpression.getExpressionString(), false);
}
Object value = fieldExpression.getValue(boundObject);
if ((value instanceof String) == false) {
if (avoidConversion(valueType) == false) {
PropertyEditor editor = findSpringConvertingPropertyEditor(field, valueType);
if (editor != null) {
editor.setValue(value);
value = editor.getAsText();
}
}
return conversionService.getConversionExecutor(valueType, String.class);
}
return value;
}
private boolean isCustomConverterConfigured(String field) {
if (binderConfiguration == null) {
return false;
}
return (binderConfiguration.getConverterId(field) != null);
}
private boolean avoidConversion(Class valueType) {
// special handling for array, collection, map types
// necessary as getFieldValue is called by form tags for non-formattable properties, too
// TODO - investigate how to improve this in Spring MVC
if (valueType == null || valueType.isArray() || Collection.class.isAssignableFrom(valueType)
|| Map.class.isAssignableFrom(valueType)) {
return true;
}
return false;
}
private PropertyEditor findSpringConvertingPropertyEditor(String field, Class valueType) {
if (conversionService != null) {
String converterId = null;
if (field != null) {
if (binderConfiguration != null) {
converterId = binderConfiguration.getConverterId(field);
}
if (valueType == null) {
valueType = parseFieldExpression(field, false).getValueType(boundObject);
}
}
if (valueType != null) {
BeanWrapper accessor = PropertyAccessorFactory.forBeanPropertyAccess(boundObject);
TypeDescriptor typeDescriptor = accessor.getPropertyTypeDescriptor(field);
return new ConvertingPropertyEditorAdapter(conversionService, converterId, typeDescriptor);
} else {
return null;
}
} else {
return null;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2007 the original author or authors.
* 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.
@@ -15,40 +15,63 @@
*/
package org.springframework.webflow.mvc.view;
import java.beans.PropertyEditor;
import java.beans.PropertyEditorSupport;
import org.springframework.binding.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
class ConversionExecutorPropertyEditor extends PropertyEditorSupport {
/**
* <p>
* 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.
* </p>
*
* @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));
}
}
}

View File

@@ -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;
}
}

View File

@@ -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) {
}
}
}

View File

@@ -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());

View File

@@ -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));
}
}

View File

@@ -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");
}
}
}

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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"));
}
}