Skip to content

Instantly share code, notes, and snippets.

@PiotrJander
Created January 27, 2021 17:37
Show Gist options
  • Save PiotrJander/0bbeb53aa445effc2ec2b2483a9f4921 to your computer and use it in GitHub Desktop.
Save PiotrJander/0bbeb53aa445effc2ec2b2483a9f4921 to your computer and use it in GitHub Desktop.
import lombok.Data;
import lombok.Value;
import org.springframework.lang.Nullable;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/** @noinspection unchecked, rawtypes */
class Scratch {
interface Converter<From, To> {
To convert(From from);
ConverterSignature getSignature();
}
// TODO support subtyping
// TODO special handling of newtypes
// TODO skip generic types and higher order converters for now
// class IdentityConverter<A> implements Converter<A, A> {
//
// @Override
// public A convert(A a) {
// return a;
// }
// }
@Data
static class FooId {
private String id;
}
@Data
static class Foo {
private FooId id;
private String name;
}
@Data
static class FooDto {
private String id;
private String name;
}
static class FooIdToStringConverter implements Converter<FooId, String> {
@Override
public String convert(FooId fooId) {
return fooId.getId();
}
@Override
public ConverterSignature getSignature() {
return new ConverterSignature(FooId.class, String.class);
}
}
@Value
static class ConverterSignature {
private Class<?> from;
private Class<?> to;
}
private Map<ConverterSignature, Converter<?, ?>> converterRegistry = new HashMap<>();
@Nullable <From, To> Converter<From, To> getConverter(Class<From> from, Class<To> to) {
//noinspection unchecked
return (Converter<From, To>) converterRegistry.get(new ConverterSignature(from, to));
}
void registerConverter(Converter<?, ?> converter) {
converterRegistry.put(converter.getSignature(), converter);
}
<From, To> Converter<From, To> createConverter(Class<From> fromClass, Class<To> toClass) {
return new Converter<>() {
@SuppressWarnings("checkstyle:MagicNumber")
@Override
public To convert(From from) {
try {
To result = toClass.getDeclaredConstructor().newInstance();
for (Method method : toClass.getMethods()) {
if (method.getName().startsWith("set")) {
Method setter = method;
Class toFieldType = setter.getParameterTypes()[0];
String fieldName = setter.getName().substring(3);
String getterName = "get" + fieldName;
Method getter = fromClass.getMethod(getterName);
Class fromFieldType = getter.getReturnType();
Object fromValue = getter.invoke(from);
if (fromFieldType.equals(toFieldType)) {
setter.invoke(result, fromValue);
} else {
Converter converter = getConverter(fromFieldType, toFieldType);
Object toValue = converter.convert(fromValue);
setter.invoke(result, toValue);
}
}
}
return result;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public ConverterSignature getSignature() {
return new ConverterSignature(fromClass, toClass);
}
};
}
public static void main(String[] args) {
Scratch scratch = new Scratch();
scratch.registerConverter(new FooIdToStringConverter());
Converter<Foo, FooDto> converter = scratch.createConverter(Foo.class, FooDto.class);
Foo foo = new Foo();
FooId id = new FooId();
id.setId("foo");
foo.setId(id);
foo.setName("bar");
FooDto from = converter.convert(foo);
System.out.println(from.toString());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment