Lots of config improvements

Removed redundant delegate methods by making the methods non-static
Automatically saving config option to the yaml model when it's accessed for the first time
Automatically saving yaml config on component/plugin disable
Automatically resetting config cache on plugin/component disable
Caching the config values so reading is cheaper (it still writes through)
Correctly handling (non-)primitive types in configs
Allowing to set either a primitive or non-primitive default value and the other is obtained from the provided value
#48
This commit is contained in:
Norbi Peti 2019-01-10 01:34:38 +01:00
parent bb31f4e378
commit 245f6bbf59
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
5 changed files with 103 additions and 60 deletions

View file

@ -1,5 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="EntryPointsManager">
<list size="1">
<item index="0" class="java.lang.String" itemvalue="org.bukkit.event.EventHandler" />
</list>
</component>
<component name="MavenProjectsManager"> <component name="MavenProjectsManager">
<option name="originalFiles"> <option name="originalFiles">
<list> <list>

View file

@ -1,16 +1,14 @@
package buttondevteam.lib.architecture; package buttondevteam.lib.architecture;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI;
import org.bukkit.configuration.ConfigurationSection; import lombok.AccessLevel;
import lombok.Getter;
import lombok.experimental.var;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
public abstract class ButtonPlugin extends JavaPlugin { public abstract class ButtonPlugin extends JavaPlugin {
private final HashMap<String, ConfigData<?>> datamap = new HashMap<>(); @Getter(AccessLevel.PROTECTED)
private ConfigurationSection section; private IHaveConfig iConfig;
protected abstract void pluginEnable(); protected abstract void pluginEnable();
@ -18,8 +16,9 @@ public abstract class ButtonPlugin extends JavaPlugin {
@Override @Override
public void onEnable() { public void onEnable() {
section = getConfig().getConfigurationSection("global"); var section = super.getConfig().getConfigurationSection("global");
if (section == null) section = getConfig().createSection("global"); if (section == null) section = super.getConfig().createSection("global");
iConfig = new IHaveConfig(section);
try { try {
pluginEnable(); pluginEnable();
} catch (Exception e) { } catch (Exception e) {
@ -31,22 +30,10 @@ public abstract class ButtonPlugin extends JavaPlugin {
public void onDisable() { public void onDisable() {
try { try {
pluginDisable(); pluginDisable();
saveConfig();
iConfig.resetConfigurationCache();
} catch (Exception e) { } catch (Exception e) {
TBMCCoreAPI.SendException("Error while disabling plugin " + getName() + "!", e); TBMCCoreAPI.SendException("Error while disabling plugin " + getName() + "!", e);
} }
} }
/**
* @see IHaveConfig#getData(Map, ConfigurationSection, String, Object)
*/
protected <T> ConfigData<T> getData(String path, T def) {
return IHaveConfig.getData(datamap, section, path, def);
}
/**
* @see IHaveConfig#getData(Map, ConfigurationSection, String, Object, Function, Function)
*/
protected <T> ConfigData<T> getData(String path, T def, Function<Object, T> getter, Function<T, Object> setter) {
return IHaveConfig.getData(datamap, section, path, def, getter, setter);
}
} }

View file

@ -17,7 +17,6 @@ import org.bukkit.plugin.java.JavaPlugin;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.function.Function;
/** /**
* Configuration is based on class name * Configuration is based on class name
@ -31,26 +30,13 @@ public abstract class Component {
@NonNull @NonNull
private JavaPlugin plugin; private JavaPlugin plugin;
@NonNull @NonNull
private ConfigurationSection config; private ConfigurationSection configSect;
@NonNull
private @Getter
IHaveConfig config;
public ConfigData<Boolean> shouldBeEnabled() { public ConfigData<Boolean> shouldBeEnabled() {
return getData("enabled", true); return config.getData("enabled", true);
}
private HashMap<String, ConfigData<?>> datamap = new HashMap<>();
/**
* @see IHaveConfig#getData(Map, ConfigurationSection, String, Object)
*/
protected <T> ConfigData<T> getData(String path, T def) {
return IHaveConfig.getData(datamap, config, path, def);
}
/**
* @see IHaveConfig#getData(Map, ConfigurationSection, String, Object, Function, Function)
*/
protected <T> ConfigData<T> getData(String path, T def, Function<Object, T> getter, Function<T, Object> setter) {
return IHaveConfig.getData(datamap, config, path, def, getter, setter);
} }
/** /**
@ -95,8 +81,10 @@ public abstract class Component {
component.plugin = plugin; component.plugin = plugin;
var compconf = plugin.getConfig().getConfigurationSection("components"); var compconf = plugin.getConfig().getConfigurationSection("components");
if (compconf == null) compconf = plugin.getConfig().createSection("components"); if (compconf == null) compconf = plugin.getConfig().createSection("components");
component.config = compconf.getConfigurationSection(component.getClassName()); component.configSect = compconf.getConfigurationSection(component.getClassName());
if (component.config == null) component.config = compconf.createSection(component.getClassName()); if (component.configSect == null)
component.configSect = compconf.createSection(component.getClassName());
component.config = new IHaveConfig(component.configSect);
component.register(plugin); component.register(plugin);
components.put(component.getClass(), component); components.put(component.getClass(), component);
if (ComponentManager.areComponentsEnabled() && component.shouldBeEnabled().get()) { if (ComponentManager.areComponentsEnabled() && component.shouldBeEnabled().get()) {
@ -108,12 +96,11 @@ public abstract class Component {
return true; return true;
} }
} }
return true; return true; //Component shouldn't be enabled
} else { } else {
if (component.enabled) { if (component.enabled) {
try { try {
component.disable(); setComponentEnabled(component, false);
component.enabled = false;
} catch (Exception | NoClassDefFoundError e) { } catch (Exception | NoClassDefFoundError e) {
TBMCCoreAPI.SendException("Failed to disable component " + component.getClassName() + "!", e); TBMCCoreAPI.SendException("Failed to disable component " + component.getClassName() + "!", e);
return false; //If failed to disable, won't unregister either return false; //If failed to disable, won't unregister either
@ -140,8 +127,11 @@ public abstract class Component {
throw new UnregisteredComponentException(component); throw new UnregisteredComponentException(component);
if (component.enabled = enabled) if (component.enabled = enabled)
component.enable(); component.enable();
else else {
component.disable(); component.disable();
component.plugin.saveConfig();
component.config.resetConfigurationCache();
}
} }
/** /**
@ -159,6 +149,7 @@ public abstract class Component {
* *
* @param plugin Plugin object * @param plugin Plugin object
*/ */
@SuppressWarnings({"unused", "WeakerAccess"})
protected void register(JavaPlugin plugin) { protected void register(JavaPlugin plugin) {
} }
@ -169,6 +160,7 @@ public abstract class Component {
* *
* @param plugin Plugin object * @param plugin Plugin object
*/ */
@SuppressWarnings({"WeakerAccess", "unused"})
protected void unregister(JavaPlugin plugin) { protected void unregister(JavaPlugin plugin) {
} }

View file

@ -1,23 +1,24 @@
package buttondevteam.lib.architecture; package buttondevteam.lib.architecture;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import javax.annotation.Nullable;
import java.util.function.Function; 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#getData(String, Object)} or {@link ButtonPlugin#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.
*/ */
@RequiredArgsConstructor(access = AccessLevel.PACKAGE) @RequiredArgsConstructor(access = AccessLevel.PACKAGE)
@AllArgsConstructor(access = AccessLevel.PACKAGE) //@AllArgsConstructor(access = AccessLevel.PACKAGE)
public class ConfigData<T> { //TODO: Save after a while public class ConfigData<T> { //TODO: Save after a while
private final ConfigurationSection config; private final ConfigurationSection config;
private final String path; private final String path;
private final T def; private @Nullable final T def;
private final Object primitiveDef;
/** /**
* 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)}
*/ */
@ -27,9 +28,35 @@ public class ConfigData<T> { //TODO: Save after a while
*/ */
private Function<T, Object> setter; private Function<T, Object> setter;
/**
* The config value should not change outside this instance
*/
private T value;
private boolean saved = false;
public ConfigData(ConfigurationSection config, String path, T def, Object primitiveDef, Function<Object, T> getter, Function<T, Object> setter) {
this.config = config;
this.path = path;
this.def = def;
this.primitiveDef = primitiveDef;
this.getter = getter;
this.setter = setter;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public T get() { public T get() {
Object val = config.get(path, def); if (value != null) return value; //Speed things up
Object val = config.get(path);
if (val == null) {
val = primitiveDef;
}
if (val == primitiveDef && !saved) {
if (def != null)
set(def); //Save default value
else
config.set(path, primitiveDef);
saved = true;
}
if (getter != null) { if (getter != null) {
T hmm = getter.apply(val); T hmm = getter.apply(val);
if (hmm == null) hmm = def; //Set if the getter returned null if (hmm == null) hmm = def; //Set if the getter returned null
@ -44,5 +71,6 @@ public class ConfigData<T> { //TODO: Save after a while
val = setter.apply(value); val = setter.apply(value);
else val = value; else val = value;
config.set(path, val); config.set(path, val);
this.value =value;
} }
} }

View file

@ -2,14 +2,19 @@ package buttondevteam.lib.architecture;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import java.util.Map; import java.util.HashMap;
import java.util.function.Function; import java.util.function.Function;
/** /**
* Members of this interface should be protected (access level) * Members of this interface should be protected (access level)
*/ */
final class IHaveConfig { public final class IHaveConfig {
private IHaveConfig() {} private final HashMap<String, ConfigData<?>> datamap = new HashMap<>();
private ConfigurationSection config;
IHaveConfig(ConfigurationSection section) {
config = section;
}
/** /**
* This method overload should only be used with primitves or String. * This method overload should only be used with primitves or String.
@ -20,9 +25,9 @@ final class IHaveConfig {
* @return The data object that can be used to get or set the value * @return The data object that can be used to get or set the value
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected static <T> ConfigData<T> getData(Map<String, ConfigData<?>> datamap, ConfigurationSection config, 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)); if (data == null) datamap.put(path, data = new ConfigData<>(config, path, def, def));
return (ConfigData<T>) data; return (ConfigData<T>) data;
} }
@ -37,9 +42,35 @@ final class IHaveConfig {
* @return The data object that can be used to get or set the value * @return The data object that can be used to get or set the value
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected static <T> ConfigData<T> getData(Map<String, ConfigData<?>> datamap, ConfigurationSection config, String path, T def, Function<Object, T> getter, Function<T, Object> setter) { public <T> ConfigData<T> getData(String path, T def, Function<Object, T> getter, Function<T, Object> setter) {
ConfigData<?> data = datamap.get(path); ConfigData<?> data = datamap.get(path);
if (data == null) datamap.put(path, data = new ConfigData<>(config, path, def, getter, setter)); if (data == null)
datamap.put(path, data = new ConfigData<>(config, path, def, setter.apply(def), getter, setter));
return (ConfigData<T>) data; return (ConfigData<T>) data;
} }
/**
* This method overload may be used with any class. The given default value will be run through the getter.
*
* @param path The path in config to use
* @param primitiveDef The <b>primitive</b> value to use by default
* @param getter A function that converts a primitive representation to the correct value
* @param setter A function that converts a value to a primitive representation
* @param <T> The type of this variable (can be any class)
* @return The data object that can be used to get or set the value
*/
@SuppressWarnings("unchecked")
public <T> ConfigData<T> getDataPrimDef(String path, Object primitiveDef, Function<Object, T> getter, Function<T, Object> setter) {
ConfigData<?> data = datamap.get(path);
if (data == null)
datamap.put(path, data = new ConfigData<>(config, path, getter.apply(primitiveDef), primitiveDef, getter, setter));
return (ConfigData<T>) data;
}
/**
* Deletes the config cache. Call this on component/plugin disable so that new values are read.
*/
public void resetConfigurationCache() {
datamap.clear();
}
} }