package com.googlecode.totallylazy.json; import com.googlecode.totallylazy.Option; import com.googlecode.totallylazy.Sequence; import com.googlecode.totallylazy.collections.PersistentMap; import com.googlecode.totallylazy.collections.PersistentSortedMap; import com.googlecode.totallylazy.functions.Function1; import com.googlecode.totallylazy.reflection.Fields; import com.googlecode.totallylazy.reflection.Methods; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import static com.googlecode.totallylazy.Maps.pairs; import static com.googlecode.totallylazy.Sequences.sequence; import static com.googlecode.totallylazy.Unchecked.cast; import static com.googlecode.totallylazy.collections.PersistentSortedMap.constructors.sortedMap; import static com.googlecode.totallylazy.proxy.Proxy.isProxy; import static com.googlecode.totallylazy.proxy.Proxy.proxy; import static com.googlecode.totallylazy.reflection.Fields.fields; public interface PersistentJsonRecord extends PersistentMap<String, Object> { static <T> T create(Class<T> aClass, Map<String, Object> map) { // TODO: Check is interface Sequence<Method> methods = sequence(aClass.getDeclaredMethods()); PersistentSortedMap<String, Object> result = methods.fold(sortedMap(), (p, m) -> { String name = m.getName(); Object value = map.remove(name); return value == null ? p : p.insert(name, Coercer.coerce(m.getGenericReturnType(), value)); }); return proxy(aClass, new Handler<>(aClass, pairs(map).fold(result, PersistentSortedMap::cons))); } static <T> T parse(Class<T> aClass, String json) { return create(aClass, Json.map(json)); } static <T, R> T modify(T instance, Function1<? super T, R> key, R value){ // TODO: Find interface correctly Class<T> aClass = cast(instance.getClass().getInterfaces()[0]); AtomicReference<String> n = new AtomicReference<>(); key.apply(proxy(aClass, (p, m, a) -> { n.set(m.getName()); return null; })); return proxy(aClass, new Handler<T>(aClass, map(instance).insert(n.get(), value))); } static PersistentMap<String, Object> map(Object instance) { if(isProxy(instance)){ Option<PersistentMap<String, Object>> map = fields(instance.getClass()). filter(f -> f.getType().equals(InvocationHandler.class)). map(f -> Fields.get(f, instance)). safeCast(Handler.class). map(h -> h.map). <PersistentMap<String, Object>>unsafeCast(). headOption(); if(map.isDefined()) return map.get(); } // TODO: Find interface correctly Sequence<Method> methods = sequence(instance.getClass().getInterfaces()[0].getDeclaredMethods()); return methods.fold(sortedMap(), (p, m) -> { String name = m.getName(); Object value = Methods.invoke(m, instance); return value == null ? p : p.insert(name, value); }); } class Handler<T> implements InvocationHandler { final Class<T> aClass; final PersistentMap<String, Object> map; Handler(Class<T> aClass, PersistentMap<String, Object> map) { this.aClass = aClass; this.map = map; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String name = method.getName(); if (name.equals("modify") && (args != null && args.length == 2)) { Function1<? super T, ?> key = cast(args[0]); AtomicReference<String> n = new AtomicReference<>(); key.apply(proxy(aClass, (p, m, a) -> { n.set(m.getName()); return null; })); return proxy(aClass, new Handler<T>(aClass, this.map.insert(n.get(), args[1]))); } if (name.equals("toString") && (args == null || args.length == 0)) { return Json.json(map); } if (method.getDeclaringClass().equals(aClass)) { return map.get(name); } return Methods.invoke(method, map, args); } } }