custom converters

This commit is contained in:
Keith Donald
2008-07-17 13:15:17 +00:00
parent 387a223972
commit 71afa7f4c1
5 changed files with 148 additions and 27 deletions

View File

@@ -15,6 +15,8 @@
*/
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
* safely cached for use by client code.
@@ -35,8 +37,8 @@ public interface ConversionService {
public Object executeConversion(Object source, Class targetClass) throws ConversionException;
/**
* Return the conversion executor capable of converting source objects of the specified <code>sourceClass</code>
* to instances of the <code>targetClass</code>.
* Return the default conversion executor capable of converting source objects of the specified
* <code>sourceClass</code> to instances of the <code>targetClass</code>.
* <p>
* The returned ConversionExecutor is thread-safe and may safely be cached for use in client code.
* @param sourceClass the source class to convert from (required)
@@ -47,17 +49,31 @@ public interface ConversionService {
public ConversionExecutor getConversionExecutor(Class sourceClass, Class targetClass)
throws ConversionExecutorNotFoundException;
/**
* Return the custom conversion executor capable of converting source objects of the specified
* <code>sourceClass</code> to instances of the <code>targetClass</code>.
* <p>
* The returned ConversionExecutor is thread-safe and may safely be cached for use in client code.
* @param id the id of the custom conversion executor (required)
* @param sourceClass the source class to convert from (required)
* @param targetClass the target class to convert to (required)
* @return the executor that can execute instance type conversion, never null
* @throws ConversionExecutorNotFoundException when no suitable conversion executor could be found
*/
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>ConversionExecutor[] getConversionExecutor(String.class)</code> would return all converters that
* convert from String to some other Object.
* 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 ConversionExecutor[] getConversionExecutors(Class sourceClass);
public Set getConversionExecutors(Class sourceClass);
/**
* Lookup a class by it alias. For example, <code>long</code> for <code>java.lang.Long</code>
* Lookup a class by its well-known alias. For example, <code>long</code> for <code>java.lang.Long</code>
* @param alias the class alias
* @return the class, or <code>null</code> if no alias exists
*/

View File

@@ -50,6 +50,10 @@ public class FormattedStringToNumber extends StringToObject {
private boolean lenient;
public FormattedStringToNumber() {
super(Number.class);
}
public FormattedStringToNumber(Class numberClass) {
super(numberClass);
}

View File

@@ -16,13 +16,12 @@
package org.springframework.binding.convert.service;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -52,6 +51,12 @@ public class GenericConversionService implements ConversionService {
*/
private final Map sourceClassConverters = new HashMap();
/**
* A map of custom converters. Custom converters are assigned a unique identifier that can be used to lookup the
* converter. This allows multiple converters for the same source->target class to be registered.
*/
private final Map customConverters = new HashMap();
/**
* Indexes classes by well-known aliases.
*/
@@ -83,14 +88,6 @@ public class GenericConversionService implements ConversionService {
public void addConverter(Converter converter) {
Class sourceClass = converter.getSourceClass();
Class targetClass = converter.getTargetClass();
if (sourceClass.isPrimitive()) {
throw new IllegalArgumentException("Invalid Converter " + converter
+ "; A primitive type is not allowed for the [sourceClass] argument");
}
if (targetClass.isPrimitive()) {
throw new IllegalArgumentException("Invalid Converter " + converter
+ "; A primitive type is not allowed for the [targetClass] argument");
}
Map sourceMap = getSourceMap(sourceClass);
sourceMap.put(targetClass, converter);
if (converter instanceof TwoWayConverter) {
@@ -99,6 +96,15 @@ public class GenericConversionService implements ConversionService {
}
}
/**
* Add given custom converter to this conversion service.
* @param id the id of the custom converter instance
* @param converter the converter
*/
public void addConverter(String id, Converter converter) {
customConverters.put(id, converter);
}
/**
* Add an alias for given target type.
*/
@@ -154,12 +160,42 @@ public class GenericConversionService implements ConversionService {
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() + "'");
"No ConversionExecutor found for converting from sourceClass [" + sourceClass.getName()
+ "] to target class [" + targetClass.getName() + "]");
}
}
}
public ConversionExecutor getConversionExecutor(String id, Class sourceClass, Class targetClass)
throws ConversionExecutorNotFoundException {
Converter converter = (Converter) customConverters.get(id);
if (converter == null) {
throw new ConversionExecutorNotFoundException(sourceClass, targetClass,
"No custom ConversionExecutor found with id '" + id + "' for converting from sourceClass ["
+ sourceClass.getName() + "] to targetClass [" + targetClass.getName() + "]");
}
if (converter.getSourceClass().isAssignableFrom(sourceClass)) {
if (!converter.getTargetClass().isAssignableFrom(targetClass)) {
throw new ConversionExecutorNotFoundException(sourceClass, targetClass,
"Custom ConversionExecutor with id '" + id + "' cannot convert from sourceClass ["
+ sourceClass.getName() + "] to targetClass [" + targetClass.getName() + "]");
}
return new StaticConversionExecutor(sourceClass, targetClass, converter);
} else if (converter.getTargetClass().isAssignableFrom(sourceClass) && converter instanceof TwoWayConverter) {
if (!converter.getSourceClass().isAssignableFrom(targetClass)) {
throw new ConversionExecutorNotFoundException(sourceClass, targetClass,
"Custom ConversionExecutor with id '" + id + "' cannot convert from sourceClass ["
+ sourceClass.getName() + "] to targetClass [" + targetClass.getName() + "]");
}
TwoWayConverter twoWay = (TwoWayConverter) converter;
return new StaticConversionExecutor(sourceClass, targetClass, new ReverseConverter(twoWay));
} else {
throw new ConversionExecutorNotFoundException(sourceClass, targetClass,
"Custom ConversionExecutor with id '" + id + "' cannot convert from sourceClass ["
+ sourceClass.getName() + "] to targetClass [" + targetClass.getName() + "]");
}
}
private Converter findRegisteredConverter(Class sourceClass, Class targetClass) {
if (sourceClass.isInterface()) {
LinkedList classQueue = new LinkedList();
@@ -224,20 +260,27 @@ public class GenericConversionService implements ConversionService {
// subclassing support
public ConversionExecutor[] getConversionExecutors(Class sourceClass) {
public Set getConversionExecutors(Class sourceClass) {
Set parentExecutors;
if (parent != null) {
parentExecutors = parent.getConversionExecutors(sourceClass);
} else {
parentExecutors = Collections.EMPTY_SET;
}
Map sourceMap = getSourceMap(sourceClass);
if (sourceMap.isEmpty()) {
return new ConversionExecutor[0];
if (parentExecutors.isEmpty() && sourceMap.isEmpty()) {
return Collections.EMPTY_SET;
}
Set entries = sourceMap.entrySet();
List conversionExecutors = new ArrayList(entries.size());
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));
}
return (ConversionExecutor[]) conversionExecutors.toArray(new ConversionExecutor[entries.size()]);
conversionExecutors.addAll(parentExecutors);
return conversionExecutors;
}
/**

View File

@@ -16,6 +16,8 @@
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;
@@ -88,9 +90,9 @@ public class BeanWrapperExpression implements Expression {
public void setValue(Object context, Object value) {
try {
BeanWrapperImpl beanWrapper = new BeanWrapperImpl(context);
ConversionExecutor[] converters = conversionService.getConversionExecutors(String.class);
for (int i = 0; i < converters.length; i++) {
ConversionExecutor converter = converters[i];
Set converters = conversionService.getConversionExecutors(String.class);
for (Iterator it = converters.iterator(); it.hasNext();) {
ConversionExecutor converter = (ConversionExecutor) it.next();
beanWrapper.registerCustomEditor(converter.getTargetClass(), new PropertyEditorConverter(converter));
}
beanWrapper.setPropertyValue(expression, value);