
com.github.rschmitt.dynamicobject.DynamicObjects Maven / Gradle / Ivy
package com.github.rschmitt.dynamicobject;
import java.io.Writer;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.Proxy;
import java.util.concurrent.ConcurrentHashMap;
import static com.github.rschmitt.dynamicobject.ClojureStuff.*;
import static java.lang.String.format;
public class DynamicObjects {
private static volatile Object readers = EMPTY_MAP;
private static final ConcurrentHashMap, String> recordTagCache = new ConcurrentHashMap<>();
private static final ConcurrentHashMap, EdnTranslatorAdapter>> translatorCache = new ConcurrentHashMap<>();
static > String serialize(T o) {
Class type = o.getType();
if (translatorCache.containsKey(type))
return (String) PRINT_STRING.invoke(o);
return (String) PRINT_STRING.invoke(o.getMap());
}
@SuppressWarnings("unchecked")
static > T deserialize(String edn, Class type) {
Object obj = READ_STRING.invoke(getReadersAsOptions(), edn);
if (obj instanceof DynamicObject)
return (T) obj;
return wrap(obj, type);
}
@SuppressWarnings("unchecked")
static > T wrap(Object map, Class type) {
Class> typeMetadata = Metadata.getTypeMetadata(map);
if (typeMetadata != null && !type.equals(typeMetadata))
throw new ClassCastException(String.format("Attempted to wrap a map tagged as %s in type %s",
typeMetadata.getSimpleName(), type.getSimpleName()));
try {
Constructor lookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
lookupConstructor.setAccessible(true);
return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class>[]{type},
new DynamicObjectInvocationHandler<>(map, type, lookupConstructor));
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}
static > T newInstance(Class type) {
return wrap(Metadata.withTypeMetadata(EMPTY_MAP, type), type);
}
static synchronized void registerType(Class type, EdnTranslator translator) {
EdnTranslatorAdapter adapter = new EdnTranslatorAdapter<>(translator);
translatorCache.put(type, adapter);
readers = ASSOC.invoke(readers, cachedRead(translator.getTag()), adapter);
defineMultimethod(type.getTypeName(), "DynamicObjects/invokeWriter", translator.getTag());
}
@SuppressWarnings("unchecked")
static synchronized void deregisterType(Class type) {
EdnTranslatorAdapter adapter = (EdnTranslatorAdapter) translatorCache.get(type);
readers = DISSOC.invoke(readers, cachedRead(adapter.getTag()));
REMOVE_METHOD.invoke(PRINT_METHOD, adapter);
translatorCache.remove(type);
}
static synchronized > void registerTag(Class type, String tag) {
recordTagCache.put(type, tag);
readers = ASSOC.invoke(readers, cachedRead(tag), new RecordReader<>(type));
defineMultimethod(":" + type.getTypeName(), "RecordPrinter/printRecord", tag);
}
static synchronized > void deregisterTag(Class type) {
String tag = recordTagCache.get(type);
readers = DISSOC.invoke(readers, cachedRead(tag));
recordTagCache.remove(type);
Object dispatchVal = cachedRead(":" + type.getTypeName());
REMOVE_METHOD.invoke(PRINT_METHOD, dispatchVal);
}
private static Object getReadersAsOptions() {
return ASSOC.invoke(EMPTY_MAP, READERS, readers);
}
@SuppressWarnings("unused")
public static Object invokeWriter(Object obj, Writer writer, String tag) {
EdnTranslatorAdapter translator = (EdnTranslatorAdapter>) GET.invoke(readers, cachedRead(tag));
return translator.invoke(obj, writer);
}
private static void defineMultimethod(String dispatchVal, String method, String arg) {
String clojureCode = format("(defmethod print-method %s [o, ^java.io.Writer w] (com.github.rschmitt.dynamicobject.%s o w \"%s\"))",
dispatchVal, method, arg);
EVAL.invoke(READ_STRING.invoke(clojureCode));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy