|
|
|
@ -1,5 +1,5 @@ |
|
|
|
/* |
|
|
|
/* |
|
|
|
* Copyright 2002-2014 the original author or authors. |
|
|
|
* Copyright 2002-2015 the original author or authors. |
|
|
|
* |
|
|
|
* |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
@ -92,7 +92,7 @@ public class GenericConversionService implements ConfigurableConversionService { |
|
|
|
new ConcurrentHashMap<ConverterCacheKey, GenericConverter>(64); |
|
|
|
new ConcurrentHashMap<ConverterCacheKey, GenericConverter>(64); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// implementing ConverterRegistry
|
|
|
|
// ConverterRegistry implementation
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void addConverter(Converter<?, ?> converter) { |
|
|
|
public void addConverter(Converter<?, ?> converter) { |
|
|
|
@ -104,7 +104,8 @@ public class GenericConversionService implements ConfigurableConversionService { |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void addConverter(Class<?> sourceType, Class<?> targetType, Converter<?, ?> converter) { |
|
|
|
public void addConverter(Class<?> sourceType, Class<?> targetType, Converter<?, ?> converter) { |
|
|
|
addConverter(new ConverterAdapter(converter, ResolvableType.forClass(sourceType), ResolvableType.forClass(targetType))); |
|
|
|
addConverter(new ConverterAdapter( |
|
|
|
|
|
|
|
converter, ResolvableType.forClass(sourceType), ResolvableType.forClass(targetType))); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
@ -116,7 +117,7 @@ public class GenericConversionService implements ConfigurableConversionService { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void addConverterFactory(ConverterFactory<?, ?> converterFactory) { |
|
|
|
public void addConverterFactory(ConverterFactory<?, ?> converterFactory) { |
|
|
|
ResolvableType[] typeInfo = getRequiredTypeInfo(converterFactory, ConverterFactory.class); |
|
|
|
ResolvableType[] typeInfo = getRequiredTypeInfo(converterFactory, ConverterFactory.class); |
|
|
|
Assert.notNull("Unable to the determine sourceType <S> and targetRangeType R which your " + |
|
|
|
Assert.notNull(typeInfo, "Unable to the determine source type <S> and target range type R which your " + |
|
|
|
"ConverterFactory<S, R> converts between; declare these generic types."); |
|
|
|
"ConverterFactory<S, R> converts between; declare these generic types."); |
|
|
|
addConverter(new ConverterFactoryAdapter(converterFactory, |
|
|
|
addConverter(new ConverterFactoryAdapter(converterFactory, |
|
|
|
new ConvertiblePair(typeInfo[0].resolve(), typeInfo[1].resolve()))); |
|
|
|
new ConvertiblePair(typeInfo[0].resolve(), typeInfo[1].resolve()))); |
|
|
|
@ -128,7 +129,8 @@ public class GenericConversionService implements ConfigurableConversionService { |
|
|
|
invalidateCache(); |
|
|
|
invalidateCache(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// implementing ConversionService
|
|
|
|
|
|
|
|
|
|
|
|
// ConversionService implementation
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public boolean canConvert(Class<?> sourceType, Class<?> targetType) { |
|
|
|
public boolean canConvert(Class<?> sourceType, Class<?> targetType) { |
|
|
|
@ -148,17 +150,18 @@ public class GenericConversionService implements ConfigurableConversionService { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Returns true if conversion between the sourceType and targetType can be bypassed. |
|
|
|
* Return whether conversion between the sourceType and targetType can be bypassed. |
|
|
|
* More precisely this method will return true if objects of sourceType can be |
|
|
|
* <p>More precisely, this method will return true if objects of sourceType can be |
|
|
|
* converted to the targetType by returning the source object unchanged. |
|
|
|
* converted to the targetType by returning the source object unchanged. |
|
|
|
* @param sourceType context about the source type to convert from (may be null if source is null) |
|
|
|
* @param sourceType context about the source type to convert from |
|
|
|
|
|
|
|
* (may be {@code null} if source is {@code null}) |
|
|
|
* @param targetType context about the target type to convert to (required) |
|
|
|
* @param targetType context about the target type to convert to (required) |
|
|
|
* @return true if conversion can be bypassed |
|
|
|
* @return {@code true} if conversion can be bypassed; {@code false} otherwise |
|
|
|
* @throws IllegalArgumentException if targetType is null |
|
|
|
* @throws IllegalArgumentException if targetType is {@code null} |
|
|
|
* @since 3.2 |
|
|
|
* @since 3.2 |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public boolean canBypassConvert(TypeDescriptor sourceType, TypeDescriptor targetType) { |
|
|
|
public boolean canBypassConvert(TypeDescriptor sourceType, TypeDescriptor targetType) { |
|
|
|
Assert.notNull(targetType, "The targetType to convert to cannot be null"); |
|
|
|
Assert.notNull(targetType, "targetType to convert to cannot be null"); |
|
|
|
if (sourceType == null) { |
|
|
|
if (sourceType == null) { |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -169,19 +172,19 @@ public class GenericConversionService implements ConfigurableConversionService { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
public <T> T convert(Object source, Class<T> targetType) { |
|
|
|
public <T> T convert(Object source, Class<T> targetType) { |
|
|
|
Assert.notNull(targetType,"The targetType to convert to cannot be null"); |
|
|
|
Assert.notNull(targetType, "targetType to convert to cannot be null"); |
|
|
|
return (T) convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(targetType)); |
|
|
|
return (T) convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(targetType)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { |
|
|
|
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { |
|
|
|
Assert.notNull(targetType,"The targetType to convert to cannot be null"); |
|
|
|
Assert.notNull(targetType, "targetType to convert to cannot be null"); |
|
|
|
if (sourceType == null) { |
|
|
|
if (sourceType == null) { |
|
|
|
Assert.isTrue(source == null, "The source must be [null] if sourceType == [null]"); |
|
|
|
Assert.isTrue(source == null, "source must be [null] if sourceType == [null]"); |
|
|
|
return handleResult(sourceType, targetType, convertNullSource(sourceType, targetType)); |
|
|
|
return handleResult(null, targetType, convertNullSource(null, targetType)); |
|
|
|
} |
|
|
|
} |
|
|
|
if (source != null && !sourceType.getObjectType().isInstance(source)) { |
|
|
|
if (source != null && !sourceType.getObjectType().isInstance(source)) { |
|
|
|
throw new IllegalArgumentException("The source to convert from must be an instance of " + |
|
|
|
throw new IllegalArgumentException("source to convert from must be an instance of " + |
|
|
|
sourceType + "; instead it was a " + source.getClass().getName()); |
|
|
|
sourceType + "; instead it was a " + source.getClass().getName()); |
|
|
|
} |
|
|
|
} |
|
|
|
GenericConverter converter = getConverter(sourceType, targetType); |
|
|
|
GenericConverter converter = getConverter(sourceType, targetType); |
|
|
|
@ -202,8 +205,8 @@ public class GenericConversionService implements ConfigurableConversionService { |
|
|
|
* @param targetType the target type |
|
|
|
* @param targetType the target type |
|
|
|
* @return the converted value |
|
|
|
* @return the converted value |
|
|
|
* @throws ConversionException if a conversion exception occurred |
|
|
|
* @throws ConversionException if a conversion exception occurred |
|
|
|
* @throws IllegalArgumentException if targetType is null, |
|
|
|
* @throws IllegalArgumentException if targetType is {@code null}, |
|
|
|
* or sourceType is null but source is not null |
|
|
|
* or sourceType is {@code null} but source is not {@code null} |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public Object convert(Object source, TypeDescriptor targetType) { |
|
|
|
public Object convert(Object source, TypeDescriptor targetType) { |
|
|
|
return convert(source, TypeDescriptor.forObject(source), targetType); |
|
|
|
return convert(source, TypeDescriptor.forObject(source), targetType); |
|
|
|
@ -218,11 +221,11 @@ public class GenericConversionService implements ConfigurableConversionService { |
|
|
|
// Protected template methods
|
|
|
|
// Protected template methods
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Template method to convert a null source. |
|
|
|
* Template method to convert a {@code null} source. |
|
|
|
* <p>Default implementation returns {@code null} or the Java 8 |
|
|
|
* <p>The default implementation returns {@code null} or the Java 8 |
|
|
|
* {@link java.util.Optional#empty()} instance if the target type is |
|
|
|
* {@link java.util.Optional#empty()} instance if the target type is |
|
|
|
* {@code java.uti.Optional}. |
|
|
|
* {@code java.util.Optional}. Subclasses may override this to return |
|
|
|
* Subclasses may override to return custom null objects for specific target types. |
|
|
|
* custom {@code null} objects for specific target types. |
|
|
|
* @param sourceType the sourceType to convert from |
|
|
|
* @param sourceType the sourceType to convert from |
|
|
|
* @param targetType the targetType to convert to |
|
|
|
* @param targetType the targetType to convert to |
|
|
|
* @return the converted null object |
|
|
|
* @return the converted null object |
|
|
|
@ -239,11 +242,10 @@ public class GenericConversionService implements ConfigurableConversionService { |
|
|
|
* First queries this ConversionService's converter cache. |
|
|
|
* First queries this ConversionService's converter cache. |
|
|
|
* On a cache miss, then performs an exhaustive search for a matching converter. |
|
|
|
* On a cache miss, then performs an exhaustive search for a matching converter. |
|
|
|
* If no converter matches, returns the default converter. |
|
|
|
* If no converter matches, returns the default converter. |
|
|
|
* Subclasses may override. |
|
|
|
|
|
|
|
* @param sourceType the source type to convert from |
|
|
|
* @param sourceType the source type to convert from |
|
|
|
* @param targetType the target type to convert to |
|
|
|
* @param targetType the target type to convert to |
|
|
|
* @return the generic converter that will perform the conversion, or {@code null} if |
|
|
|
* @return the generic converter that will perform the conversion, |
|
|
|
* no suitable converter was found |
|
|
|
* or {@code null} if no suitable converter was found |
|
|
|
* @see #getDefaultConverter(TypeDescriptor, TypeDescriptor) |
|
|
|
* @see #getDefaultConverter(TypeDescriptor, TypeDescriptor) |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) { |
|
|
|
protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) { |
|
|
|
@ -269,9 +271,8 @@ public class GenericConversionService implements ConfigurableConversionService { |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Return the default converter if no converter is found for the given sourceType/targetType pair. |
|
|
|
* Return the default converter if no converter is found for the given sourceType/targetType pair. |
|
|
|
* Returns a NO_OP Converter if the sourceType is assignable to the targetType. |
|
|
|
* <p>Returns a NO_OP Converter if the sourceType is assignable to the targetType. |
|
|
|
* Returns {@code null} otherwise, indicating no suitable converter could be found. |
|
|
|
* Returns {@code null} otherwise, indicating no suitable converter could be found. |
|
|
|
* Subclasses may override. |
|
|
|
|
|
|
|
* @param sourceType the source type to convert from |
|
|
|
* @param sourceType the source type to convert from |
|
|
|
* @param targetType the target type to convert to |
|
|
|
* @param targetType the target type to convert to |
|
|
|
* @return the default generic converter that will perform the conversion |
|
|
|
* @return the default generic converter that will perform the conversion |
|
|
|
@ -280,7 +281,8 @@ public class GenericConversionService implements ConfigurableConversionService { |
|
|
|
return (sourceType.isAssignableTo(targetType) ? NO_OP_CONVERTER : null); |
|
|
|
return (sourceType.isAssignableTo(targetType) ? NO_OP_CONVERTER : null); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// internal helpers
|
|
|
|
|
|
|
|
|
|
|
|
// Internal helpers
|
|
|
|
|
|
|
|
|
|
|
|
private ResolvableType[] getRequiredTypeInfo(Object converter, Class<?> genericIfc) { |
|
|
|
private ResolvableType[] getRequiredTypeInfo(Object converter, Class<?> genericIfc) { |
|
|
|
ResolvableType resolvableType = ResolvableType.forClass(converter.getClass()).as(genericIfc); |
|
|
|
ResolvableType resolvableType = ResolvableType.forClass(converter.getClass()).as(genericIfc); |
|
|
|
@ -303,7 +305,7 @@ public class GenericConversionService implements ConfigurableConversionService { |
|
|
|
private Object handleConverterNotFound(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { |
|
|
|
private Object handleConverterNotFound(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { |
|
|
|
if (source == null) { |
|
|
|
if (source == null) { |
|
|
|
assertNotPrimitiveTargetType(sourceType, targetType); |
|
|
|
assertNotPrimitiveTargetType(sourceType, targetType); |
|
|
|
return source; |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
if (sourceType.isAssignableTo(targetType) && targetType.getObjectType().isInstance(source)) { |
|
|
|
if (sourceType.isAssignableTo(targetType) && targetType.getObjectType().isInstance(source)) { |
|
|
|
return source; |
|
|
|
return source; |
|
|
|
@ -375,7 +377,7 @@ public class GenericConversionService implements ConfigurableConversionService { |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public String toString() { |
|
|
|
public String toString() { |
|
|
|
return this.typeInfo + " : " + this.converter; |
|
|
|
return (this.typeInfo + " : " + this.converter); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -425,7 +427,7 @@ public class GenericConversionService implements ConfigurableConversionService { |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public String toString() { |
|
|
|
public String toString() { |
|
|
|
return this.typeInfo + " : " + this.converterFactory; |
|
|
|
return (this.typeInfo + " : " + this.converterFactory); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -453,20 +455,20 @@ public class GenericConversionService implements ConfigurableConversionService { |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
ConverterCacheKey otherKey = (ConverterCacheKey) other; |
|
|
|
ConverterCacheKey otherKey = (ConverterCacheKey) other; |
|
|
|
return ObjectUtils.nullSafeEquals(this.sourceType, otherKey.sourceType) && |
|
|
|
return (ObjectUtils.nullSafeEquals(this.sourceType, otherKey.sourceType) && |
|
|
|
ObjectUtils.nullSafeEquals(this.targetType, otherKey.targetType); |
|
|
|
ObjectUtils.nullSafeEquals(this.targetType, otherKey.targetType)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public int hashCode() { |
|
|
|
public int hashCode() { |
|
|
|
return ObjectUtils.nullSafeHashCode(this.sourceType) * 29 + |
|
|
|
return (ObjectUtils.nullSafeHashCode(this.sourceType) * 29 + |
|
|
|
ObjectUtils.nullSafeHashCode(this.targetType); |
|
|
|
ObjectUtils.nullSafeHashCode(this.targetType)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public String toString() { |
|
|
|
public String toString() { |
|
|
|
return "ConverterCacheKey [sourceType = " + this.sourceType + |
|
|
|
return ("ConverterCacheKey [sourceType = " + this.sourceType + |
|
|
|
", targetType = " + this.targetType + "]"; |
|
|
|
", targetType = " + this.targetType + "]"); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -544,9 +546,9 @@ public class GenericConversionService implements ConfigurableConversionService { |
|
|
|
return converter; |
|
|
|
return converter; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
// Check ConditionalGenericConverter that match all types
|
|
|
|
// Check ConditionalConverters for a dynamic match
|
|
|
|
for (GenericConverter globalConverter : this.globalConverters) { |
|
|
|
for (GenericConverter globalConverter : this.globalConverters) { |
|
|
|
if (((ConditionalConverter)globalConverter).matches(sourceType, targetType)) { |
|
|
|
if (((ConditionalConverter) globalConverter).matches(sourceType, targetType)) { |
|
|
|
return globalConverter; |
|
|
|
return globalConverter; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -563,6 +565,7 @@ public class GenericConversionService implements ConfigurableConversionService { |
|
|
|
Set<Class<?>> visited = new HashSet<Class<?>>(20); |
|
|
|
Set<Class<?>> visited = new HashSet<Class<?>>(20); |
|
|
|
addToClassHierarchy(0, ClassUtils.resolvePrimitiveIfNecessary(type), false, hierarchy, visited); |
|
|
|
addToClassHierarchy(0, ClassUtils.resolvePrimitiveIfNecessary(type), false, hierarchy, visited); |
|
|
|
boolean array = type.isArray(); |
|
|
|
boolean array = type.isArray(); |
|
|
|
|
|
|
|
|
|
|
|
int i = 0; |
|
|
|
int i = 0; |
|
|
|
while (i < hierarchy.size()) { |
|
|
|
while (i < hierarchy.size()) { |
|
|
|
Class<?> candidate = hierarchy.get(i); |
|
|
|
Class<?> candidate = hierarchy.get(i); |
|
|
|
|