package de.tutorials;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
public class ConverterExample {
/**
* @param args
*/
public static void main(String[] args) {
IConverter converter = new Converter();
converter.register(new StringToIntegerConversion());
converter.register(new StringToCollectionStringConversion());
converter.register(new StringToCollectionIntegerConversion());
converter.register(new StringToClassConversion());
converter.register(new StringToGenericClassConversion());
int value = converter.convert("4711", Integer.class);
System.out.println(value);
Collection<String> values = converter.convert("1,2,3,4",new TypeVariable<Collection<String>>() {});
System.out.println(values + " " + values.iterator().next().getClass());
Collection<Integer> numbers = converter.convert("1,2,3,4",new TypeVariable<Collection<Integer>>() {});
System.out.println(numbers + " " + numbers.iterator().next().getClass());
Class<?> clazz = converter.convert("java.lang.String", new TypeVariable<Class<?>>(){});
System.out.println(clazz);
Class<?> clazz1 = converter.convert("java.lang.String", Class.class);
System.out.println(clazz1);
}
static class StringToClassConversion implements IConversion<String,Class>{
public Class convert(String input) {
try {
return Class.forName(input);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
static class StringToGenericClassConversion implements IConversion<String,Class<?>>{
public Class<?> convert(String input) {
try {
return Class.forName(input);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
static class StringToIntegerConversion implements
IConversion<String, Integer> {
public Integer convert(String input) {
return Integer.valueOf(input);
}
}
static class StringToCollectionStringConversion implements
IConversion<String, Collection<String>> {
public Collection<String> convert(String input) {
return Arrays.asList(input.split(","));
}
}
static class StringToCollectionIntegerConversion implements
IConversion<String, Collection<Integer>> {
public Collection<Integer> convert(String input) {
Collection<Integer> numbers = new ArrayList<Integer>();
for (String item : input.split(",")) {
numbers.add(Integer.valueOf(item));
}
return numbers;
}
}
static interface IConversion<TInput extends Object, TOutput extends Object> {
TOutput convert(TInput input);
}
static interface IConverter {
void register(IConversion<? extends Object, ? extends Object> conversion);
<TOutput> TOutput convert(Object o, Type outputType);
<TOutput> TOutput convert(Object o, TypeVariable<?> typeVariable);
}
static class Converter implements IConverter {
final static String CONVERSION_METHOD_NAME = "convert";
Map<Type, Map<Type, IConversion<Object, Object>>> inputToOutputConverters;
public Converter() {
inputToOutputConverters = new LinkedHashMap<Type, Map<Type, IConversion<Object, Object>>>();
}
@Override
@SuppressWarnings("unchecked")
public void register(
IConversion<? extends Object, ? extends Object> conversion) {
ConversionInfo conversionInfo = analyze(conversion);
if (conversionInfo != null) {
Map<Type, IConversion<Object, Object>> outputToConverter = inputToOutputConverters
.get(conversionInfo.getInputType());
if (outputToConverter == null) {
outputToConverter = new LinkedHashMap<Type, IConversion<Object, Object>>();
inputToOutputConverters.put(conversionInfo.getInputType(),
outputToConverter);
}
outputToConverter.put(conversionInfo.getOutputType(),
(IConversion<Object, Object>) conversion);
}
}
@Override
@SuppressWarnings("unchecked")
public <TOutput> TOutput convert(Object o, TypeVariable<?> typeVariable) {
return (TOutput) convert(o, typeVariable.getType());
}
@Override
@SuppressWarnings("unchecked")
public <TOutput> TOutput convert(Object o, Type outputType) {
Type compatibleInputType = findCompatibleInputType(o.getClass());
Type compatibleOutputType = findCompatiblOutputType(
compatibleInputType, outputType);
TOutput output = null;
if (compatibleInputType != null && compatibleOutputType != null) {
IConversion<Object, Object> conversion = inputToOutputConverters
.get(compatibleInputType).get(compatibleOutputType);
System.out.println(String.format(
"Using conversion: %s to convert from: %s to: %s",
conversion, compatibleInputType, compatibleOutputType));
output = (TOutput) conversion.convert(o);
}
return output;
}
private Type findCompatibleInputType(Type inputType) {
Type result = null;
if (inputType != null) {
if (inputToOutputConverters.containsKey(inputType)) {
result = inputType;
} else {
for (Type candidate : inputToOutputConverters.keySet()) {
if (typesAreCompatible(candidate, inputType)) {
result = candidate;
break;
}
}
}
}
return result;
}
private Type findCompatiblOutputType(Type inputType, Type outputType) {
Type result = null;
if (inputType != null && outputType != null) {
if (inputToOutputConverters.get(inputType).containsKey(
outputType)) {
result = outputType;
} else {
for (Type candidate : inputToOutputConverters
.get(inputType).keySet()) {
if (typesAreCompatible(candidate, outputType)) {
result = candidate;
break;
}
}
}
}
return result;
}
@SuppressWarnings("unchecked")
private boolean rawTypesAreCompatible(Type candidate, Type inputType) {
return ((Class) candidate).isAssignableFrom((Class) inputType);
}
private boolean typesAreCompatible(Type candidate, Type type) {
boolean result = false;
if (type instanceof Class && candidate instanceof Class
&& rawTypesAreCompatible(candidate, type)) {
result = true;
}
if (!result && type instanceof ParameterizedType
&& candidate instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) type;
ParameterizedType pCandidate = (ParameterizedType) candidate;
if (rawTypesAreCompatible(pCandidate.getRawType(), pType
.getRawType())
&& Arrays.equals(pCandidate.getActualTypeArguments(),
pType.getActualTypeArguments())) {
result = true;
}
}
return result;
}
private ConversionInfo analyze(
IConversion<? extends Object, ? extends Object> converter) {
ConversionInfo conversionInfo = null;
for (Method conversionMethodCandidate : converter.getClass()
.getDeclaredMethods()) {
if (isConversionMethod(conversionMethodCandidate)) {
Type returnType = conversionMethodCandidate.getReturnType();
Type parameterType = conversionMethodCandidate
.getParameterTypes()[0];
if (conversionMethodCandidate.getGenericReturnType() instanceof ParameterizedType) {
returnType = (ParameterizedType) conversionMethodCandidate
.getGenericReturnType();
if (conversionMethodCandidate
.getGenericParameterTypes()[0] instanceof ParameterizedType) {
parameterType = (ParameterizedType) conversionMethodCandidate
.getGenericParameterTypes()[0];
}
}
conversionInfo = new ConversionInfo(parameterType,
returnType);
break;
}
}
return conversionInfo;
}
private boolean isConversionMethod(Method conversionMethodCandidate) {
return CONVERSION_METHOD_NAME.equals(conversionMethodCandidate
.getName())
&& conversionMethodCandidate.getParameterTypes().length == 1
&& !Void.class.equals(conversionMethodCandidate
.getReturnType())
&& !conversionMethodCandidate.isBridge();
}
}
static class ConversionInfo {
Type inputType;
Type outputType;
public ConversionInfo(Type inputType, Type outputType) {
this.inputType = inputType;
this.outputType = outputType;
}
public Type getInputType() {
return inputType;
}
public Type getOutputType() {
return outputType;
}
}
static class TypeVariable<TType> {
Type type;
public TypeVariable() {
this.type = ((ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
public Type getType() {
return type;
}
}
}