Skip to content

Instantly share code, notes, and snippets.

@DasBrain
Created June 8, 2022 15:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DasBrain/7ca2c989be92c03139bc54fbfce533fd to your computer and use it in GitHub Desktop.
Save DasBrain/7ca2c989be92c03139bc54fbfce533fd to your computer and use it in GitHub Desktop.
import static java.lang.invoke.MethodType.methodType;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.RecordComponent;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Arrays;
import java.util.Map;
public sealed interface Deserializer<T> permits DeserializerImpl<T> {
T deserialize(Map<String, Object> map);
static <T extends Record> Deserializer<T> forRecord(Lookup lookup) {
return DeserializerImpl.forRecord(lookup);
}
}
record DeserializerImpl<T> (MethodHandle mh) implements Deserializer<T> {
private static final MethodHandle MAP_GET;
static {
try {
MAP_GET = MethodHandles.lookup().findVirtual(Map.class, "get",
methodType(Object.class, Object.class));
} catch (ReflectiveOperationException roe) {
throw new ExceptionInInitializerError(roe);
}
}
@SuppressWarnings("unchecked")
public T deserialize(Map<String, Object> map) {
try {
return (T) (Object) mh.invokeExact(map);
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable t) {
throw newUndeclaredThrowableException(t);
}
}
// To keep deserialize short(er)
private static UndeclaredThrowableException newUndeclaredThrowableException(Throwable t) {
return new UndeclaredThrowableException(t);
}
static <T extends Record> Deserializer<T> forRecord(Lookup l) {
try {
Class<? extends Record> recordClass = l.lookupClass().asSubclass(Record.class);
RecordComponent[] components = recordClass.getRecordComponents();
Class<?>[] componentClasses = Arrays.stream(components).map(RecordComponent::getType)
.toArray(Class<?>[]::new);
MethodHandle constructor = l.findConstructor(recordClass,
methodType(void.class, componentClasses));
MethodHandle erased = constructor.asType(constructor.type().generic());
MethodHandle[] filters = Arrays.stream(components)
.map(c -> MethodHandles.insertArguments(MAP_GET, 1, c.getName()))
.toArray(MethodHandle[]::new);
MethodHandle filtered = MethodHandles.filterArguments(erased, 0, filters);
MethodHandle shuffled = MethodHandles.permuteArguments(filtered,
methodType(Object.class, Map.class), new int[components.length]);
return new DeserializerImpl<>(shuffled);
} catch (IllegalAccessException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment