From 0410f5e19d2fcf539fe13b529e025ac7a15ef74a Mon Sep 17 00:00:00 2001 From: Redempt Date: Mon, 20 Dec 2021 23:50:05 -0500 Subject: [PATCH] Fix collection loading to use proper YAML lists - Added wrapper DataHolder to abstract away implementation details of storage from the converters - Switch SQLCache to use synchronized maps, which can store null keys and values --- res/plugin.yml | 2 +- src/redempt/redlib/config/ConfigManager.java | 18 +++- .../conversion/CollectionConverter.java | 20 ++-- .../config/conversion/MapConverter.java | 11 ++- .../config/conversion/NativeConverter.java | 5 +- .../config/conversion/ObjectConverter.java | 13 +-- .../conversion/StaticRootConverter.java | 9 +- .../config/conversion/StringConverter.java | 5 +- .../config/conversion/TypeConverter.java | 8 +- .../data/ConfigurationSectionDataHolder.java | 66 +++++++++++++ .../redlib/config/data/DataHolder.java | 89 ++++++++++++++++++ .../redlib/config/data/ListDataHolder.java | 92 +++++++++++++++++++ .../redlib/config/data/MapDataHolder.java | 75 +++++++++++++++ src/redempt/redlib/sql/SQLCache.java | 3 +- 14 files changed, 378 insertions(+), 38 deletions(-) create mode 100644 src/redempt/redlib/config/data/ConfigurationSectionDataHolder.java create mode 100644 src/redempt/redlib/config/data/DataHolder.java create mode 100644 src/redempt/redlib/config/data/ListDataHolder.java create mode 100644 src/redempt/redlib/config/data/MapDataHolder.java diff --git a/res/plugin.yml b/res/plugin.yml index b20f83d..ad861c2 100755 --- a/res/plugin.yml +++ b/res/plugin.yml @@ -1,6 +1,6 @@ name: RedLib main: redempt.redlib.RedLib -version: 2021-12-20 06:09 +version: 2021-12-21 04:50 author: Redempt api-version: 1.13 load: STARTUP diff --git a/src/redempt/redlib/config/ConfigManager.java b/src/redempt/redlib/config/ConfigManager.java index 3352153..b6e8d4c 100644 --- a/src/redempt/redlib/config/ConfigManager.java +++ b/src/redempt/redlib/config/ConfigManager.java @@ -12,6 +12,8 @@ import redempt.redlib.config.conversion.StaticRootConverter; import redempt.redlib.config.conversion.StringConverter; import redempt.redlib.config.conversion.TypeConverter; +import redempt.redlib.config.data.ConfigurationSectionDataHolder; +import redempt.redlib.config.data.DataHolder; import java.io.File; import java.io.IOException; @@ -77,6 +79,7 @@ public static ConfigManager create(Plugin plugin) { } private FileConfiguration config; + private DataHolder holder; private File file; private TypeConverter converter; private Object target; @@ -86,12 +89,17 @@ private ConfigManager(File file) { this.file = file; file.getParentFile().mkdirs(); if (file.exists()) { - config = YamlConfiguration.loadConfiguration(file); + setConfig(YamlConfiguration.loadConfiguration(file)); } else { - config = new YamlConfiguration(); + setConfig(new YamlConfiguration()); } } + private void setConfig(FileConfiguration config) { + this.config = config; + this.holder = new ConfigurationSectionDataHolder(config); + } + /** * Specifies the given Object to load config values to and save config values from. The Object * must be from a ConfigMappable class, and all of its non-transient fields will be worked with. @@ -150,7 +158,7 @@ public ConfigManager load() { } private void load(TypeConverter converter) { - converter.loadFrom(config, null, (T) target); + converter.loadFrom(holder, null, (T) target); } /** @@ -158,12 +166,12 @@ private void load(TypeConverter converter) { * @return This ConfigManager */ public ConfigManager reload() { - config = YamlConfiguration.loadConfiguration(file); + setConfig(YamlConfiguration.loadConfiguration(file)); return load(); } private void save(TypeConverter converter, boolean overwrite) { - converter.saveTo((T) target, config, null, overwrite); + converter.saveTo((T) target, holder, null, overwrite); try { config.save(file); } catch (IOException e) { diff --git a/src/redempt/redlib/config/conversion/CollectionConverter.java b/src/redempt/redlib/config/conversion/CollectionConverter.java index 51e93ce..6de0d9e 100644 --- a/src/redempt/redlib/config/conversion/CollectionConverter.java +++ b/src/redempt/redlib/config/conversion/CollectionConverter.java @@ -1,8 +1,9 @@ package redempt.redlib.config.conversion; -import org.bukkit.configuration.ConfigurationSection; import redempt.redlib.config.ConfigManager; import redempt.redlib.config.ConfigType; +import redempt.redlib.config.data.DataHolder; +import redempt.redlib.config.data.ListDataHolder; import java.util.ArrayDeque; import java.util.ArrayList; @@ -39,34 +40,37 @@ public class CollectionConverter { * @return A collection converter */ public static > TypeConverter create(ConfigManager manager, ConfigType collectionType) { - TypeConverter converter = (TypeConverter) manager.getConverter(collectionType.getComponentTypes().get(0)); + ConfigType componentType = collectionType.getComponentTypes().get(0); + TypeConverter converter = (TypeConverter) manager.getConverter(componentType); return new TypeConverter() { @Override - public T loadFrom(ConfigurationSection section, String path, T currentValue) { + public T loadFrom(DataHolder section, String path, T currentValue) { if (currentValue == null) { currentValue = (T) defaults.get(collectionType.getType()).get(); } else { currentValue.clear(); } - ConfigurationSection newSection = section.getConfigurationSection(path); + DataHolder newSection = path == null ? section : section.getList(path); if (newSection == null) { return null; } T collection = currentValue; - newSection.getKeys(false).forEach(k -> { - collection.add(converter.loadFrom(newSection, k, null)); + newSection.getKeys().forEach(k -> { + V obj = converter.loadFrom(newSection, k, null); + collection.add(obj); }); return collection; } @Override - public void saveTo(T t, ConfigurationSection section, String path) { - ConfigurationSection newSection = section.createSection(path); + public void saveTo(T t, DataHolder section, String path) { + DataHolder newSection = new ListDataHolder(); int pos = 0; for (V obj : t) { converter.saveTo(obj, newSection, String.valueOf(pos)); pos++; } + section.set(path, newSection); } }; } diff --git a/src/redempt/redlib/config/conversion/MapConverter.java b/src/redempt/redlib/config/conversion/MapConverter.java index 740753c..4bea74e 100644 --- a/src/redempt/redlib/config/conversion/MapConverter.java +++ b/src/redempt/redlib/config/conversion/MapConverter.java @@ -3,6 +3,7 @@ import org.bukkit.configuration.ConfigurationSection; import redempt.redlib.config.ConfigManager; import redempt.redlib.config.ConfigType; +import redempt.redlib.config.data.DataHolder; import java.util.LinkedHashMap; import java.util.List; @@ -29,18 +30,18 @@ public static > TypeConverter create(ConfigManager TypeConverter valueConverter = (TypeConverter) manager.getConverter(types.get(1)); return new TypeConverter() { @Override - public M loadFrom(ConfigurationSection section, String path, M currentValue) { + public M loadFrom(DataHolder section, String path, M currentValue) { if (currentValue == null) { currentValue = (M) new LinkedHashMap<>(); } else { currentValue.clear(); } M map = currentValue; - ConfigurationSection newSection = section.getConfigurationSection(path); + DataHolder newSection = path == null ? section : section.getSubsection(path); if (newSection == null) { return null; } - newSection.getKeys(false).forEach(k -> { + newSection.getKeys().forEach(k -> { K key = keyConverter.fromString(k); V value = valueConverter.loadFrom(newSection, k, null); map.put(key, value); @@ -49,8 +50,8 @@ public M loadFrom(ConfigurationSection section, String path, M currentValue) { } @Override - public void saveTo(M m, ConfigurationSection section, String path) { - ConfigurationSection newSection = section.createSection(path); + public void saveTo(M m, DataHolder section, String path) { + DataHolder newSection = path == null ? section : section.createSubsection(path); m.forEach((k, v) -> { String keyPath = keyConverter.toString(k); valueConverter.saveTo(v, newSection, keyPath); diff --git a/src/redempt/redlib/config/conversion/NativeConverter.java b/src/redempt/redlib/config/conversion/NativeConverter.java index d637263..534c2d8 100644 --- a/src/redempt/redlib/config/conversion/NativeConverter.java +++ b/src/redempt/redlib/config/conversion/NativeConverter.java @@ -1,6 +1,7 @@ package redempt.redlib.config.conversion; import org.bukkit.configuration.ConfigurationSection; +import redempt.redlib.config.data.DataHolder; /** * A converter which saves and loads directly to config without modifying the data @@ -16,12 +17,12 @@ public class NativeConverter { public static TypeConverter create() { return new TypeConverter() { @Override - public T loadFrom(ConfigurationSection section, String path, T currentValue) { + public T loadFrom(DataHolder section, String path, T currentValue) { return (T) section.get(path); } @Override - public void saveTo(T t, ConfigurationSection section, String path) { + public void saveTo(T t, DataHolder section, String path) { section.set(path, t); } }; diff --git a/src/redempt/redlib/config/conversion/ObjectConverter.java b/src/redempt/redlib/config/conversion/ObjectConverter.java index f48dfcc..f0c16d8 100644 --- a/src/redempt/redlib/config/conversion/ObjectConverter.java +++ b/src/redempt/redlib/config/conversion/ObjectConverter.java @@ -5,6 +5,7 @@ import redempt.redlib.config.ConfigType; import redempt.redlib.config.annotations.ConfigPath; import redempt.redlib.config.annotations.ConfigPostInit; +import redempt.redlib.config.data.DataHolder; import redempt.redlib.config.instantiation.InstantiationInfo; import redempt.redlib.config.instantiation.Instantiator; @@ -71,8 +72,8 @@ public static TypeConverter create(ConfigManager manager, ConfigType t InstantiationInfo info = new InstantiationInfo(postInit, configPath, configPathConverter); return new TypeConverter() { @Override - public T loadFrom(ConfigurationSection section, String path, T currentValue) { - ConfigurationSection newSection = path == null ? section : section.getConfigurationSection(path); + public T loadFrom(DataHolder section, String path, T currentValue) { + DataHolder newSection = path == null ? section : section.getSubsection(path); List objs = new ArrayList<>(); for (Field field : fields) { Object value = converters.get(field).loadFrom(newSection, field.getName(), null); @@ -82,16 +83,16 @@ public T loadFrom(ConfigurationSection section, String path, T currentValue) { } @Override - public void saveTo(T t, ConfigurationSection section, String path) { + public void saveTo(T t, DataHolder section, String path) { saveTo(t, section, path, true); } @Override - public void saveTo(T t, ConfigurationSection section, String path, boolean overwrite) { + public void saveTo(T t, DataHolder section, String path, boolean overwrite) { if (path != null && section.isSet(path) && !overwrite) { return; } - ConfigurationSection newSection = path == null ? section : section.createSection(path); + DataHolder newSection = path == null ? section : section.createSubsection(path); try { for (Field field : fields) { saveWith(converters.get(field), field.get(t), newSection, field.getName(), overwrite); @@ -103,7 +104,7 @@ public void saveTo(T t, ConfigurationSection section, String path, boolean overw }; } - private static void saveWith(TypeConverter converter, Object obj, ConfigurationSection section, String path, boolean overwrite) { + private static void saveWith(TypeConverter converter, Object obj, DataHolder section, String path, boolean overwrite) { converter.saveTo((T) obj, section, path, overwrite); } diff --git a/src/redempt/redlib/config/conversion/StaticRootConverter.java b/src/redempt/redlib/config/conversion/StaticRootConverter.java index 8d8ed56..1633128 100644 --- a/src/redempt/redlib/config/conversion/StaticRootConverter.java +++ b/src/redempt/redlib/config/conversion/StaticRootConverter.java @@ -3,6 +3,7 @@ import org.bukkit.configuration.ConfigurationSection; import redempt.redlib.config.ConfigManager; import redempt.redlib.config.ConfigType; +import redempt.redlib.config.data.DataHolder; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -38,7 +39,7 @@ public static TypeConverter create(ConfigManager manager, Class root) } return new TypeConverter() { @Override - public T loadFrom(ConfigurationSection section, String path, T currentValue) { + public T loadFrom(DataHolder section, String path, T currentValue) { try { for (Field field : fields) { Object val = converters.get(field).loadFrom(section, field.getName(), null); @@ -51,12 +52,12 @@ public T loadFrom(ConfigurationSection section, String path, T currentValue) { } @Override - public void saveTo(T t, ConfigurationSection section, String path) { + public void saveTo(T t, DataHolder section, String path) { saveTo(t, section, path, true); } @Override - public void saveTo(T t, ConfigurationSection section, String path, boolean overwrite) { + public void saveTo(T t, DataHolder section, String path, boolean overwrite) { try { for (Field field : fields) { Object obj = field.get(null); @@ -69,7 +70,7 @@ public void saveTo(T t, ConfigurationSection section, String path, boolean overw }; } - private static void saveWith(TypeConverter converter, Object obj, ConfigurationSection section, String path, boolean overwrite) { + private static void saveWith(TypeConverter converter, Object obj, DataHolder section, String path, boolean overwrite) { converter.saveTo((T) obj, section, path, overwrite); } diff --git a/src/redempt/redlib/config/conversion/StringConverter.java b/src/redempt/redlib/config/conversion/StringConverter.java index 6183428..7f79dd1 100644 --- a/src/redempt/redlib/config/conversion/StringConverter.java +++ b/src/redempt/redlib/config/conversion/StringConverter.java @@ -1,6 +1,7 @@ package redempt.redlib.config.conversion; import org.bukkit.configuration.ConfigurationSection; +import redempt.redlib.config.data.DataHolder; import java.util.function.Function; @@ -53,7 +54,7 @@ public String toString(T t) { * @return */ @Override - public default T loadFrom(ConfigurationSection section, String path, T currentValue) { + public default T loadFrom(DataHolder section, String path, T currentValue) { return fromString(section.getString(path)); } @@ -63,7 +64,7 @@ public default T loadFrom(ConfigurationSection section, String path, T currentVa * @param path The path to the data that should be saved in the ConfigurationSection */ @Override - public default void saveTo(T t, ConfigurationSection section, String path) { + public default void saveTo(T t, DataHolder section, String path) { section.set(path, toString(t)); } diff --git a/src/redempt/redlib/config/conversion/TypeConverter.java b/src/redempt/redlib/config/conversion/TypeConverter.java index d793fd5..52ce10c 100644 --- a/src/redempt/redlib/config/conversion/TypeConverter.java +++ b/src/redempt/redlib/config/conversion/TypeConverter.java @@ -1,6 +1,6 @@ package redempt.redlib.config.conversion; -import org.bukkit.configuration.ConfigurationSection; +import redempt.redlib.config.data.DataHolder; /** * An interface which converts data in config for a given type @@ -16,7 +16,7 @@ public interface TypeConverter { * @param currentValue The current value, used for collections and maps * @return The loaded object */ - public T loadFrom(ConfigurationSection section, String path, T currentValue); + public T loadFrom(DataHolder section, String path, T currentValue); /** * Attemps to save the object to config @@ -24,7 +24,7 @@ public interface TypeConverter { * @param section The ConfigurationSection to save to * @param path The path to the data that should be saved in the ConfigurationSection */ - public void saveTo(T t, ConfigurationSection section, String path); + public void saveTo(T t, DataHolder section, String path); /** * Attemps to save the object to config @@ -33,7 +33,7 @@ public interface TypeConverter { * @param path The path to the data that should be saved in the ConfigurationSection * @param overwrite Whether to overwrite existing data */ - public default void saveTo(T t, ConfigurationSection section, String path, boolean overwrite) { + public default void saveTo(T t, DataHolder section, String path, boolean overwrite) { if (!overwrite && section.isSet(path)) { return; } diff --git a/src/redempt/redlib/config/data/ConfigurationSectionDataHolder.java b/src/redempt/redlib/config/data/ConfigurationSectionDataHolder.java new file mode 100644 index 0000000..ad1b7b3 --- /dev/null +++ b/src/redempt/redlib/config/data/ConfigurationSectionDataHolder.java @@ -0,0 +1,66 @@ +package redempt.redlib.config.data; + +import org.bukkit.configuration.ConfigurationSection; + +import java.util.Set; + +public class ConfigurationSectionDataHolder implements DataHolder { + + private ConfigurationSection section; + + public ConfigurationSectionDataHolder(ConfigurationSection section) { + this.section = section; + } + + @Override + public Object get(String path) { + return section.get(path); + } + + @Override + public void set(String path, Object obj) { + section.set(path, DataHolder.unwrap(obj)); + } + + @Override + public DataHolder getSubsection(String path) { + ConfigurationSection subsection = section.getConfigurationSection(path); + return subsection == null ? null : new ConfigurationSectionDataHolder(subsection); + } + + @Override + public DataHolder createSubsection(String path) { + return new ConfigurationSectionDataHolder(section.createSection(path)); + } + + @Override + public Set getKeys() { + return section.getKeys(false); + } + + @Override + public boolean isSet(String path) { + return section.isSet(path); + } + + @Override + public String getString(String path) { + return section.getString(path); + } + + @Override + public DataHolder getList(String path) { + return new ListDataHolder(section.getList(path)); + } + + @Override + public void remove(String path) { + section.set(path, null); + } + + @Override + public Object unwrap() { + return section; + } + +} diff --git a/src/redempt/redlib/config/data/DataHolder.java b/src/redempt/redlib/config/data/DataHolder.java new file mode 100644 index 0000000..c5653ee --- /dev/null +++ b/src/redempt/redlib/config/data/DataHolder.java @@ -0,0 +1,89 @@ +package redempt.redlib.config.data; + +import java.util.Set; + +/** + * An arbitrary data structure which can map keys to values + * @author Redempt + */ +public interface DataHolder { + + /** + * Unwraps the object a DataHolder wraps, if it is one + * @param obj The object + * @return The unwrapped object, or the original object if it was not a DataHolder + */ + public static Object unwrap(Object obj) { + if (obj instanceof DataHolder) { + return ((DataHolder) obj).unwrap(); + } + return obj; + } + + /** + * Gets the object mapped to the given path + * @param path The path + * @return The object mapped to the path + */ + public Object get(String path); + + /** + * Sets the object at a given path + * @param path The path to the object + * @param obj The object to set + */ + public void set(String path, Object obj); + + /** + * Gets an existing subsection of this DataHolder + * @param path The path to the data + * @return The subsection, or null + */ + public DataHolder getSubsection(String path); + + /** + * Creates a subsection of this DataHolder + * @param path The path of the subsection to create + * @return The created subsection + */ + public DataHolder createSubsection(String path); + + /** + * @return All valid keys + */ + public Set getKeys(); + + /** + * Checks whether a given path has a value associated + * @param path The path to check + * @return Whether the path has an associated value + */ + public boolean isSet(String path); + + /** + * Gets a string value from a path + * @param path The path to the string + * @return The string + */ + public String getString(String path); + + /** + * Gets a list subsection + * @param path The path to the subsection + * @return The list subsection, or null + */ + public DataHolder getList(String path); + + /** + * Removes a mapping + * @param path The path of the data to remove + */ + public void remove(String path); + + /** + * Unwraps the object this DataHolder wraps + * @return The wrapped storage + */ + public Object unwrap(); + +} diff --git a/src/redempt/redlib/config/data/ListDataHolder.java b/src/redempt/redlib/config/data/ListDataHolder.java new file mode 100644 index 0000000..fa2bcc2 --- /dev/null +++ b/src/redempt/redlib/config/data/ListDataHolder.java @@ -0,0 +1,92 @@ +package redempt.redlib.config.data; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class ListDataHolder implements DataHolder { + + private List list; + + public ListDataHolder(List list) { + this.list = (List) list; + } + + public ListDataHolder() { + this(new ArrayList<>()); + } + + @Override + public Object get(String path) { + int index = Integer.parseInt(path); + if (index < 0 || index >= list.size()) { + return null; + } + return list.get(index); + } + + @Override + public void set(String path, Object obj) { + int index = Integer.parseInt(path); + obj = DataHolder.unwrap(obj); + if (index >= list.size()) { + list.add(obj); + } else { + list.set(index, obj); + } + } + + @Override + public DataHolder getSubsection(String path) { + Object obj = get(path); + return obj instanceof Map ? new MapDataHolder((Map) obj) : null; + } + + @Override + public DataHolder createSubsection(String path) { + int index = Integer.parseInt(path); + MapDataHolder holder = new MapDataHolder(); + if (index >= list.size()) { + list.add(holder.unwrap()); + } else { + list.set(index, holder.unwrap()); + } + return holder; + } + + @Override + public Set getKeys() { + return IntStream.range(0, list.size()).mapToObj(String::valueOf).collect(Collectors.toSet()); + } + + @Override + public boolean isSet(String path) { + int index = Integer.parseInt(path); + return index > 0 && index < list.size(); + } + + @Override + public String getString(String path) { + return (String) get(path); + } + + @Override + public DataHolder getList(String path) { + Object obj = get(path); + return obj instanceof List ? new ListDataHolder((List) obj) : null; + } + + @Override + public void remove(String path) { + list.remove(Integer.parseInt(path)); + } + + @Override + public Object unwrap() { + return list; + } + +} diff --git a/src/redempt/redlib/config/data/MapDataHolder.java b/src/redempt/redlib/config/data/MapDataHolder.java new file mode 100644 index 0000000..b645db6 --- /dev/null +++ b/src/redempt/redlib/config/data/MapDataHolder.java @@ -0,0 +1,75 @@ +package redempt.redlib.config.data; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class MapDataHolder implements DataHolder { + + private Map map; + + public MapDataHolder(Map map) { + this.map = map; + } + + public MapDataHolder() { + this(new HashMap<>()); + } + + + @Override + public Object get(String path) { + return map.get(path); + } + + @Override + public void set(String path, Object obj) { + map.put(path, DataHolder.unwrap(obj)); + } + + @Override + public DataHolder getSubsection(String path) { + Object obj = map.get(path); + return obj instanceof Map ? new MapDataHolder((Map) obj) : null; + } + + @Override + public DataHolder createSubsection(String path) { + MapDataHolder subsection = new MapDataHolder(); + map.put(path, subsection.unwrap()); + return subsection; + } + + @Override + public Set getKeys() { + return map.keySet(); + } + + @Override + public boolean isSet(String path) { + return map.containsKey(path); + } + + @Override + public String getString(String path) { + return (String) map.get(path); + } + + @Override + public DataHolder getList(String path) { + Object obj = map.get(path); + return obj instanceof List ? new ListDataHolder((List) obj) : null; + } + + @Override + public void remove(String path) { + map.remove(path); + } + + @Override + public Object unwrap() { + return map; + } + +} diff --git a/src/redempt/redlib/sql/SQLCache.java b/src/redempt/redlib/sql/SQLCache.java index 60213d6..320c91d 100644 --- a/src/redempt/redlib/sql/SQLCache.java +++ b/src/redempt/redlib/sql/SQLCache.java @@ -1,6 +1,7 @@ package redempt.redlib.sql; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -18,7 +19,7 @@ public class SQLCache { private String deleteQuery; private String selectQuery; private String updateQuery; - private Map cache = new ConcurrentHashMap<>(); + private Map cache = Collections.synchronizedMap(new HashMap<>()); private Set modified = Collections.synchronizedSet(new HashSet<>()); private SQLHelper sql;