All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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