diff --git a/.idea/dictionaries/csh20.xml b/.idea/dictionaries/csh20.xml new file mode 100644 index 0000000..7fec0b9 --- /dev/null +++ b/.idea/dictionaries/csh20.xml @@ -0,0 +1,8 @@ + + + + unserialize + unserializer + + + \ No newline at end of file diff --git a/src/main/java/com/ilummc/tlib/ExampleMain.java b/src/main/java/com/ilummc/tlib/ExampleMain.java index 46af092..ed4022f 100644 --- a/src/main/java/com/ilummc/tlib/ExampleMain.java +++ b/src/main/java/com/ilummc/tlib/ExampleMain.java @@ -1,7 +1,6 @@ package com.ilummc.tlib; import com.ilummc.tlib.annotations.Config; -import com.ilummc.tlib.annotations.ConfigNode; import com.ilummc.tlib.bean.Property; import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; @@ -9,7 +8,6 @@ import org.bukkit.plugin.java.JavaPlugin; @Config(name = "cfg.yml", charset = "GBK") public class ExampleMain extends JavaPlugin { - @ConfigNode("enableUpdate") private Property update = Property.of(false); @Override diff --git a/src/main/java/com/ilummc/tlib/TLib.java b/src/main/java/com/ilummc/tlib/TLib.java index e193b09..f9919a2 100644 --- a/src/main/java/com/ilummc/tlib/TLib.java +++ b/src/main/java/com/ilummc/tlib/TLib.java @@ -1,5 +1,6 @@ package com.ilummc.tlib; +import com.ilummc.tlib.annotations.Config; import com.ilummc.tlib.annotations.Dependency; import com.ilummc.tlib.annotations.Logger; import com.ilummc.tlib.inject.DependencyInjector; @@ -20,9 +21,15 @@ public class TLib { @Logger("§3[§6TLib§3|{1}§3] §f{2}") private TLogger tLogger; + private TLibConfig config; + private TLib() { } + public TLibConfig getConfig() { + return config; + } + public TLogger getLogger() { return tLogger; } @@ -48,4 +55,14 @@ public class TLib { } } + @Config(name = "tlib.yml") + public class TLibConfig { + + private int downloadPoolSize = 4; + + public int getDownloadPoolSize() { + return downloadPoolSize; + } + } + } diff --git a/src/main/java/com/ilummc/tlib/annotations/Config.java b/src/main/java/com/ilummc/tlib/annotations/Config.java index ca16a4f..03b792a 100644 --- a/src/main/java/com/ilummc/tlib/annotations/Config.java +++ b/src/main/java/com/ilummc/tlib/annotations/Config.java @@ -4,6 +4,7 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.lang.reflect.Modifier; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @@ -15,12 +16,10 @@ public @interface Config { boolean saveOnExit() default false; - boolean readOnly() default true; - - boolean fixUnicode() default true; - String charset() default "UTF-8"; boolean listenChanges() default false; + int excludeModifiers() default Modifier.STATIC | Modifier.TRANSIENT; + } diff --git a/src/main/java/com/ilummc/tlib/annotations/ConfigNode.java b/src/main/java/com/ilummc/tlib/annotations/ConfigNode.java deleted file mode 100644 index 7e2478d..0000000 --- a/src/main/java/com/ilummc/tlib/annotations/ConfigNode.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.ilummc.tlib.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.RUNTIME) -public @interface ConfigNode { - - String value(); -} diff --git a/src/main/java/com/ilummc/tlib/bean/PropertyTypeAdaptor.java b/src/main/java/com/ilummc/tlib/bean/PropertyTypeAdaptor.java new file mode 100644 index 0000000..f802433 --- /dev/null +++ b/src/main/java/com/ilummc/tlib/bean/PropertyTypeAdaptor.java @@ -0,0 +1,16 @@ +package com.ilummc.tlib.bean; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; + +import java.lang.reflect.Type; + +public class PropertyTypeAdaptor implements JsonDeserializer { + + @Override + public Property deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context) throws JsonParseException { + return null; + } +} diff --git a/src/main/java/com/ilummc/tlib/dependency/TDependency.java b/src/main/java/com/ilummc/tlib/dependency/TDependency.java index 5488a45..b4bbcf2 100644 --- a/src/main/java/com/ilummc/tlib/dependency/TDependency.java +++ b/src/main/java/com/ilummc/tlib/dependency/TDependency.java @@ -55,7 +55,7 @@ public class TDependency { .url(dl == null ? url + "/" + groupId.replace('.', '/') + "/" + artifactId + "/" + version + "/" + artifactId + "-" + version + ".jar" : dl) .file(target) - .setThreads(8) + .setThreads(TLib.getTLib().getConfig().getDownloadPoolSize()) .setOnStart(event -> lock.lock()) .setOnConnected(event -> TLib.getTLib().getLogger().info(" 正在下载 " + String.join(":", new String[]{groupId, artifactId, version}) + diff --git a/src/main/java/com/ilummc/tlib/inject/DependencyInjector.java b/src/main/java/com/ilummc/tlib/inject/DependencyInjector.java index faca285..e09a874 100644 --- a/src/main/java/com/ilummc/tlib/inject/DependencyInjector.java +++ b/src/main/java/com/ilummc/tlib/inject/DependencyInjector.java @@ -1,10 +1,7 @@ package com.ilummc.tlib.inject; import com.ilummc.tlib.TLib; -import com.ilummc.tlib.annotations.Dependencies; -import com.ilummc.tlib.annotations.Dependency; -import com.ilummc.tlib.annotations.Logger; -import com.ilummc.tlib.annotations.PluginInstance; +import com.ilummc.tlib.annotations.*; import com.ilummc.tlib.dependency.TDependency; import com.ilummc.tlib.util.TLogger; import org.bukkit.Bukkit; @@ -31,12 +28,25 @@ public class DependencyInjector { } private static void injectConfig(Plugin plugin, Object o) { - + for (Field field : o.getClass().getDeclaredFields()) { + try { + Config config; + if ((config = field.getType().getAnnotation(Config.class)) != null) { + field.setAccessible(true); + Object obj = TConfigInjector.loadConfig(plugin, field.getType()); + if (obj != null) { + TLib.getTLib().getLogger().info("插件 " + plugin.getName() + " 的 " + config.name() + " 配置文件成功加载"); + field.set(o, obj); + } + } + } catch (IllegalAccessException ignored) { + } + } } private static void injectLogger(Plugin plugin, Object o) { - try { - for (Field field : o.getClass().getDeclaredFields()) { + for (Field field : o.getClass().getDeclaredFields()) { + try { Logger logger; if ((logger = field.getAnnotation(Logger.class)) != null) { field.getType().asSubclass(TLogger.class); @@ -45,8 +55,8 @@ public class DependencyInjector { field.setAccessible(true); field.set(o, tLogger); } + } catch (Exception ignored) { } - } catch (Exception ignored) { } } diff --git a/src/main/java/com/ilummc/tlib/inject/TConfigInjector.java b/src/main/java/com/ilummc/tlib/inject/TConfigInjector.java index 76921a9..447484a 100644 --- a/src/main/java/com/ilummc/tlib/inject/TConfigInjector.java +++ b/src/main/java/com/ilummc/tlib/inject/TConfigInjector.java @@ -1,6 +1,161 @@ package com.ilummc.tlib.inject; +import com.google.common.collect.Lists; +import com.google.common.io.Files; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.annotations.SerializedName; +import com.ilummc.tlib.TLib; +import com.ilummc.tlib.annotations.Config; +import com.ilummc.tlib.bean.Property; +import org.apache.commons.lang3.Validate; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.plugin.Plugin; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.nio.charset.Charset; +import java.util.*; +import java.util.stream.Collectors; + public class TConfigInjector { + public static Object loadConfig(Plugin plugin, Class clazz) { + try { + Config config = clazz.getAnnotation(Config.class); + Validate.notNull(config); + File file = new File(plugin.getDataFolder(), config.name()); + if (!file.exists()) if (config.fromJar()) plugin.saveResource(config.name(), true); + else saveConfig(plugin, clazz); + return unserialize(plugin, clazz); + } catch (NullPointerException e) { + TLib.getTLib().getLogger().warn("插件 " + plugin + " 的配置类 " + clazz.getSimpleName() + " 加载失败:没有 @Config 注解"); + } catch (Exception e) { + TLib.getTLib().getLogger().warn("插件 " + plugin + " 的配置类 " + clazz.getSimpleName() + " 加载失败"); + } + return null; + } + + public static Object unserialize(Plugin plugin, Class clazz) { + try { + Config config = clazz.getAnnotation(Config.class); + Validate.notNull(config); + return new GsonBuilder().disableHtmlEscaping().excludeFieldsWithModifiers(config.excludeModifiers()) + .create().fromJson(new Gson().toJson(new Yaml() + .dump(Files.toString(new File(plugin.getDataFolder(), config.name()), Charset.forName(config.charset())))), clazz); + } catch (NullPointerException e) { + TLib.getTLib().getLogger().warn("插件 " + plugin + " 的配置类 " + clazz.getSimpleName() + " 加载失败:没有 @Config 注解"); + return null; + } catch (Exception e) { + try { + return clazz.newInstance(); + } catch (InstantiationException | IllegalAccessException e1) { + TLib.getTLib().getLogger().warn("插件 " + plugin + " 的配置类 " + clazz.getSimpleName() + " 加载失败"); + return null; + } + } + } + + public static Map serialize(Plugin plugin, Class clazz) { + try { + Constructor constructor = clazz.getConstructor(); + constructor.setAccessible(true); + Config config = clazz.getAnnotation(Config.class); + Validate.notNull(config); + return new Serializer(new LinkedHashMap<>(), constructor.newInstance(), config.excludeModifiers()).get(); + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { + TLib.getTLib().getLogger().warn("插件 " + plugin + " 的配置类 " + clazz.getSimpleName() + " 序列化失败:没有无参构造方法"); + } catch (NullPointerException e) { + TLib.getTLib().getLogger().warn("插件 " + plugin + " 的配置类 " + clazz.getSimpleName() + " 序列化失败:没有 @Config 注解"); + } catch (Exception e) { + TLib.getTLib().getLogger().warn("插件 " + plugin + " 的配置类 " + clazz.getSimpleName() + " 序列化失败"); + } + return null; + } + + public static void saveConfig(Plugin plugin, Class clazz) throws IOException, NullPointerException { + Object obj = serialize(plugin, clazz); + Validate.notNull(obj); + Config config = clazz.getAnnotation(Config.class); + Validate.notNull(config); + File target = new File(plugin.getDataFolder(), config.name()); + if (!target.exists()) target.createNewFile(); + DumperOptions options = new DumperOptions(); + options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + Yaml yaml = new Yaml(options); + String str = yaml.dump(obj); + byte[] arr = str.getBytes(config.charset()); + Files.write(arr, target); + } + + private static final List primitiveType = Lists.newArrayList(Integer.class, + Double.class, Float.class, Boolean.class, Short.class, Byte.class, Character.class, Long.class, String.class); + + private static class Serializer { + + private HashMap map; + private Object o; + private int modifiers; + + private Serializer(HashMap map, Object o, int modifiers) { + this.map = map; + this.o = o; + this.modifiers = modifiers; + } + + private HashMap get() { + for (Field field : o.getClass().getDeclaredFields()) { + if ((field.getModifiers() & modifiers) == 0 && !field.isSynthetic()) + try { + SerializedName node = field.getAnnotation(SerializedName.class); + if (!field.isAccessible()) field.setAccessible(true); + Object obj = field.get(o); + map.put(node == null ? field.getName() : node.value(), serialize(obj)); + } catch (Exception ignored) { + } + } + return map; + } + + @SuppressWarnings({"unchecked"}) + private Object serialize(Object o) { + try { + if (o.getClass().isPrimitive() || primitiveType.contains(o.getClass())) { + return o; + } else if (o.getClass().isArray()) { + List list = new ArrayList(); + int len = (int) o.getClass().getField("length").get(o); + for (int i = 0; i < len; i++) { + list.add(serialize(Array.get(o, i))); + } + return list; + } else if (o instanceof Collection) { + return ((Collection) o).stream().map(this::serialize).collect(Collectors.toList()); + } else if (o instanceof Map) { + Map map = new LinkedHashMap<>(); + ((Map) o).forEach((o1, o2) -> map.put((String) o1, serialize(o2))); + return map; + } else if (o instanceof ConfigurationSerializable) { + Map map = new LinkedHashMap(); + map.put("==", o.getClass().getName()); + map.putAll(((ConfigurationSerializable) o).serialize()); + return map; + } else if (o instanceof Property) { + return serialize(((Property) o).get()); + } else { + return new Serializer(new HashMap<>(), o, modifiers).get(); + } + } catch (Exception ignored) { + return null; + } + } + + } }