diff --git a/src/main/java/com/mengcraft/simpleorm/EbeanHandler.java b/src/main/java/com/mengcraft/simpleorm/EbeanHandler.java index 74dcb4e..f00478b 100644 --- a/src/main/java/com/mengcraft/simpleorm/EbeanHandler.java +++ b/src/main/java/com/mengcraft/simpleorm/EbeanHandler.java @@ -35,8 +35,6 @@ import java.util.UUID; import java.util.logging.Level; -import static com.mengcraft.simpleorm.lib.Reflector.invoke; - @EqualsAndHashCode(of = "id") public class EbeanHandler { @@ -260,20 +258,20 @@ public void initialize() throws DatabaseException { conf.addClass(type); } - ClassLoader mainctx = Thread.currentThread().getContextClassLoader(); + ClassLoader mainCtx = Thread.currentThread().getContextClassLoader(); try { - URLClassLoader plugctx = (URLClassLoader) Reflect.getLoader(plugin); + URLClassLoader cl = (URLClassLoader) Reflect.getLoader(plugin); mapping.forEach(clz -> { for (URL jar : ((URLClassLoader) clz.getClassLoader()).getURLs()) { - invoke(plugctx, "addURL", jar); + Utils.addUrl(cl, jar); } }); - Thread.currentThread().setContextClassLoader(plugctx); + Thread.currentThread().setContextClassLoader(cl); server = EbeanServerFactory.create(conf); } catch (Exception e) { throw new DatabaseException(e); } finally { - Thread.currentThread().setContextClassLoader(mainctx); + Thread.currentThread().setContextClassLoader(mainCtx); } } diff --git a/src/main/java/com/mengcraft/simpleorm/ORM.java b/src/main/java/com/mengcraft/simpleorm/ORM.java index 2d1984e..54fdf67 100644 --- a/src/main/java/com/mengcraft/simpleorm/ORM.java +++ b/src/main/java/com/mengcraft/simpleorm/ORM.java @@ -5,15 +5,14 @@ import com.mengcraft.simpleorm.lib.GsonUtils; import com.mengcraft.simpleorm.lib.LibraryLoader; import com.mengcraft.simpleorm.lib.MavenLibrary; -import com.mengcraft.simpleorm.lib.Reflector; import com.mengcraft.simpleorm.provider.IDataSourceProvider; import com.mengcraft.simpleorm.provider.IRedisProvider; import com.mengcraft.simpleorm.redis.RedisProviders; +import com.mengcraft.simpleorm.serializable.SerializableTypes; import com.zaxxer.hikari.HikariDataSource; import lombok.Getter; import lombok.NonNull; import lombok.SneakyThrows; -import lombok.val; import org.bukkit.Bukkit; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.serialization.ConfigurationSerializable; @@ -30,8 +29,6 @@ import java.util.function.Supplier; import java.util.logging.Level; -import static com.mengcraft.simpleorm.lib.Tuple.tuple; - public class ORM extends JavaPlugin { public static final String PLAYER_METADATA_KEY = "ORM_METADATA"; @@ -192,15 +189,7 @@ public static Gson json() { } public static T deserialize(Class clz, Map map) { - if (ConfigurationSerializable.class.isAssignableFrom(clz)) { - try { - return Reflector.object(clz, tuple(Map.class, map)); - } catch (Exception ignored) { - return Reflector.invoke(clz, "deserialize", tuple(Map.class, map)); - } - } - val json = json(); - return json.fromJson(json.toJsonTree(map), clz); + return SerializableTypes.asDeserializer(clz).deserialize(clz, map); } public static T attr(Player player, @NonNull String key) { diff --git a/src/main/java/com/mengcraft/simpleorm/Reflect.java b/src/main/java/com/mengcraft/simpleorm/Reflect.java index a1f21bc..b2a50e9 100644 --- a/src/main/java/com/mengcraft/simpleorm/Reflect.java +++ b/src/main/java/com/mengcraft/simpleorm/Reflect.java @@ -1,6 +1,7 @@ package com.mengcraft.simpleorm; import com.avaje.ebean.EbeanServer; +import com.mengcraft.simpleorm.lib.Utils; import lombok.SneakyThrows; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginDescriptionFile; @@ -9,12 +10,11 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; -import static com.mengcraft.simpleorm.lib.Reflector.getField; - public final class Reflect { private static final Method SET_DATABASE_ENABLED = INIT_SET_DATABASE_ENABLED(); private static final Field EBEAN = INIT_EBEAN(); + private static final Field JAVA_PLUGIN_classLoader = Utils.getAccessibleField(JavaPlugin.class, "classLoader"); private static Method INIT_SET_DATABASE_ENABLED() { try { @@ -48,8 +48,9 @@ public static void setEbeanServer(Plugin proxy, EbeanServer in) { } } + @SneakyThrows public static ClassLoader getLoader(Plugin plugin) { - return getField(plugin, "classLoader"); + return (ClassLoader) JAVA_PLUGIN_classLoader.get(plugin); } public static boolean isLegacy() { diff --git a/src/main/java/com/mengcraft/simpleorm/lib/GsonUtils.java b/src/main/java/com/mengcraft/simpleorm/lib/GsonUtils.java index a629d84..96a7de7 100644 --- a/src/main/java/com/mengcraft/simpleorm/lib/GsonUtils.java +++ b/src/main/java/com/mengcraft/simpleorm/lib/GsonUtils.java @@ -14,6 +14,10 @@ import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; +import com.mengcraft.simpleorm.serializable.CustomTypeAdapter; +import com.mengcraft.simpleorm.serializable.GsonDeserializer; +import com.mengcraft.simpleorm.serializable.IDeserializer; +import com.mengcraft.simpleorm.serializable.SerializableTypes; import jdk.nashorn.api.scripting.ScriptObjectMirror; import org.bukkit.configuration.serialization.ConfigurationSerializable; @@ -24,7 +28,6 @@ import java.util.stream.StreamSupport; import static com.mengcraft.simpleorm.ORM.nil; -import static com.mengcraft.simpleorm.lib.Tuple.tuple; public class GsonUtils { @@ -60,7 +63,7 @@ public static Gson createJsonInBuk() { public static Gson createJsonInBuk(FieldNamingPolicy policy) { GsonBuilder b = new GsonBuilder(); - b.registerTypeHierarchyAdapter(ConfigurationSerializable.class, new JsonSerializeAdapter()); + b.registerTypeAdapterFactory(CustomTypeAdapter.newTypeHierarchyFactory(ConfigurationSerializable.class, new CustomSerializer())); b.registerTypeAdapter(ScriptObjectMirror.class, new ScriptObjectSerializer()); if (!nil(policy)) { b.setFieldNamingPolicy(policy); @@ -79,22 +82,22 @@ public JsonElement serialize(ScriptObjectMirror obj, Type t, JsonSerializationCo } } - public static class JsonSerializeAdapter implements JsonSerializer, JsonDeserializer { + public static class CustomSerializer implements JsonSerializer, JsonDeserializer { public JsonElement serialize(ConfigurationSerializable input, Type clz, JsonSerializationContext ctx) { return ctx.serialize(input.serialize()); } - public ConfigurationSerializable deserialize(JsonElement jsonElement, Type clz, JsonDeserializationContext ctx) throws JsonParseException { - if (!jsonElement.isJsonObject()) { + public ConfigurationSerializable deserialize(JsonElement element, Type type, JsonDeserializationContext ctx) throws JsonParseException { + if (!element.isJsonObject()) { return null; } - Tuple tuple = tuple(Map.class, dump(jsonElement)); - try { - return Reflector.object((Class) clz, tuple); - } catch (Exception ignored) { + Class cls = (Class) type; + IDeserializer deserializer = SerializableTypes.asDeserializer(cls); + if (deserializer == GsonDeserializer.INSTANCE) {// delegate to defaults + return null; } - return Reflector.invoke(clz, "deserialize", tuple); + return deserializer.deserialize(cls, (Map) dump(element)); } } diff --git a/src/main/java/com/mengcraft/simpleorm/lib/LibraryLoader.java b/src/main/java/com/mengcraft/simpleorm/lib/LibraryLoader.java index fc7eb9e..b200f84 100644 --- a/src/main/java/com/mengcraft/simpleorm/lib/LibraryLoader.java +++ b/src/main/java/com/mengcraft/simpleorm/lib/LibraryLoader.java @@ -1,5 +1,6 @@ package com.mengcraft.simpleorm.lib; +import com.mengcraft.simpleorm.Reflect; import lombok.SneakyThrows; import lombok.val; import org.bukkit.Bukkit; @@ -10,10 +11,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import static com.mengcraft.simpleorm.lib.Reflector.getField; -import static com.mengcraft.simpleorm.lib.Reflector.invoke; - - /** * Created on 15-12-13. */ @@ -48,9 +45,9 @@ public static void load(JavaPlugin plugin, Library library, boolean global) { if (global && GLOBAL_LOADER != null) { classLoader = GLOBAL_LOADER; } else { - classLoader = getField(plugin, "classLoader"); + classLoader = Reflect.getLoader(plugin); } - invoke(classLoader, "addURL", lib.toURI().toURL()); + Utils.addUrl((URLClassLoader) classLoader, lib.toURI().toURL()); plugin.getLogger().info("Load library " + lib + " done"); } diff --git a/src/main/java/com/mengcraft/simpleorm/lib/Reflector.java b/src/main/java/com/mengcraft/simpleorm/lib/Reflector.java deleted file mode 100644 index 35bb819..0000000 --- a/src/main/java/com/mengcraft/simpleorm/lib/Reflector.java +++ /dev/null @@ -1,130 +0,0 @@ -package com.mengcraft.simpleorm.lib; - -import lombok.SneakyThrows; -import lombok.val; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Created by on 2017/7/3. - */ -public enum Reflector { - - REFLECTOR; - - final Map> construct = map(); - final Map> method = map(); - final Map> f = map(); - - - @SneakyThrows - static Field getRef(Class type, String name) { - try { - Field field = type.getDeclaredField(name); - field.setAccessible(true); - return field; - } catch (NoSuchFieldException e) { - if (!(type == Object.class)) { - return getRef(type.getSuperclass(), name); - } - throw e; - } - } - - @SneakyThrows - static Method getRef(Class type, String name, Class[] p) { - try { - val method = type.getDeclaredMethod(name, p); - method.setAccessible(true); - return method; - } catch (NoSuchMethodException e) { - if (!(type == Object.class)) { - return getMethodRef(type.getSuperclass(), name, p); - } - throw e; - } - } - - @SneakyThrows - static Field getFieldRef(Class type, String name) { - Map map = REFLECTOR.f.computeIfAbsent(type, t -> map()); - return map.computeIfAbsent(name, n -> getRef(type, name)); - } - - @SneakyThrows - static Method getMethodRef(Class type, String name, Class[] p) { - Map map = REFLECTOR.method.computeIfAbsent(type, t -> map()); - return map.computeIfAbsent(name + "|" + Arrays.toString(p), n -> getRef(type, name, p)); - } - - static Map map() { - return new ConcurrentHashMap<>(); - } - - static Class[] classArray(Object[] input) { - int len = input.length; - Class[] out = new Class[len]; - while (!(--len == -1)) { - out[len] = input[len].getClass(); - } - return out; - } - - @SneakyThrows - public static T invoke(Object any, String method, Object... input) { - Class[] p = classArray(input); - val i = getMethodRef(Class.class.isInstance(any) ? Class.class.cast(any) : any.getClass(), method, p); - return (T) i.invoke(any, input); - } - - @SneakyThrows - public static T invoke(Object any, String method, Tuple... input) { - Tuple, List> tuple = Tuple.flip(Arrays.asList(input)); - val i = getMethodRef(Class.class.isInstance(any) ? Class.class.cast(any) : any.getClass(), method, tuple.left().toArray(new Class[input.length])); - return (T) i.invoke(any, tuple.right().toArray()); - } - - @SneakyThrows - public static T getField(Object any, String field) { - val i = getFieldRef(any.getClass(), field); - return (T) i.get(any); - } - - @SneakyThrows - public static void setField(Object any, String field, Object what) { - val i = getFieldRef(any.getClass(), field); - i.set(any, what); - } - - @SneakyThrows - public static T object(Class type, Object... param) { - val map = REFLECTOR.construct.computeIfAbsent(type, $ -> map()); - val classArray = classArray(param); - val ref = map.computeIfAbsent(Arrays.toString(classArray), $ -> getRef(type, classArray)); - return (T) ref.newInstance(param); - } - - @SneakyThrows - public static T object(Class type, Tuple... input) { - val map = REFLECTOR.construct.computeIfAbsent(type, $ -> map()); - Tuple, List> tuple = Tuple.flip(Arrays.asList(input)); - Class[] classArray = tuple.left().toArray(new Class[input.length]); - val ref = map.computeIfAbsent(Arrays.toString(classArray), $ -> getRef(type, classArray)); - return (T) ref.newInstance(tuple.right().toArray()); - } - - @SneakyThrows - static Constructor getRef(Class type, Class[] ar) { - val out = type.getDeclaredConstructor(ar); - out.setAccessible(true); - return out; - } - -} diff --git a/src/main/java/com/mengcraft/simpleorm/lib/Tuple.java b/src/main/java/com/mengcraft/simpleorm/lib/Tuple.java deleted file mode 100644 index 63ca732..0000000 --- a/src/main/java/com/mengcraft/simpleorm/lib/Tuple.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.mengcraft.simpleorm.lib; - -import lombok.EqualsAndHashCode; -import lombok.ToString; - -import java.util.ArrayList; -import java.util.List; - -@EqualsAndHashCode -@ToString -public class Tuple { - - private final L left; - private final R right; - - public Tuple(L left, R right) { - this.left = left; - this.right = right; - } - - public static Tuple tuple(L left, R right) { - return new Tuple<>(left, right); - } - - public L left() { - return left; - } - - public R right() { - return right; - } - - public static Tuple, List> flip(List> input) { - List ll = new ArrayList<>(input.size()); - List lr = new ArrayList<>(input.size()); - for (Tuple tuple : input) { - ll.add(tuple.left); - lr.add(tuple.right); - } - return tuple(ll, lr); - } -} diff --git a/src/main/java/com/mengcraft/simpleorm/lib/Utils.java b/src/main/java/com/mengcraft/simpleorm/lib/Utils.java index 36930f3..afddf52 100644 --- a/src/main/java/com/mengcraft/simpleorm/lib/Utils.java +++ b/src/main/java/com/mengcraft/simpleorm/lib/Utils.java @@ -5,6 +5,8 @@ import javax.persistence.Table; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; public class Utils { @@ -50,4 +52,11 @@ public static String translateSqlName(Class cls) { } return separateCamelCase(cls.getSimpleName(), "_").toLowerCase(); } + + private static final Method URL_CLASS_LOADER_addURL = getAccessibleMethod(URLClassLoader.class, "addURL", URL.class); + + @SneakyThrows + public static void addUrl(URLClassLoader cl, URL url) { + URL_CLASS_LOADER_addURL.invoke(cl, url); + } } diff --git a/src/main/java/com/mengcraft/simpleorm/serializable/ConstructorDeserializer.java b/src/main/java/com/mengcraft/simpleorm/serializable/ConstructorDeserializer.java new file mode 100644 index 0000000..476da39 --- /dev/null +++ b/src/main/java/com/mengcraft/simpleorm/serializable/ConstructorDeserializer.java @@ -0,0 +1,19 @@ +package com.mengcraft.simpleorm.serializable; + +import lombok.AllArgsConstructor; +import lombok.SneakyThrows; + +import java.lang.reflect.Constructor; +import java.util.Map; + +@AllArgsConstructor +public class ConstructorDeserializer implements IDeserializer { + + private final Constructor constructor; + + @Override + @SneakyThrows + public T deserialize(Class cls, Map map) { + return (T) constructor.newInstance(map); + } +} diff --git a/src/main/java/com/mengcraft/simpleorm/serializable/CustomTypeAdapter.java b/src/main/java/com/mengcraft/simpleorm/serializable/CustomTypeAdapter.java new file mode 100644 index 0000000..fd66eeb --- /dev/null +++ b/src/main/java/com/mengcraft/simpleorm/serializable/CustomTypeAdapter.java @@ -0,0 +1,153 @@ +package com.mengcraft.simpleorm.serializable; + +import com.google.common.base.Preconditions; +import com.google.gson.Gson; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.internal.Streams; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +import java.io.IOException; +import java.lang.reflect.Type; + +/** + * Copy and modify from TreeTypeAdapter. + * + * @param the bean type + * @author caoli5288@gmail.com + */ +public class CustomTypeAdapter extends TypeAdapter { + + private final JsonSerializer serializer; + private final JsonDeserializer deserializer; + private final Gson gson; + private final TypeToken typeToken; + private final GsonContextImpl context = new GsonContextImpl(); + private final TypeAdapter delegate; + + public CustomTypeAdapter(JsonSerializer serializer, JsonDeserializer deserializer, + Gson gson, TypeToken typeToken, TypeAdapterFactory skipPast) { + this.serializer = serializer; + this.deserializer = deserializer; + this.gson = gson; + this.typeToken = typeToken; + this.delegate = gson.getDelegateAdapter(skipPast, typeToken); + } + + @Override + public T read(JsonReader in) throws IOException { + if (deserializer == null) { + return delegate.read(in); + } + JsonElement value = Streams.parse(in); + if (value.isJsonNull()) { + return null; + } + T deserialized = deserializer.deserialize(value, typeToken.getType(), context); + if (deserialized == null) { + deserialized = delegate.fromJsonTree(value); + } + return deserialized; + } + + @Override + public void write(JsonWriter out, T value) throws IOException { + if (serializer == null) { + delegate.write(out, value); + return; + } + if (value == null) { + out.nullValue(); + return; + } + JsonElement tree = serializer.serialize(value, typeToken.getType(), context); + Streams.write(tree, out); + } + + /** + * Returns a new factory that will match each type against {@code exactType}. + */ + public static TypeAdapterFactory newFactory(TypeToken exactType, Object typeAdapter) { + return new SingleTypeFactory(typeAdapter, exactType, false, null); + } + + /** + * Returns a new factory that will match each type and its raw type against + * {@code exactType}. + */ + public static TypeAdapterFactory newFactoryWithMatchRawType( + TypeToken exactType, Object typeAdapter) { + // only bother matching raw types if exact type is a raw type + boolean matchRawType = exactType.getType() == exactType.getRawType(); + return new SingleTypeFactory(typeAdapter, exactType, matchRawType, null); + } + + /** + * Returns a new factory that will match each type's raw type for assignability + * to {@code hierarchyType}. + */ + public static TypeAdapterFactory newTypeHierarchyFactory( + Class hierarchyType, Object typeAdapter) { + return new SingleTypeFactory(typeAdapter, null, false, hierarchyType); + } + + private static final class SingleTypeFactory implements TypeAdapterFactory { + private final TypeToken exactType; + private final boolean matchRawType; + private final Class hierarchyType; + private final JsonSerializer serializer; + private final JsonDeserializer deserializer; + + SingleTypeFactory(Object typeAdapter, TypeToken exactType, boolean matchRawType, + Class hierarchyType) { + serializer = typeAdapter instanceof JsonSerializer + ? (JsonSerializer) typeAdapter + : null; + deserializer = typeAdapter instanceof JsonDeserializer + ? (JsonDeserializer) typeAdapter + : null; + Preconditions.checkArgument(serializer != null || deserializer != null); + this.exactType = exactType; + this.matchRawType = matchRawType; + this.hierarchyType = hierarchyType; + } + + @SuppressWarnings("unchecked") // guarded by typeToken.equals() call + @Override + public TypeAdapter create(Gson gson, TypeToken type) { + boolean matches = exactType != null + ? exactType.equals(type) || matchRawType && exactType.getType() == type.getRawType() + : hierarchyType.isAssignableFrom(type.getRawType()); + return matches + ? new CustomTypeAdapter<>((JsonSerializer) serializer, + (JsonDeserializer) deserializer, gson, type, this) + : null; + } + } + + private final class GsonContextImpl implements JsonSerializationContext, JsonDeserializationContext { + @Override + public JsonElement serialize(Object src) { + return gson.toJsonTree(src); + } + + @Override + public JsonElement serialize(Object src, Type typeOfSrc) { + return gson.toJsonTree(src, typeOfSrc); + } + + @SuppressWarnings("unchecked") + @Override + public R deserialize(JsonElement json, Type typeOfT) throws JsonParseException { + return gson.fromJson(json, typeOfT); + } + } +} diff --git a/src/main/java/com/mengcraft/simpleorm/serializable/GsonDeserializer.java b/src/main/java/com/mengcraft/simpleorm/serializable/GsonDeserializer.java new file mode 100644 index 0000000..7f8fb2b --- /dev/null +++ b/src/main/java/com/mengcraft/simpleorm/serializable/GsonDeserializer.java @@ -0,0 +1,17 @@ +package com.mengcraft.simpleorm.serializable; + +import com.google.gson.Gson; +import com.mengcraft.simpleorm.ORM; + +import java.util.Map; + +public class GsonDeserializer implements IDeserializer { + + public static final GsonDeserializer INSTANCE = new GsonDeserializer<>(); + + @Override + public T deserialize(Class cls, Map map) { + Gson json = ORM.json(); + return json.fromJson(json.toJsonTree(map), cls); + } +} diff --git a/src/main/java/com/mengcraft/simpleorm/serializable/IDeserializer.java b/src/main/java/com/mengcraft/simpleorm/serializable/IDeserializer.java new file mode 100644 index 0000000..ea96b7a --- /dev/null +++ b/src/main/java/com/mengcraft/simpleorm/serializable/IDeserializer.java @@ -0,0 +1,8 @@ +package com.mengcraft.simpleorm.serializable; + +import java.util.Map; + +public interface IDeserializer { + + T deserialize(Class cls, Map map); +} diff --git a/src/main/java/com/mengcraft/simpleorm/serializable/MethodDeserializer.java b/src/main/java/com/mengcraft/simpleorm/serializable/MethodDeserializer.java new file mode 100644 index 0000000..8d66f85 --- /dev/null +++ b/src/main/java/com/mengcraft/simpleorm/serializable/MethodDeserializer.java @@ -0,0 +1,19 @@ +package com.mengcraft.simpleorm.serializable; + +import lombok.AllArgsConstructor; +import lombok.SneakyThrows; + +import java.lang.reflect.Method; +import java.util.Map; + +@AllArgsConstructor +public class MethodDeserializer implements IDeserializer { + + private final Method method; + + @Override + @SneakyThrows + public T deserialize(Class cls, Map map) { + return (T) method.invoke(cls, map); + } +} diff --git a/src/main/java/com/mengcraft/simpleorm/serializable/SerializableTypes.java b/src/main/java/com/mengcraft/simpleorm/serializable/SerializableTypes.java new file mode 100644 index 0000000..9f5cb2a --- /dev/null +++ b/src/main/java/com/mengcraft/simpleorm/serializable/SerializableTypes.java @@ -0,0 +1,42 @@ +package com.mengcraft.simpleorm.serializable; + +import com.google.common.collect.Maps; +import org.bukkit.configuration.serialization.ConfigurationSerializable; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Map; + +public class SerializableTypes { + + private static final Map, IDeserializer> TYPES = Maps.newHashMap(); + + @SuppressWarnings("all") + public static IDeserializer asDeserializer(Class cls) { + return (IDeserializer) TYPES.computeIfAbsent(cls, SerializableTypes::parse); + } + + private static IDeserializer parse(Class cls) { + if (cls.isAssignableFrom(ConfigurationSerializable.class)) { + for (Constructor constructor : cls.getDeclaredConstructors()) { + Class[] types = constructor.getParameterTypes(); + if (isSerializableTypes(types)) { + constructor.setAccessible(true); + return new ConstructorDeserializer<>(constructor); + } + } + for (Method method : cls.getDeclaredMethods()) { + if ((method.getModifiers() & Modifier.STATIC) != 0 && isSerializableTypes(method.getParameterTypes())) { + method.setAccessible(true); + return new MethodDeserializer<>(method); + } + } + } + return GsonDeserializer.INSTANCE; + } + + private static boolean isSerializableTypes(Class[] types) { + return types.length == 1 && types[0] == Map.class; + } +}