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

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