Add ConfigData builder, reset support
This commit is contained in:
parent
4310e45a6f
commit
8f610c9935
5 changed files with 137 additions and 26 deletions
|
@ -72,7 +72,8 @@ public abstract class ButtonPlugin extends JavaPlugin {
|
||||||
return false;
|
return false;
|
||||||
var section = config.getConfigurationSection("global");
|
var section = config.getConfigurationSection("global");
|
||||||
if (section == null) section = config.createSection("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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -161,8 +161,10 @@ public abstract class Component<TP extends JavaPlugin> {
|
||||||
var configSect = compconf.getConfigurationSection(component.getClassName());
|
var configSect = compconf.getConfigurationSection(component.getClassName());
|
||||||
if (configSect == null)
|
if (configSect == null)
|
||||||
configSect = compconf.createSection(component.getClassName());
|
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
|
} else //Testing
|
||||||
|
if (component.config == null)
|
||||||
component.config = new IHaveConfig(null, plugin::saveConfig);
|
component.config = new IHaveConfig(null, plugin::saveConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,7 @@ package buttondevteam.lib.architecture;
|
||||||
|
|
||||||
import buttondevteam.core.MainPlugin;
|
import buttondevteam.core.MainPlugin;
|
||||||
import buttondevteam.lib.ChromaUtils;
|
import buttondevteam.lib.ChromaUtils;
|
||||||
import lombok.AccessLevel;
|
import lombok.*;
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.configuration.Configuration;
|
import org.bukkit.configuration.Configuration;
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
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.<br>
|
* Use the getter/setter constructor if {@link T} isn't a primitive type or String.<br>
|
||||||
* Use {@link Component#getConfig()} or {@link ButtonPlugin#getIConfig()} then {@link IHaveConfig#getData(String, Object)} to get an instance.
|
* 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<T> {
|
public class ConfigData<T> {
|
||||||
private static final HashMap<Configuration, SaveTask> saveTasks = new HashMap<>();
|
private static final HashMap<Configuration, SaveTask> saveTasks = new HashMap<>();
|
||||||
/**
|
/**
|
||||||
* May be null for testing
|
* May be null for testing
|
||||||
*/
|
*/
|
||||||
private final ConfigurationSection config;
|
private ConfigurationSection config;
|
||||||
@Getter
|
@Getter
|
||||||
@Setter(AccessLevel.PACKAGE)
|
@Setter(AccessLevel.PACKAGE)
|
||||||
private String path;
|
private String path;
|
||||||
|
@ -38,19 +34,31 @@ public class ConfigData<T> {
|
||||||
/**
|
/**
|
||||||
* The parameter is of a primitive type as returned by {@link YamlConfiguration#get(String)}
|
* The parameter is of a primitive type as returned by {@link YamlConfiguration#get(String)}
|
||||||
*/
|
*/
|
||||||
private Function<Object, T> getter;
|
private final Function<Object, T> getter;
|
||||||
/**
|
/**
|
||||||
* The result should be a primitive type or string that can be retrieved correctly later
|
* The result should be a primitive type or string that can be retrieved correctly later
|
||||||
*/
|
*/
|
||||||
private Function<T, Object> setter;
|
private final Function<T, Object> setter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The config value should not change outside this instance
|
* The config value should not change outside this instance
|
||||||
*/
|
*/
|
||||||
private T value;
|
private T value;
|
||||||
|
|
||||||
//This constructor is needed because it sets the getter and setter
|
|
||||||
ConfigData(ConfigurationSection config, String path, T def, Object primitiveDef, Function<Object, T> getter, Function<T, Object> setter, Runnable saveAction) {
|
ConfigData(ConfigurationSection config, String path, T def, Object primitiveDef, Function<Object, T> getter, Function<T, Object> 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.config = config;
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.def = def;
|
this.def = def;
|
||||||
|
@ -60,20 +68,16 @@ public class ConfigData<T> {
|
||||||
this.saveAction = saveAction;
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ConfigData{" + "path='" + path + '\'' + ", value=" + value + '}';
|
return "ConfigData{" + "path='" + path + '\'' + ", value=" + value + '}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reset(ConfigurationSection config) {
|
||||||
|
value = null;
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public T get() {
|
public T get() {
|
||||||
if (value != null) return value; //Speed things up
|
if (value != null) return value; //Speed things up
|
||||||
|
@ -157,4 +161,89 @@ public class ConfigData<T> {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> ConfigData.ConfigDataBuilder<T> builder(ConfigurationSection config, String path, Runnable saveAction) {
|
||||||
|
return new ConfigDataBuilder<T>(config, path, saveAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
||||||
|
public static class ConfigDataBuilder<T> {
|
||||||
|
private final ConfigurationSection config;
|
||||||
|
private final String path;
|
||||||
|
private T def;
|
||||||
|
private Object primitiveDef;
|
||||||
|
private Function<Object, T> getter;
|
||||||
|
private Function<T, Object> 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.
|
||||||
|
* <br/>
|
||||||
|
* To set the value as it is stored, use {@link #primitiveDef(Object)}.
|
||||||
|
*
|
||||||
|
* @param def The default value
|
||||||
|
* @return This builder
|
||||||
|
*/
|
||||||
|
public ConfigDataBuilder<T> 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.
|
||||||
|
* <br/>
|
||||||
|
* To set the value as used in the code, use {@link #def(Object)}.
|
||||||
|
*
|
||||||
|
* @param primitiveDef The default value
|
||||||
|
* @return This builder
|
||||||
|
*/
|
||||||
|
public ConfigDataBuilder<T> 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<T> getter(Function<Object, T> 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<T> setter(Function<T, Object> setter) {
|
||||||
|
this.setter = setter;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a modifiable config representation. Use if you want to change the value <i>in code</i>.
|
||||||
|
*
|
||||||
|
* @return A ConfigData instance.
|
||||||
|
*/
|
||||||
|
public ConfigData<T> 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 <i>in the config</i>.
|
||||||
|
*
|
||||||
|
* @return A ReadOnlyConfigData instance.
|
||||||
|
*/
|
||||||
|
public ReadOnlyConfigData<T> 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 + ")";}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ public final class IHaveConfig {
|
||||||
* Returns the Bukkit ConfigurationSection. Use {@link #signalChange()} after changing it.
|
* Returns the Bukkit ConfigurationSection. Use {@link #signalChange()} after changing it.
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
private final ConfigurationSection config;
|
private ConfigurationSection config;
|
||||||
private final Runnable saveAction;
|
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 <T> The runtime type of the config value
|
||||||
|
* @return A ConfigData builder to set how to obtain the value
|
||||||
|
*/
|
||||||
|
public <T> ConfigData.ConfigDataBuilder<T> 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 path The path in config to use
|
||||||
* @param def The value to use by default
|
* @param def The value to use by default
|
||||||
|
@ -46,7 +57,7 @@ public final class IHaveConfig {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T> ConfigData<T> getData(String path, T def) {
|
public <T> ConfigData<T> getData(String path, T def) {
|
||||||
ConfigData<?> data = datamap.get(path);
|
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<T>) data;
|
return (ConfigData<T>) data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +128,7 @@ public final class IHaveConfig {
|
||||||
ConfigData<?> data = datamap.get(path);
|
ConfigData<?> data = datamap.get(path);
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
val defval = def.get();
|
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<T>) data;
|
return (ConfigData<T>) data;
|
||||||
}
|
}
|
||||||
|
@ -164,6 +175,14 @@ public final class IHaveConfig {
|
||||||
ConfigData.signalChange(config, saveAction);
|
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.
|
* Generates the config YAML.
|
||||||
*
|
*
|
||||||
|
|
|
@ -10,6 +10,6 @@ public class ReadOnlyConfigData<T> extends ConfigData<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlyConfigData(ConfigurationSection config, String path, T def, Object primitiveDef, Runnable saveAction) {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue