added conversion service performance optimizations; added mapping cyclical ref handling; removed ConverterInfo in favor of specifying S and T at registration time if necessary

This commit is contained in:
Keith Donald
2009-10-07 16:54:36 +00:00
parent c9f0e68c82
commit acf574c3e3
17 changed files with 248 additions and 99 deletions

View File

@@ -1,41 +0,0 @@
/*
* Copyright 2002-2009 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.core.convert.converter;
/**
* A meta interface a Converter may implement to describe what types he can convert between.
*
* Implementing this interface is required when registering converters that do not declare their
* parameterized types S and T with a {@link org.springframework.core.convert.ConversionService}.
*
* @author Keith Donald
* @since 3.0
* @see Converter
*/
public interface ConverterInfo {
/**
* The source type the converter converts from.
*/
Class<?> getSourceType();
/**
* The target type the converter converts to.
*/
Class<?> getTargetType();
}

View File

@@ -16,6 +16,7 @@
package org.springframework.core.convert.support;
import java.util.Locale;
/**
* Default implementation of a conversion service. Will automatically register <i>from string</i>
@@ -31,15 +32,15 @@ public class DefaultConversionService extends GenericConversionService {
* Create a new default conversion service, installing the default converters.
*/
public DefaultConversionService() {
addConverter(new StringToBooleanConverter());
addConverter(new StringToCharacterConverter());
addConverter(new StringToLocaleConverter());
addConverter(new NumberToCharacterConverter());
addConverter(new ObjectToStringConverter());
addConverterFactory(new StringToNumberConverterFactory());
addConverterFactory(new StringToEnumConverterFactory());
addConverterFactory(new NumberToNumberConverterFactory());
addConverterFactory(new CharacterToNumberFactory());
addConverter(String.class, Boolean.class, new StringToBooleanConverter());
addConverter(String.class, Character.class, new StringToCharacterConverter());
addConverter(String.class, Locale.class, new StringToLocaleConverter());
addConverter(Number.class, Character.class, new NumberToCharacterConverter());
addConverter(Object.class, String.class, new ObjectToStringConverter());
addConverterFactory(String.class, Number.class, new StringToNumberConverterFactory());
addConverterFactory(String.class, Enum.class, new StringToEnumConverterFactory());
addConverterFactory(Number.class, Number.class, new NumberToNumberConverterFactory());
addConverterFactory(Character.class, Number.class, new CharacterToNumberFactory());
}
}

View File

@@ -33,7 +33,6 @@ import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.core.convert.converter.ConverterInfo;
import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -54,7 +53,7 @@ public class GenericConversionService implements ConversionService, ConverterReg
}
};
private final Map<Class, Map<Class, GenericConverter>> sourceTypeConverters = new HashMap<Class, Map<Class, GenericConverter>>();
private final Map<Class, Map<Class, GenericConverter>> sourceTypeConverters = new HashMap<Class, Map<Class, GenericConverter>>(36);
private ConversionService parent;
@@ -132,7 +131,7 @@ public class GenericConversionService implements ConversionService, ConverterReg
}
Class sourceType = typeInfo[0];
Class targetType = typeInfo[1];
getSourceMap(sourceType).put(targetType, new ConverterAdapter(converter));
addConverter(sourceType, targetType, converter);
}
public void addConverterFactory(ConverterFactory<?, ?> converterFactory) {
@@ -143,7 +142,7 @@ public class GenericConversionService implements ConversionService, ConverterReg
}
Class sourceType = typeInfo[0];
Class targetType = typeInfo[1];
getSourceMap(sourceType).put(targetType, new ConverterFactoryAdapter(converterFactory));
addConverterFactory(sourceType, targetType, converterFactory);
}
public void removeConvertible(Class<?> sourceType, Class<?> targetType) {
@@ -196,6 +195,28 @@ public class GenericConversionService implements ConversionService, ConverterReg
getSourceMap(sourceType).put(targetType, converter);
}
/**
* Registers a Converter with the sourceType and targetType to index on specified explicitly.
* This method performs better than {@link #addConverter(Converter)} because there parameterized types S and T don't have to be discovered.
* @param sourceType the source type to convert from
* @param targetType the target type to convert to
* @param converter the converter.
*/
protected void addConverter(Class<?> sourceType, Class<?> targetType, Converter<?, ?> converter) {
addGenericConverter(sourceType, targetType, new ConverterAdapter(converter));
}
/**
* Registers a ConverterFactory with the sourceType and targetType to index on specified explicitly.
* This method performs better than {@link #addConverter(ConverterFactory)} because there parameterized types S and T don't have to be discovered.
* @param sourceType the source type to convert from
* @param targetType the target type to convert to
* @param converter the converter.factory
*/
protected void addConverterFactory(Class<?> sourceType, Class<?> targetType, ConverterFactory<?, ?> converterFactory) {
addGenericConverter(sourceType, targetType, new ConverterFactoryAdapter(converterFactory));
}
/**
* Hook method to convert a null source.
* Default implementation returns <code>null</code>.
@@ -259,15 +280,7 @@ public class GenericConversionService implements ConversionService, ConverterReg
}
private Class[] getRequiredTypeInfo(Object converter, Class genericIfc) {
Class[] typeInfo = new Class[2];
if (converter instanceof ConverterInfo) {
ConverterInfo info = (ConverterInfo) converter;
typeInfo[0] = info.getSourceType();
typeInfo[1] = info.getTargetType();
return typeInfo;
} else {
return GenericTypeResolver.resolveTypeArguments(converter.getClass(), genericIfc);
}
return GenericTypeResolver.resolveTypeArguments(converter.getClass(), genericIfc);
}
private GenericConverter findConverterByClassPair(Class sourceType, Class targetType) {

View File

@@ -37,7 +37,7 @@ public interface GenericConverter {
/**
* Convert the source to the targetType described by the TypeDescriptor.
* @param source the source object to convert (never <code>null</code>)
* @param source the source object to convert (may be null)
* @param sourceType context about the source type to convert from
* @param targetType context about the target type to convert to
* @return the converted object

View File

@@ -47,11 +47,11 @@ final class NumberToNumberConverterFactory implements ConverterFactory<Number, N
private final static class NumberToNumber<T extends Number> implements Converter<Number, T> {
private final Class<T> targetType;
public NumberToNumber(Class<T> targetType) {
this.targetType = targetType;
}
public T convert(Number source) {
return NumberUtils.convertNumberToTargetClass(source, this.targetType);
}

View File

@@ -28,6 +28,14 @@ import org.springframework.core.convert.converter.Converter;
*/
final class ObjectToStringConverter implements Converter<Object, String> {
public Class<?> getSourceType() {
return Object.class;
}
public Class<?> getTargetType() {
return String.class;
}
public String convert(Object source) {
return source.toString();
}

View File

@@ -48,8 +48,7 @@ final class StringToBooleanConverter implements Converter<String, Boolean> {
trueValues.add("1");
falseValues.add("0");
}
public Boolean convert(String source) {
String value = (source != null ? source.trim() : null);
if (!StringUtils.hasLength(value)) {

View File

@@ -31,7 +31,9 @@ final class StringToCharacterConverter implements Converter<String, Character> {
return null;
}
if (source.length() > 1) {
throw new IllegalArgumentException("Can only convert a [String] with length of 1 to a [Character]; string value '" + source + "' has length of " + source.length());
throw new IllegalArgumentException(
"Can only convert a [String] with length of 1 to a [Character]; string value '" + source
+ "' has length of " + source.length());
}
return source.charAt(0);
}

View File

@@ -35,7 +35,7 @@ final class StringToEnumConverterFactory implements ConverterFactory<String, Enu
private class StringToEnum<T extends Enum> implements Converter<String, T> {
private final Class<T> enumType;
public StringToEnum(Class<T> enumType) {
this.enumType = enumType;
}

View File

@@ -47,11 +47,11 @@ final class StringToNumberConverterFactory implements ConverterFactory<String, N
private static final class StringToNumber<T extends Number> implements Converter<String, T> {
private final Class<T> targetType;
public StringToNumber(Class<T> targetType) {
this.targetType = targetType;
}
public T convert(String source) {
if ("".equals(source)) {
return null;