Created
June 8, 2022 15:33
-
-
Save DasBrain/7ca2c989be92c03139bc54fbfce533fd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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