com.github.datalking.common.convert.GenericConversionService Maven / Gradle / Ivy
package com.github.datalking.common.convert;
import com.github.datalking.common.ConfigurableConversionService;
import com.github.datalking.common.GenericTypeResolver;
import com.github.datalking.common.convert.converter.ConditionalConverter;
import com.github.datalking.common.convert.converter.ConditionalGenericConverter;
import com.github.datalking.common.convert.converter.Converter;
import com.github.datalking.common.convert.converter.ConverterFactory;
import com.github.datalking.common.convert.converter.GenericConverter;
import com.github.datalking.common.convert.converter.GenericConverter.ConvertiblePair;
import com.github.datalking.common.convert.descriptor.TypeDescriptor;
import com.github.datalking.util.Assert;
import com.github.datalking.util.ClassUtils;
import com.github.datalking.util.ObjectUtils;
import com.github.datalking.util.StringUtils;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author yaoo on 5/10/18
*/
public class GenericConversionService implements ConfigurableConversionService {
private static final GenericConverter NO_OP_CONVERTER = new NoOpConverter("NO_OP");
private static final GenericConverter NO_MATCH = new NoOpConverter("NO_MATCH");
private final Converters converters = new Converters();
private final Map converterCache = new ConcurrentHashMap<>(64);
// implementing ConverterRegistry
@Override
public void addConverter(Converter, ?> converter) {
GenericConverter.ConvertiblePair typeInfo = getRequiredTypeInfo(converter, Converter.class);
if (typeInfo == null) {
throw new IllegalArgumentException("Unable to determine source typeand target typefor "+ converter.getClass().getName());
}
addConverter(new ConverterAdapter(converter, typeInfo));
}
@Override
public void addConverter(Class> sourceType, Class> targetType, Converter, ?> converter) {
GenericConverter.ConvertiblePair typeInfo = new GenericConverter.ConvertiblePair(sourceType, targetType);
addConverter(new ConverterAdapter(converter, typeInfo));
}
@Override
public void addConverter(GenericConverter converter) {
this.converters.add(converter);
invalidateCache();
}
@Override
public void addConverterFactory(ConverterFactory, ?> factory) {
GenericConverter.ConvertiblePair typeInfo = getRequiredTypeInfo(factory, ConverterFactory.class);
if (typeInfo == null) {
throw new IllegalArgumentException("Unable to determine source typeand target typefor " + factory.getClass().getName());
}
addConverter(new ConverterFactoryAdapter(factory, typeInfo));
}
@Override
public void removeConvertible(Class> sourceType, Class> targetType) {
this.converters.remove(sourceType, targetType);
invalidateCache();
}
// implementing ConversionService
public boolean canConvert(Class> sourceType, Class> targetType) {
Assert.notNull(targetType, "targetType to convert to cannot be null");
return canConvert((sourceType != null ? TypeDescriptor.valueOf(sourceType) : null),
TypeDescriptor.valueOf(targetType));
}
public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
Assert.notNull(targetType, "targetType to convert to cannot be null");
if (sourceType == null) {
return true;
}
GenericConverter converter = getConverter(sourceType, targetType);
return (converter != null);
}
public boolean canBypassConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
Assert.notNull(targetType, "targetType to convert to cannot be null");
if (sourceType == null) {
return true;
}
GenericConverter converter = getConverter(sourceType, targetType);
return (converter == NO_OP_CONVERTER);
}
public T convert(Object source, Class targetType) {
Assert.notNull(targetType, "targetType to convert to cannot be null");
return (T) convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(targetType));
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
Assert.notNull(targetType, "targetType to convert to cannot be null");
if (sourceType == null) {
Assert.isTrue(source == null, "source must be [null] if sourceType == [null]");
return handleResult(null, targetType, convertNullSource(null, targetType));
}
if (source != null && !sourceType.getObjectType().isInstance(source)) {
throw new IllegalArgumentException("source to convert from must be an instance of " +
sourceType + "; instead it was a " + source.getClass().getName());
}
GenericConverter converter = getConverter(sourceType, targetType);
if (converter != null) {
Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
return handleResult(sourceType, targetType, result);
}
return handleConverterNotFound(source, sourceType, targetType);
}
public Object convert(Object source, TypeDescriptor targetType) {
return convert(source, TypeDescriptor.forObject(source), targetType);
}
@Override
public String toString() {
return this.converters.toString();
}
// Protected template methods
protected Object convertNullSource(TypeDescriptor sourceType, TypeDescriptor targetType) {
return null;
}
protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
GenericConverter converter = this.converterCache.get(key);
if (converter != null) {
return (converter != NO_MATCH ? converter : null);
}
converter = this.converters.find(sourceType, targetType);
if (converter == null) {
converter = getDefaultConverter(sourceType, targetType);
}
if (converter != null) {
this.converterCache.put(key, converter);
return converter;
}
this.converterCache.put(key, NO_MATCH);
return null;
}
protected GenericConverter getDefaultConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
return (sourceType.isAssignableTo(targetType) ? NO_OP_CONVERTER : null);
}
// internal helpers
private GenericConverter.ConvertiblePair getRequiredTypeInfo(Object converter, Class> genericIfc) {
Class>[] args = GenericTypeResolver.resolveTypeArguments(converter.getClass(), genericIfc);
return (args != null ? new GenericConverter.ConvertiblePair(args[0], args[1]) : null);
}
private void invalidateCache() {
this.converterCache.clear();
}
private Object handleConverterNotFound(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
assertNotPrimitiveTargetType(sourceType, targetType);
return source;
}
if (sourceType.isAssignableTo(targetType) && targetType.getObjectType().isInstance(source)) {
return source;
}
try {
throw new Exception(sourceType + "," + targetType);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private Object handleResult(TypeDescriptor sourceType, TypeDescriptor targetType, Object result) {
if (result == null) {
assertNotPrimitiveTargetType(sourceType, targetType);
}
return result;
}
private void assertNotPrimitiveTargetType(TypeDescriptor sourceType, TypeDescriptor targetType) {
if (targetType.isPrimitive()) {
throw new IllegalArgumentException("A null value cannot be assigned to a primitive type");
}
}
/**
* Converter -> GenericConverter
*/
private final class ConverterAdapter implements ConditionalGenericConverter {
private final Converter
© 2015 - 2025 Weber Informatics LLC | Privacy Policy