From 8f610c993521d615a8a664abe6a4c8af471d61a3 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Thu, 27 Aug 2020 16:18:25 +0200 Subject: [PATCH] Add ConfigData builder, reset support --- .../lib/architecture/ButtonPlugin.java | 3 +- .../lib/architecture/Component.java | 6 +- .../lib/architecture/ConfigData.java | 125 +++++++++++++++--- .../lib/architecture/IHaveConfig.java | 27 +++- .../lib/architecture/ReadOnlyConfigData.java | 2 +- 5 files changed, 137 insertions(+), 26 deletions(-) diff --git a/Chroma-Core/src/main/java/buttondevteam/lib/architecture/ButtonPlugin.java b/Chroma-Core/src/main/java/buttondevteam/lib/architecture/ButtonPlugin.java index cf7db91..a6d7e2e 100644 --- a/Chroma-Core/src/main/java/buttondevteam/lib/architecture/ButtonPlugin.java +++ b/Chroma-Core/src/main/java/buttondevteam/lib/architecture/ButtonPlugin.java @@ -72,7 +72,8 @@ public abstract class ButtonPlugin extends JavaPlugin { return false; var section = config.getConfigurationSection("global"); if (section == null) section = config.createSection("global"); - iConfig = new IHaveConfig(section, this::saveConfig); + if (iConfig != null) iConfig.reset(section); + else iConfig = new IHaveConfig(section, this::saveConfig); return true; } diff --git a/Chroma-Core/src/main/java/buttondevteam/lib/architecture/Component.java b/Chroma-Core/src/main/java/buttondevteam/lib/architecture/Component.java index fa77d78..886e16b 100644 --- a/Chroma-Core/src/main/java/buttondevteam/lib/architecture/Component.java +++ b/Chroma-Core/src/main/java/buttondevteam/lib/architecture/Component.java @@ -161,9 +161,11 @@ public abstract class Component { var configSect = compconf.getConfigurationSection(component.getClassName()); if (configSect == null) configSect = compconf.createSection(component.getClassName()); - component.config = new IHaveConfig(configSect, plugin::saveConfig); + if (component.config != null) component.config.reset(configSect); + else component.config = new IHaveConfig(configSect, plugin::saveConfig); } else //Testing - component.config = new IHaveConfig(null, plugin::saveConfig); + if (component.config == null) + component.config = new IHaveConfig(null, plugin::saveConfig); } /** diff --git a/Chroma-Core/src/main/java/buttondevteam/lib/architecture/ConfigData.java b/Chroma-Core/src/main/java/buttondevteam/lib/architecture/ConfigData.java index 47adc92..c7202ad 100644 --- a/Chroma-Core/src/main/java/buttondevteam/lib/architecture/ConfigData.java +++ b/Chroma-Core/src/main/java/buttondevteam/lib/architecture/ConfigData.java @@ -2,10 +2,7 @@ package buttondevteam.lib.architecture; import buttondevteam.core.MainPlugin; import buttondevteam.lib.ChromaUtils; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.Setter; +import lombok.*; import org.bukkit.Bukkit; import org.bukkit.configuration.Configuration; import org.bukkit.configuration.ConfigurationSection; @@ -22,13 +19,12 @@ import java.util.function.Function; * Use the getter/setter constructor if {@link T} isn't a primitive type or String.
* Use {@link Component#getConfig()} or {@link ButtonPlugin#getIConfig()} then {@link IHaveConfig#getData(String, Object)} to get an instance. */ -//@AllArgsConstructor(access = AccessLevel.PACKAGE) public class ConfigData { private static final HashMap saveTasks = new HashMap<>(); /** * May be null for testing */ - private final ConfigurationSection config; + private ConfigurationSection config; @Getter @Setter(AccessLevel.PACKAGE) private String path; @@ -38,19 +34,31 @@ public class ConfigData { /** * The parameter is of a primitive type as returned by {@link YamlConfiguration#get(String)} */ - private Function getter; + private final Function getter; /** * The result should be a primitive type or string that can be retrieved correctly later */ - private Function setter; + private final Function setter; /** * The config value should not change outside this instance */ private T value; - //This constructor is needed because it sets the getter and setter ConfigData(ConfigurationSection config, String path, T def, Object primitiveDef, Function getter, Function setter, Runnable saveAction) { + if (def == null) { + if (primitiveDef == null) + throw new IllegalArgumentException("Either def or primitiveDef must be set."); + if (getter == null) + throw new IllegalArgumentException("A getter and setter must be present when using primitiveDef."); + def = getter.apply(primitiveDef); + } else if (primitiveDef == null) + if (setter == null) + primitiveDef = def; + else + primitiveDef = setter.apply(def); + if ((getter == null) != (setter == null)) + throw new IllegalArgumentException("Both setters and getters must be present (or none if def is primitive)."); this.config = config; this.path = path; this.def = def; @@ -60,20 +68,16 @@ public class ConfigData { this.saveAction = saveAction; } - @java.beans.ConstructorProperties({"config", "path", "def", "primitiveDef", "saveAction"}) - ConfigData(ConfigurationSection config, String path, T def, Object primitiveDef, Runnable saveAction) { - this.config = config; - this.path = path; - this.def = def; - this.primitiveDef = primitiveDef; - this.saveAction = saveAction; - } - @Override public String toString() { return "ConfigData{" + "path='" + path + '\'' + ", value=" + value + '}'; } + void reset(ConfigurationSection config) { + value = null; + this.config = config; + } + @SuppressWarnings("unchecked") public T get() { if (value != null) return value; //Speed things up @@ -157,4 +161,89 @@ public class ConfigData { } return false; } + + public static ConfigData.ConfigDataBuilder builder(ConfigurationSection config, String path, Runnable saveAction) { + return new ConfigDataBuilder(config, path, saveAction); + } + + @RequiredArgsConstructor(access = AccessLevel.PACKAGE) + public static class ConfigDataBuilder { + private final ConfigurationSection config; + private final String path; + private T def; + private Object primitiveDef; + private Function getter; + private Function setter; + private final Runnable saveAction; + + /** + * The default value to use, as used in code. If not a primitive type, use the {@link #getter(Function)} and {@link #setter(Function)} methods. + *
+ * To set the value as it is stored, use {@link #primitiveDef(Object)}. + * + * @param def The default value + * @return This builder + */ + public ConfigDataBuilder def(T def) { + this.def = def; + return this; + } + + /** + * The default value to use, as stored in yaml. Must be a primitive type. Make sure to use the {@link #getter(Function)} and {@link #setter(Function)} methods. + *
+ * To set the value as used in the code, use {@link #def(Object)}. + * + * @param primitiveDef The default value + * @return This builder + */ + public ConfigDataBuilder primitiveDef(Object primitiveDef) { + this.primitiveDef = primitiveDef; + return this; + } + + /** + * A function to use to obtain the runtime object from the yaml representation (usually string). + * The {@link #setter(Function)} must also be set. + * + * @param getter A function that receives the primitive type and returns the runtime type + * @return This builder + */ + public ConfigDataBuilder getter(Function getter) { + this.getter = getter; + return this; + } + + /** + * A function to use to obtain the yaml representation (usually string) from the runtime object. + * The {@link #getter(Function)} must also be set. + * + * @param setter A function that receives the runtime type and returns the primitive type + * @return This builder + */ + public ConfigDataBuilder setter(Function setter) { + this.setter = setter; + return this; + } + + /** + * Builds a modifiable config representation. Use if you want to change the value in code. + * + * @return A ConfigData instance. + */ + public ConfigData build() { + return new ConfigData<>(config, path, def, primitiveDef, getter, setter, saveAction); + } + + /** + * Builds a read-only config representation. Use if you only want the value to be changed in the config. + * + * @return A ReadOnlyConfigData instance. + */ + public ReadOnlyConfigData buildReadOnly() { + return new ReadOnlyConfigData<>(config, path, def, primitiveDef, getter, setter, saveAction); + } + + public String toString() {return "ConfigData.ConfigDataBuilder(config=" + this.config + ", path=" + this.path + ", def=" + this.def + ", primitiveDef=" + this.primitiveDef + ", getter=" + this.getter + ", setter=" + this.setter + ", saveAction=" + this.saveAction + ")";} + } } diff --git a/Chroma-Core/src/main/java/buttondevteam/lib/architecture/IHaveConfig.java b/Chroma-Core/src/main/java/buttondevteam/lib/architecture/IHaveConfig.java index 5d9e1fa..54f3091 100644 --- a/Chroma-Core/src/main/java/buttondevteam/lib/architecture/IHaveConfig.java +++ b/Chroma-Core/src/main/java/buttondevteam/lib/architecture/IHaveConfig.java @@ -22,7 +22,7 @@ public final class IHaveConfig { * Returns the Bukkit ConfigurationSection. Use {@link #signalChange()} after changing it. */ @Getter - private final ConfigurationSection config; + private ConfigurationSection config; private final Runnable saveAction; /** @@ -36,7 +36,18 @@ public final class IHaveConfig { } /** - * This method overload should only be used with primitves or String. + * Gets a config object for the given path. The def or primitiveDef must be set. If a getter is present, a setter must be present as well. + * + * @param path The dot-separated path relative to this config instance + * @param The runtime type of the config value + * @return A ConfigData builder to set how to obtain the value + */ + public ConfigData.ConfigDataBuilder getConfig(String path) { + return ConfigData.builder(config, path, saveAction); + } + + /** + * This method overload should only be used with primitives or String. * * @param path The path in config to use * @param def The value to use by default @@ -46,7 +57,7 @@ public final class IHaveConfig { @SuppressWarnings("unchecked") public ConfigData getData(String path, T def) { ConfigData data = datamap.get(path); - if (data == null) datamap.put(path, data = new ConfigData<>(config, path, def, def, saveAction)); + if (data == null) datamap.put(path, data = new ConfigData<>(config, path, def, def, null, null, saveAction)); return (ConfigData) data; } @@ -117,7 +128,7 @@ public final class IHaveConfig { ConfigData data = datamap.get(path); if (data == null) { val defval = def.get(); - datamap.put(path, data = new ConfigData<>(config, path, defval, defval, saveAction)); + datamap.put(path, data = new ConfigData<>(config, path, defval, defval, null, null, saveAction)); } return (ConfigData) data; } @@ -164,6 +175,14 @@ public final class IHaveConfig { ConfigData.signalChange(config, saveAction); } + /** + * Clears all caches and loads everything from yaml. + */ + public void reset(ConfigurationSection config) { + this.config = config; + datamap.forEach((path, data) -> data.reset(config)); + } + /** * Generates the config YAML. * diff --git a/Chroma-Core/src/main/java/buttondevteam/lib/architecture/ReadOnlyConfigData.java b/Chroma-Core/src/main/java/buttondevteam/lib/architecture/ReadOnlyConfigData.java index 5594c21..1cc1435 100644 --- a/Chroma-Core/src/main/java/buttondevteam/lib/architecture/ReadOnlyConfigData.java +++ b/Chroma-Core/src/main/java/buttondevteam/lib/architecture/ReadOnlyConfigData.java @@ -10,6 +10,6 @@ public class ReadOnlyConfigData extends ConfigData { } ReadOnlyConfigData(ConfigurationSection config, String path, T def, Object primitiveDef, Runnable saveAction) { - super(config, path, def, primitiveDef, saveAction); + super(config, path, def, primitiveDef, null, null, saveAction); } }