Convert config stuff and fix issues
This commit is contained in:
parent
65be3b2df9
commit
482df40992
5 changed files with 576 additions and 518 deletions
|
@ -34,6 +34,7 @@
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>compile</id>
|
<id>compile</id>
|
||||||
|
<phase>process-sources</phase>
|
||||||
<goals>
|
<goals>
|
||||||
<goal>compile</goal>
|
<goal>compile</goal>
|
||||||
</goals>
|
</goals>
|
||||||
|
|
|
@ -6,8 +6,6 @@ import buttondevteam.lib.TBMCCoreAPI
|
||||||
import buttondevteam.lib.architecture.Component.Companion.updateConfig
|
import buttondevteam.lib.architecture.Component.Companion.updateConfig
|
||||||
import buttondevteam.lib.chat.Command2MC
|
import buttondevteam.lib.chat.Command2MC
|
||||||
import buttondevteam.lib.chat.ICommand2MC
|
import buttondevteam.lib.chat.ICommand2MC
|
||||||
import lombok.AccessLevel
|
|
||||||
import lombok.Getter
|
|
||||||
import org.bukkit.configuration.InvalidConfigurationException
|
import org.bukkit.configuration.InvalidConfigurationException
|
||||||
import org.bukkit.configuration.file.FileConfiguration
|
import org.bukkit.configuration.file.FileConfiguration
|
||||||
import org.bukkit.configuration.file.YamlConfiguration
|
import org.bukkit.configuration.file.YamlConfiguration
|
||||||
|
@ -16,22 +14,19 @@ import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
import java.util.function.Function
|
|
||||||
|
|
||||||
@HasConfig(global = true)
|
@HasConfig(global = true)
|
||||||
abstract class ButtonPlugin : JavaPlugin() {
|
abstract class ButtonPlugin : JavaPlugin() {
|
||||||
protected val iConfig = IHaveConfig { saveConfig() }
|
protected val iConfig = IHaveConfig { saveConfig() }
|
||||||
private var yaml: CommentedConfiguration? = null
|
private var yaml: CommentedConfiguration? = null
|
||||||
|
|
||||||
@Getter(AccessLevel.PROTECTED)
|
protected val data //TODO
|
||||||
private val data //TODO
|
|
||||||
: IHaveConfig? = null
|
: IHaveConfig? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to unregister components in the right order - and to reload configs
|
* Used to unregister components in the right order - and to reload configs
|
||||||
*/
|
*/
|
||||||
@Getter
|
val componentStack = Stack<Component<*>>()
|
||||||
private val componentStack = Stack<Component<*>>()
|
|
||||||
protected abstract fun pluginEnable()
|
protected abstract fun pluginEnable()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -111,9 +106,12 @@ abstract class ButtonPlugin : JavaPlugin() {
|
||||||
this.yaml = yaml
|
this.yaml = yaml
|
||||||
val res = getTextResource("configHelp.yml") ?: return true
|
val res = getTextResource("configHelp.yml") ?: return true
|
||||||
val yc = YamlConfiguration.loadConfiguration(res)
|
val yc = YamlConfiguration.loadConfiguration(res)
|
||||||
for ((key, value) in yc.getValues(true)) if (value is String) yaml.addComment(key.replace(".generalDescriptionInsteadOfAConfig", ""),
|
for ((key, value) in yc.getValues(true)) if (value is String) yaml.addComment(key.replace(
|
||||||
*Arrays.stream<String>(value.split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray())
|
".generalDescriptionInsteadOfAConfig",
|
||||||
.map<String> { str: String -> "# " + str.trim { it <= ' ' } }.toArray<String> { _Dummy_.__Array__() })
|
""
|
||||||
|
),
|
||||||
|
*value.split("\n").map { str -> "# " + str.trim { it <= ' ' } }.toTypedArray()
|
||||||
|
)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,7 +146,7 @@ abstract class ButtonPlugin : JavaPlugin() {
|
||||||
val command2MC = Command2MC()
|
val command2MC = Command2MC()
|
||||||
fun configGenAllowed(obj: Any): Boolean {
|
fun configGenAllowed(obj: Any): Boolean {
|
||||||
return !Optional.ofNullable(obj.javaClass.getAnnotation(ConfigOpts::class.java))
|
return !Optional.ofNullable(obj.javaClass.getAnnotation(ConfigOpts::class.java))
|
||||||
.map(Function<ConfigOpts, Boolean> { obj: ConfigOpts -> obj.disableConfigGen() }).orElse(false)
|
.map { it.disableConfigGen }.orElse(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,38 +11,34 @@ import org.bukkit.event.Listener
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
import java.util.function.Function
|
|
||||||
import java.util.stream.Collectors
|
import java.util.stream.Collectors
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration is based on class name
|
* Configuration is based on class name
|
||||||
*/
|
*/
|
||||||
@HasConfig(global = false) //Used for obtaining javadoc
|
@HasConfig(global = false) //Used for obtaining javadoc
|
||||||
|
|
||||||
abstract class Component<TP : JavaPlugin?> {
|
abstract class Component<TP : JavaPlugin?> {
|
||||||
@Getter
|
var isEnabled = false
|
||||||
private var enabled = false
|
|
||||||
|
|
||||||
@Getter
|
var plugin: TP? = null
|
||||||
private var plugin: TP = null
|
|
||||||
|
|
||||||
@Getter
|
val config = IHaveConfig(null)
|
||||||
private val config = IHaveConfig(null)
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private val data //TODO
|
private val data //TODO
|
||||||
: IHaveConfig? = null
|
: IHaveConfig? = null
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
val shouldBeEnabled = config.getData("enabled",
|
val shouldBeEnabled: ConfigData<Boolean> = config.getData("enabled",
|
||||||
Optional.ofNullable(javaClass.getAnnotation(ComponentMetadata::class.java)).map(Function<ComponentMetadata, Boolean> { obj: ComponentMetadata -> obj.enabledByDefault() }).orElse(true))
|
Optional.ofNullable(javaClass.getAnnotation(ComponentMetadata::class.java)).map { it.enabledByDefault }
|
||||||
|
.orElse(true))
|
||||||
|
|
||||||
fun log(message: String) {
|
fun log(message: String) {
|
||||||
plugin!!.logger.info("[" + className + "] " + message)
|
plugin!!.logger.info("[$className] $message")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun logWarn(message: String) {
|
fun logWarn(message: String) {
|
||||||
plugin!!.logger.warning("[" + className + "] " + message)
|
plugin!!.logger.warning("[$className] $message")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -85,7 +81,7 @@ abstract class Component<TP : JavaPlugin?> {
|
||||||
fun registerCommand(command: ICommand2MC) {
|
fun registerCommand(command: ICommand2MC) {
|
||||||
if (plugin is ButtonPlugin) command.registerToPlugin(plugin as ButtonPlugin)
|
if (plugin is ButtonPlugin) command.registerToPlugin(plugin as ButtonPlugin)
|
||||||
command.registerToComponent(this)
|
command.registerToComponent(this)
|
||||||
ButtonPlugin.getCommand2MC().registerCommand(command)
|
ButtonPlugin.command2MC.registerCommand(command)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -107,18 +103,23 @@ abstract class Component<TP : JavaPlugin?> {
|
||||||
* @return A map containing configs
|
* @return A map containing configs
|
||||||
*/
|
*/
|
||||||
fun getConfigMap(key: String?, defaultProvider: Map<String, Consumer<IHaveConfig?>>): Map<String, IHaveConfig> {
|
fun getConfigMap(key: String?, defaultProvider: Map<String, Consumer<IHaveConfig?>>): Map<String, IHaveConfig> {
|
||||||
val c: ConfigurationSection = getConfig().getConfig()
|
val c: ConfigurationSection = config.config
|
||||||
var cs = c.getConfigurationSection(key)
|
var cs = c.getConfigurationSection(key)
|
||||||
if (cs == null) cs = c.createSection(key)
|
if (cs == null) cs = c.createSection(key)
|
||||||
val res = cs!!.getValues(false).entries.stream().filter { (_, value): Map.Entry<String?, Any?> -> value is ConfigurationSection }
|
val res = cs!!.getValues(false).entries.stream()
|
||||||
.collect(Collectors.toMap<Map.Entry<String?, Any?>, String, IHaveConfig>(Function<Map.Entry<String?, Any?>, String> { (key1, value) -> java.util.Map.Entry.key }, Function<Map.Entry<String?, Any?>, IHaveConfig> { (_, value): Map.Entry<String?, Any?> ->
|
.filter { (_, value): Map.Entry<String?, Any?> -> value is ConfigurationSection }
|
||||||
val conf = IHaveConfig { getPlugin().saveConfig() }
|
.collect(
|
||||||
conf.reset(value as ConfigurationSection?)
|
Collectors.toMap<Map.Entry<String?, Any?>, String, IHaveConfig>(
|
||||||
conf
|
{ it.key },
|
||||||
}))
|
{ (_, value): Map.Entry<String?, Any?> ->
|
||||||
if (res.size == 0) {
|
val conf = IHaveConfig { plugin!!.saveConfig() }
|
||||||
|
conf.reset(value as ConfigurationSection?)
|
||||||
|
conf
|
||||||
|
})
|
||||||
|
)
|
||||||
|
if (res.isEmpty()) {
|
||||||
for ((key1, value) in defaultProvider) {
|
for ((key1, value) in defaultProvider) {
|
||||||
val conf = IHaveConfig { getPlugin().saveConfig() }
|
val conf = IHaveConfig { plugin!!.saveConfig() }
|
||||||
conf.reset(cs.createSection(key1))
|
conf.reset(cs.createSection(key1))
|
||||||
value.accept(conf)
|
value.accept(conf)
|
||||||
res[key1] = conf
|
res[key1] = conf
|
||||||
|
@ -128,7 +129,7 @@ abstract class Component<TP : JavaPlugin?> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private val className: String
|
private val className: String
|
||||||
private get() = javaClass.simpleName
|
get() = javaClass.simpleName
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val components = HashMap<Class<out Component<*>>, Component<out JavaPlugin>>()
|
private val components = HashMap<Class<out Component<*>>, Component<out JavaPlugin>>()
|
||||||
|
@ -143,7 +144,7 @@ abstract class Component<TP : JavaPlugin?> {
|
||||||
* @return Whether the component is registered successfully (it may have failed to enable)
|
* @return Whether the component is registered successfully (it may have failed to enable)
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun <T : JavaPlugin?> registerComponent(plugin: T, component: Component<T>): Boolean {
|
fun <T : JavaPlugin> registerComponent(plugin: T, component: Component<T>): Boolean {
|
||||||
return registerUnregisterComponent(plugin, component, true)
|
return registerUnregisterComponent(plugin, component, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,29 +157,37 @@ abstract class Component<TP : JavaPlugin?> {
|
||||||
* @return Whether the component is unregistered successfully (it also got disabled)
|
* @return Whether the component is unregistered successfully (it also got disabled)
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun <T : JavaPlugin?> unregisterComponent(plugin: T, component: Component<T>): Boolean {
|
fun <T : JavaPlugin> unregisterComponent(plugin: T, component: Component<T>): Boolean {
|
||||||
return registerUnregisterComponent(plugin, component, false)
|
return registerUnregisterComponent(plugin, component, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : JavaPlugin?> registerUnregisterComponent(plugin: T, component: Component<T>, register: Boolean): Boolean {
|
fun <T : JavaPlugin> registerUnregisterComponent(
|
||||||
|
plugin: T,
|
||||||
|
component: Component<T>,
|
||||||
|
register: Boolean
|
||||||
|
): Boolean {
|
||||||
return try {
|
return try {
|
||||||
val metaAnn = component.javaClass.getAnnotation(ComponentMetadata::class.java)
|
val metaAnn = component.javaClass.getAnnotation(ComponentMetadata::class.java)
|
||||||
if (metaAnn != null) {
|
if (metaAnn != null) {
|
||||||
val dependencies: Array<Class<out Component<*>>> = metaAnn.depends()
|
val dependencies = metaAnn.depends
|
||||||
for (dep in dependencies) { //TODO: Support dependencies at enable/disable as well
|
for (dep in dependencies) { //TODO: Support dependencies at enable/disable as well
|
||||||
if (!components.containsKey(dep)) {
|
if (!components.containsKey(dep.java)) {
|
||||||
plugin!!.logger.warning("Failed to " + (if (register) "" else "un") + "register component " + component.className + " as a required dependency is missing/disabled: " + dep.simpleName)
|
plugin.logger.warning("Failed to " + (if (register) "" else "un") + "register component " + component.className + " as a required dependency is missing/disabled: " + dep.simpleName)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (register) {
|
if (register) {
|
||||||
if (components.containsKey(component.javaClass)) {
|
if (components.containsKey(component.javaClass)) {
|
||||||
TBMCCoreAPI.SendException("Failed to register component " + component.className, IllegalArgumentException("The component is already registered!"), plugin)
|
TBMCCoreAPI.SendException(
|
||||||
|
"Failed to register component " + component.className,
|
||||||
|
IllegalArgumentException("The component is already registered!"),
|
||||||
|
plugin
|
||||||
|
)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
component.plugin = plugin
|
component.plugin = plugin // TODO: Perhaps construct a new object with these initialized
|
||||||
component.config.saveAction = Runnable { plugin!!.saveConfig() }
|
component.config.saveAction = Runnable { plugin.saveConfig() }
|
||||||
updateConfig(plugin, component)
|
updateConfig(plugin, component)
|
||||||
component.register(plugin)
|
component.register(plugin)
|
||||||
components[component.javaClass] = component
|
components[component.javaClass] = component
|
||||||
|
@ -188,7 +197,11 @@ abstract class Component<TP : JavaPlugin?> {
|
||||||
setComponentEnabled(component, true)
|
setComponentEnabled(component, true)
|
||||||
true
|
true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
TBMCCoreAPI.SendException("Failed to enable component " + component.className + "!", e, component)
|
TBMCCoreAPI.SendException(
|
||||||
|
"Failed to enable component " + component.className + "!",
|
||||||
|
e,
|
||||||
|
component
|
||||||
|
)
|
||||||
true
|
true
|
||||||
} catch (e: NoClassDefFoundError) {
|
} catch (e: NoClassDefFoundError) {
|
||||||
TBMCCoreAPI.SendException("Failed to enable component " + component.className + "!", e, component)
|
TBMCCoreAPI.SendException("Failed to enable component " + component.className + "!", e, component)
|
||||||
|
@ -197,14 +210,22 @@ abstract class Component<TP : JavaPlugin?> {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!components.containsKey(component.javaClass)) return true //Already unregistered
|
if (!components.containsKey(component.javaClass)) return true //Already unregistered
|
||||||
if (component.enabled) {
|
if (component.isEnabled) {
|
||||||
try {
|
try {
|
||||||
setComponentEnabled(component, false)
|
setComponentEnabled(component, false)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
TBMCCoreAPI.SendException("Failed to disable component " + component.className + "!", e, component)
|
TBMCCoreAPI.SendException(
|
||||||
|
"Failed to disable component " + component.className + "!",
|
||||||
|
e,
|
||||||
|
component
|
||||||
|
)
|
||||||
return false //If failed to disable, won't unregister either
|
return false //If failed to disable, won't unregister either
|
||||||
} catch (e: NoClassDefFoundError) {
|
} catch (e: NoClassDefFoundError) {
|
||||||
TBMCCoreAPI.SendException("Failed to disable component " + component.className + "!", e, component)
|
TBMCCoreAPI.SendException(
|
||||||
|
"Failed to disable component " + component.className + "!",
|
||||||
|
e,
|
||||||
|
component
|
||||||
|
)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -228,10 +249,10 @@ abstract class Component<TP : JavaPlugin?> {
|
||||||
@Throws(UnregisteredComponentException::class)
|
@Throws(UnregisteredComponentException::class)
|
||||||
fun setComponentEnabled(component: Component<*>, enabled: Boolean) {
|
fun setComponentEnabled(component: Component<*>, enabled: Boolean) {
|
||||||
if (!components.containsKey(component.javaClass)) throw UnregisteredComponentException(component)
|
if (!components.containsKey(component.javaClass)) throw UnregisteredComponentException(component)
|
||||||
if (component.enabled == enabled) return //Don't do anything
|
if (component.isEnabled == enabled) return //Don't do anything
|
||||||
if (enabled.also { component.enabled = it }) {
|
if (enabled.also { component.isEnabled = it }) {
|
||||||
try {
|
try {
|
||||||
updateConfig(component.getPlugin(), component)
|
updateConfig(component.plugin!!, component)
|
||||||
component.enable()
|
component.enable()
|
||||||
if (ButtonPlugin.configGenAllowed(component)) {
|
if (ButtonPlugin.configGenAllowed(component)) {
|
||||||
IHaveConfig.pregenConfig(component, null)
|
IHaveConfig.pregenConfig(component, null)
|
||||||
|
@ -253,7 +274,7 @@ abstract class Component<TP : JavaPlugin?> {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
component.disable()
|
component.disable()
|
||||||
ButtonPlugin.getCommand2MC().unregisterCommands(component)
|
ButtonPlugin.command2MC.unregisterCommands(component)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,252 +1,260 @@
|
||||||
package buttondevteam.lib.architecture;
|
package buttondevteam.lib.architecture
|
||||||
|
|
||||||
import buttondevteam.core.MainPlugin;
|
import buttondevteam.core.MainPlugin
|
||||||
import buttondevteam.lib.ChromaUtils;
|
import buttondevteam.lib.ChromaUtils
|
||||||
import lombok.*;
|
import buttondevteam.lib.architecture.IHaveConfig.getConfig
|
||||||
import org.bukkit.Bukkit;
|
import lombok.*
|
||||||
import org.bukkit.configuration.Configuration;
|
import org.bukkit.Bukkit
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.Configuration
|
||||||
import org.bukkit.scheduler.BukkitTask;
|
import org.bukkit.scheduler.BukkitTask
|
||||||
|
import java.util.function.BiFunction
|
||||||
import java.lang.reflect.Array;
|
import java.util.function.Function
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.BiFunction;
|
|
||||||
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 [T] isn't a primitive type or String.<br></br>
|
||||||
* Use {@link Component#getConfig()} or {@link ButtonPlugin#getIConfig()} then {@link IHaveConfig#getData(String, Object)} to get an instance.
|
* Use [Component.getConfig] or [ButtonPlugin.getIConfig] then [IHaveConfig.getData] to get an instance.
|
||||||
*/
|
*/
|
||||||
public class ConfigData<T> {
|
open class ConfigData<T> internal constructor(
|
||||||
private static final HashMap<Configuration, SaveTask> saveTasks = new HashMap<>();
|
config: IHaveConfig?,
|
||||||
/**
|
path: String?,
|
||||||
* May be null for testing
|
def: T,
|
||||||
*/
|
primitiveDef: Any?,
|
||||||
private IHaveConfig config;
|
getter: Function<Any?, T>?,
|
||||||
@Getter
|
setter: Function<T, Any?>?
|
||||||
@Setter(AccessLevel.PACKAGE)
|
) {
|
||||||
private String path;
|
/**
|
||||||
protected final T def;
|
* May be null for testing
|
||||||
private final Object primitiveDef;
|
*/
|
||||||
/**
|
private val config: IHaveConfig?
|
||||||
* The parameter is of a primitive type as returned by {@link YamlConfiguration#get(String)}
|
|
||||||
*/
|
|
||||||
private final Function<Object, T> getter;
|
|
||||||
/**
|
|
||||||
* The result should be a primitive type or string that can be retrieved correctly later
|
|
||||||
*/
|
|
||||||
private final Function<T, Object> setter;
|
|
||||||
|
|
||||||
/**
|
@Getter
|
||||||
* The config value should not change outside this instance
|
@Setter(AccessLevel.PACKAGE)
|
||||||
*/
|
private val path: String?
|
||||||
private T value;
|
protected val def: T?
|
||||||
|
private val primitiveDef: Any?
|
||||||
|
|
||||||
ConfigData(IHaveConfig config, String path, T def, Object primitiveDef, Function<Object, T> getter, Function<T, Object> setter) {
|
/**
|
||||||
if (def == null) {
|
* The parameter is of a primitive type as returned by [YamlConfiguration.get]
|
||||||
if (primitiveDef == null)
|
*/
|
||||||
throw new IllegalArgumentException("Either def or primitiveDef must be set.");
|
private val getter: Function<Any?, T>?
|
||||||
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;
|
|
||||||
this.primitiveDef = primitiveDef;
|
|
||||||
this.getter = getter;
|
|
||||||
this.setter = setter;
|
|
||||||
get(); //Generate config automatically
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public String toString() {
|
* The result should be a primitive type or string that can be retrieved correctly later
|
||||||
return "ConfigData{" + "path='" + path + '\'' + ", value=" + value + '}';
|
*/
|
||||||
}
|
private val setter: Function<T, Any?>?
|
||||||
|
|
||||||
void reset() {
|
/**
|
||||||
value = null;
|
* The config value should not change outside this instance
|
||||||
}
|
*/
|
||||||
|
private var value: T? = null
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
init {
|
||||||
public T get() {
|
var def: T? = def
|
||||||
if (value != null) return value; //Speed things up
|
var primitiveDef = primitiveDef
|
||||||
var config = this.config.getConfig();
|
if (def == null) {
|
||||||
Object val;
|
requireNotNull(primitiveDef) { "Either def or primitiveDef must be set." }
|
||||||
if (config == null || !config.isSet(path)) { //Call set() if config == null
|
requireNotNull(getter) { "A getter and setter must be present when using primitiveDef." }
|
||||||
val = primitiveDef;
|
def = getter.apply(primitiveDef)
|
||||||
if ((def == null || this instanceof ReadOnlyConfigData) && config != null) //In Discord's case def may be null
|
} else if (primitiveDef == null) primitiveDef = if (setter == null) def else setter.apply(def)
|
||||||
setInternal(primitiveDef); //If read-only then we still need to save the default value so it can be set
|
require(getter == null == (setter == null)) { "Both setters and getters must be present (or none if def is primitive)." }
|
||||||
else
|
this.config = config
|
||||||
set(def); //Save default value - def is always set
|
this.path = path
|
||||||
} else
|
this.def = def
|
||||||
val = config.get(path); //config==null: testing
|
this.primitiveDef = primitiveDef
|
||||||
if (val == null) //If it's set to null explicitly
|
this.getter = getter
|
||||||
val = primitiveDef;
|
this.setter = setter
|
||||||
BiFunction<Object, Object, Object> convert = (_val, _def) -> {
|
get() //Generate config automatically
|
||||||
if (_def instanceof Number) //If we expect a number
|
}
|
||||||
if (_val instanceof Number)
|
|
||||||
_val = ChromaUtils.convertNumber((Number) _val,
|
|
||||||
(Class<? extends Number>) _def.getClass());
|
|
||||||
else
|
|
||||||
_val = _def; //If we didn't get a number, return default (which is a number)
|
|
||||||
else if (_val instanceof List && _def != null && _def.getClass().isArray())
|
|
||||||
_val = ((List<T>) _val).toArray((T[]) Array.newInstance(_def.getClass().getComponentType(), 0));
|
|
||||||
return _val;
|
|
||||||
};
|
|
||||||
if (getter != null) {
|
|
||||||
val = convert.apply(val, primitiveDef);
|
|
||||||
T hmm = getter.apply(val);
|
|
||||||
if (hmm == null) hmm = def; //Set if the getter returned null
|
|
||||||
return hmm;
|
|
||||||
}
|
|
||||||
val = convert.apply(val, def);
|
|
||||||
return value = (T) val; //Always cache, if not cached yet
|
|
||||||
}
|
|
||||||
|
|
||||||
public void set(T value) {
|
override fun toString(): String {
|
||||||
if (this instanceof ReadOnlyConfigData)
|
return "ConfigData{path='$path', value=$value}"
|
||||||
return; //Safety for Discord channel/role data
|
}
|
||||||
Object val;
|
|
||||||
if (setter != null && value != null)
|
|
||||||
val = setter.apply(value);
|
|
||||||
else val = value;
|
|
||||||
if (config.getConfig() != null)
|
|
||||||
setInternal(val);
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setInternal(Object val) {
|
fun reset() {
|
||||||
config.getConfig().set(path, val);
|
value = null
|
||||||
signalChange(config);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static void signalChange(IHaveConfig config) {
|
fun get(): T? {
|
||||||
var cc = config.getConfig();
|
if (value != null) return value //Speed things up
|
||||||
var sa = config.getSaveAction();
|
val config = config!!.getConfig<Any>()
|
||||||
if (!saveTasks.containsKey(cc.getRoot())) {
|
var `val`: Any?
|
||||||
synchronized (saveTasks) {
|
if (config == null || !config.isSet(path)) { //Call set() if config == null
|
||||||
saveTasks.put(cc.getRoot(), new SaveTask(Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.Instance, () -> {
|
`val` = primitiveDef
|
||||||
synchronized (saveTasks) {
|
if ((def == null || this is ReadOnlyConfigData<*>) && config != null) //In Discord's case def may be null
|
||||||
saveTasks.remove(cc.getRoot());
|
setInternal(primitiveDef) //If read-only then we still need to save the default value so it can be set
|
||||||
sa.run();
|
else set(def) //Save default value - def is always set
|
||||||
}
|
} else `val` = config.get(path) //config==null: testing
|
||||||
}, 100), sa));
|
if (`val` == null) //If it's set to null explicitly
|
||||||
}
|
`val` = primitiveDef
|
||||||
}
|
val convert = BiFunction { _val: Any?, _def: Any? ->
|
||||||
}
|
if (_def is Number) //If we expect a number
|
||||||
|
_val = if (_val is Number) ChromaUtils.convertNumber(
|
||||||
|
_val as Number?,
|
||||||
|
_def.javaClass as Class<out Number?>
|
||||||
|
) else _def //If we didn't get a number, return default (which is a number)
|
||||||
|
else if (_val is List<*> && _def != null && _def.javaClass.isArray) _val = (_val as List<T>).toArray<T>(
|
||||||
|
java.lang.reflect.Array.newInstance(
|
||||||
|
_def.javaClass.componentType,
|
||||||
|
0
|
||||||
|
) as Array<T>
|
||||||
|
)
|
||||||
|
_val
|
||||||
|
}
|
||||||
|
if (getter != null) {
|
||||||
|
`val` = convert.apply(`val`, primitiveDef)
|
||||||
|
var hmm: T? = getter.apply(`val`)
|
||||||
|
if (hmm == null) hmm = def //Set if the getter returned null
|
||||||
|
return hmm
|
||||||
|
}
|
||||||
|
`val` = convert.apply(`val`, def)
|
||||||
|
return `val` as T?. also {
|
||||||
|
value = it //Always cache, if not cached yet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@AllArgsConstructor
|
fun set(value: T?) {
|
||||||
private static class SaveTask {
|
if (this is ReadOnlyConfigData<*>) return //Safety for Discord channel/role data
|
||||||
BukkitTask task;
|
val `val`: Any?
|
||||||
Runnable saveAction;
|
`val` = if (setter != null && value != null) setter.apply(value) else value
|
||||||
}
|
if (config!!.getConfig<Any>() != null) setInternal(`val`)
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean saveNow(Configuration config) {
|
private fun setInternal(`val`: Any?) {
|
||||||
synchronized (saveTasks) {
|
config!!.getConfig<Any>().set(path, `val`)
|
||||||
SaveTask st = saveTasks.get(config);
|
signalChange(config)
|
||||||
if (st != null) {
|
}
|
||||||
st.task.cancel();
|
|
||||||
saveTasks.remove(config);
|
|
||||||
st.saveAction.run();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> ConfigData.ConfigDataBuilder<T> builder(IHaveConfig config, String path) {
|
@AllArgsConstructor
|
||||||
return new ConfigDataBuilder<T>(config, path);
|
private class SaveTask {
|
||||||
}
|
var task: BukkitTask? = null
|
||||||
|
var saveAction: Runnable? = null
|
||||||
|
}
|
||||||
|
|
||||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
||||||
public static class ConfigDataBuilder<T> {
|
class ConfigDataBuilder<T> {
|
||||||
private final IHaveConfig config;
|
private val config: IHaveConfig? = null
|
||||||
private final String path;
|
private val path: String? = null
|
||||||
private T def;
|
private var def: T? = null
|
||||||
private Object primitiveDef;
|
private var primitiveDef: Any? = null
|
||||||
private Function<Object, T> getter;
|
private var getter: Function<Any?, T?>? = null
|
||||||
private Function<T, Object> setter;
|
private var setter: Function<T?, Any?>? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default value to use, as used in code. If not a primitive type, use the {@link #getter(Function)} and {@link #setter(Function)} methods.
|
* The default value to use, as used in code. If not a primitive type, use the [.getter] and [.setter] methods.
|
||||||
* <br/>
|
* <br></br>
|
||||||
* To set the value as it is stored, use {@link #primitiveDef(Object)}.
|
* To set the value as it is stored, use [.primitiveDef].
|
||||||
*
|
*
|
||||||
* @param def The default value
|
* @param def The default value
|
||||||
* @return This builder
|
* @return This builder
|
||||||
*/
|
*/
|
||||||
public ConfigDataBuilder<T> def(T def) {
|
fun def(def: T): ConfigDataBuilder<T> {
|
||||||
this.def = def;
|
this.def = def
|
||||||
return this;
|
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.
|
* The default value to use, as stored in yaml. Must be a primitive type. Make sure to use the [.getter] and [.setter] methods.
|
||||||
* <br/>
|
* <br></br>
|
||||||
* To set the value as used in the code, use {@link #def(Object)}.
|
* To set the value as used in the code, use [.def].
|
||||||
*
|
*
|
||||||
* @param primitiveDef The default value
|
* @param primitiveDef The default value
|
||||||
* @return This builder
|
* @return This builder
|
||||||
*/
|
*/
|
||||||
public ConfigDataBuilder<T> primitiveDef(Object primitiveDef) {
|
fun primitiveDef(primitiveDef: Any?): ConfigDataBuilder<T> {
|
||||||
this.primitiveDef = primitiveDef;
|
this.primitiveDef = primitiveDef
|
||||||
return this;
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A function to use to obtain the runtime object from the yaml representation (usually string).
|
* A function to use to obtain the runtime object from the yaml representation (usually string).
|
||||||
* The {@link #setter(Function)} must also be set.
|
* The [.setter] must also be set.
|
||||||
*
|
*
|
||||||
* @param getter A function that receives the primitive type and returns the runtime type
|
* @param getter A function that receives the primitive type and returns the runtime type
|
||||||
* @return This builder
|
* @return This builder
|
||||||
*/
|
*/
|
||||||
public ConfigDataBuilder<T> getter(Function<Object, T> getter) {
|
fun getter(getter: Function<Any?, T>?): ConfigDataBuilder<T> {
|
||||||
this.getter = getter;
|
this.getter = getter
|
||||||
return this;
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A function to use to obtain the yaml representation (usually string) from the runtime object.
|
* A function to use to obtain the yaml representation (usually string) from the runtime object.
|
||||||
* The {@link #getter(Function)} must also be set.
|
* The [.getter] must also be set.
|
||||||
*
|
*
|
||||||
* @param setter A function that receives the runtime type and returns the primitive type
|
* @param setter A function that receives the runtime type and returns the primitive type
|
||||||
* @return This builder
|
* @return This builder
|
||||||
*/
|
*/
|
||||||
public ConfigDataBuilder<T> setter(Function<T, Object> setter) {
|
fun setter(setter: Function<T, Any?>?): ConfigDataBuilder<T> {
|
||||||
this.setter = setter;
|
this.setter = setter
|
||||||
return this;
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a modifiable config representation. Use if you want to change the value <i>in code</i>.
|
* Builds a modifiable config representation. Use if you want to change the value *in code*.
|
||||||
*
|
*
|
||||||
* @return A ConfigData instance.
|
* @return A ConfigData instance.
|
||||||
*/
|
*/
|
||||||
public ConfigData<T> build() {
|
fun build(): ConfigData<T?> {
|
||||||
ConfigData<T> config = new ConfigData<>(this.config, path, def, primitiveDef, getter, setter);
|
val config = ConfigData(config, path, def, primitiveDef, getter, setter)
|
||||||
this.config.onConfigBuild(config);
|
this.config!!.onConfigBuild(config)
|
||||||
return config;
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a read-only config representation. Use if you only want the value to be changed <i>in the config</i>.
|
* Builds a read-only config representation. Use if you only want the value to be changed *in the config*.
|
||||||
*
|
*
|
||||||
* @return A ReadOnlyConfigData instance.
|
* @return A ReadOnlyConfigData instance.
|
||||||
*/
|
*/
|
||||||
public ReadOnlyConfigData<T> buildReadOnly() {
|
fun buildReadOnly(): ReadOnlyConfigData<T?> {
|
||||||
ReadOnlyConfigData<T> config = new ReadOnlyConfigData<>(this.config, path, def, primitiveDef, getter, setter);
|
val config = ReadOnlyConfigData(config, path, def, primitiveDef, getter, setter)
|
||||||
this.config.onConfigBuild(config);
|
this.config!!.onConfigBuild(config)
|
||||||
return config;
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {return "ConfigData.ConfigDataBuilder(config=" + this.config + ", path=" + this.path + ", def=" + this.def + ", primitiveDef=" + this.primitiveDef + ", getter=" + this.getter + ", setter=" + this.setter + ")";}
|
override fun toString(): String {
|
||||||
}
|
return "ConfigData.ConfigDataBuilder(config=" + config + ", path=" + path + ", def=" + def + ", primitiveDef=" + primitiveDef + ", getter=" + getter + ", setter=" + setter + ")"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val saveTasks = HashMap<Configuration, SaveTask>()
|
||||||
|
fun signalChange(config: IHaveConfig?) {
|
||||||
|
val cc = config!!.getConfig<Any>()
|
||||||
|
val sa = config.saveAction
|
||||||
|
if (!saveTasks.containsKey(cc.getRoot())) {
|
||||||
|
synchronized(saveTasks) {
|
||||||
|
saveTasks.put(
|
||||||
|
cc.getRoot(),
|
||||||
|
SaveTask(Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.Instance, {
|
||||||
|
synchronized(
|
||||||
|
saveTasks
|
||||||
|
) {
|
||||||
|
saveTasks.remove(cc.getRoot())
|
||||||
|
sa!!.run()
|
||||||
|
}
|
||||||
|
}, 100), sa)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun saveNow(config: Configuration): Boolean {
|
||||||
|
synchronized(saveTasks) {
|
||||||
|
val st = saveTasks[config]
|
||||||
|
if (st != null) {
|
||||||
|
st.task!!.cancel()
|
||||||
|
saveTasks.remove(config)
|
||||||
|
st.saveAction!!.run()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> builder(config: IHaveConfig?, path: String?): ConfigDataBuilder<T> {
|
||||||
|
return ConfigDataBuilder(config, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,256 +1,286 @@
|
||||||
package buttondevteam.lib.architecture;
|
package buttondevteam.lib.architecture
|
||||||
|
|
||||||
import buttondevteam.core.MainPlugin;
|
import buttondevteam.core.MainPlugin
|
||||||
import buttondevteam.lib.TBMCCoreAPI;
|
import buttondevteam.lib.TBMCCoreAPI
|
||||||
import lombok.Getter;
|
import buttondevteam.lib.architecture.ConfigData.ConfigDataBuilder
|
||||||
import lombok.Setter;
|
import lombok.Getter
|
||||||
import lombok.val;
|
import org.bukkit.Bukkit
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.configuration.ConfigurationSection
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import java.lang.reflect.InvocationTargetException
|
||||||
|
import java.util.*
|
||||||
import javax.annotation.Nullable;
|
import java.util.function.Function
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.util.function.Predicate
|
||||||
import java.util.*;
|
import java.util.function.Supplier
|
||||||
import java.util.function.Function;
|
import java.util.stream.Collectors
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A config system
|
* A config system
|
||||||
|
* May be used in testing.
|
||||||
|
*
|
||||||
|
* @param saveAction What to do to save the config to disk. Don't use get methods until it's non-null.
|
||||||
*/
|
*/
|
||||||
public final class IHaveConfig {
|
class IHaveConfig(var saveAction: Runnable?) { // TODO: Make non-nullable after adding component builder
|
||||||
private final HashMap<String, ConfigData<?>> datamap = new HashMap<>();
|
private val datamap = HashMap<String, ConfigData<*>>()
|
||||||
/**
|
|
||||||
* Returns the Bukkit ConfigurationSection. Use {@link #signalChange()} after changing it.
|
|
||||||
*/
|
|
||||||
@Getter
|
|
||||||
private ConfigurationSection config;
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
private Runnable saveAction;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* May be used in testing.
|
* Returns the Bukkit ConfigurationSection. Use [.signalChange] after changing it.
|
||||||
*
|
*/
|
||||||
* @param saveAction What to do to save the config to disk. Don't use get methods until it's non-null.
|
@Getter
|
||||||
*/
|
private var config: ConfigurationSection? = null
|
||||||
public IHaveConfig(Runnable saveAction) {
|
|
||||||
this.saveAction = saveAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
* 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 path The dot-separated path relative to this config instance
|
||||||
* @param <T> The runtime type of the config value
|
* @param <T> The runtime type of the config value
|
||||||
* @return A ConfigData builder to set how to obtain the value
|
* @return A ConfigData builder to set how to obtain the value
|
||||||
*/
|
</T> */
|
||||||
public <T> ConfigData.ConfigDataBuilder<T> getConfig(String path) {
|
fun <T> getConfig(path: String?): ConfigDataBuilder<T> {
|
||||||
return ConfigData.builder(this, path);
|
return ConfigData.builder(this, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
void onConfigBuild(ConfigData<?> config) {
|
fun onConfigBuild(config: ConfigData<*>) {
|
||||||
datamap.put(config.getPath(), config);
|
datamap[config.path] = config
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method overload should only be used with primitives or String.
|
* 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
|
||||||
* @param <T> The type of this variable (only use primitives or String)
|
* @param <T> The type of this variable (only use primitives or String)
|
||||||
* @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
|
||||||
*/
|
</T> */
|
||||||
@SuppressWarnings("unchecked")
|
fun <T> getData(path: String, def: T): ConfigData<T> {
|
||||||
public <T> ConfigData<T> getData(String path, T def) {
|
var data = datamap[path]
|
||||||
ConfigData<?> data = datamap.get(path);
|
if (data == null) datamap[path] = ConfigData(this, path, def, def, null, null).also { data = it }
|
||||||
if (data == null) datamap.put(path, data = new ConfigData<>(this, path, def, def, null, null));
|
@Suppress("UNCHECKED_CAST")
|
||||||
return (ConfigData<T>) data;
|
return data as ConfigData<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method overload may be used with any class.
|
* This method overload may be used with any class.
|
||||||
*
|
*
|
||||||
* @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
|
||||||
* @param getter A function that converts a primitive representation to the correct value
|
* @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 setter A function that converts a value to a primitive representation
|
||||||
* @param <T> The type of this variable (can be any class)
|
* @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
|
* @return The data object that can be used to get or set the value
|
||||||
*/
|
</T> */
|
||||||
@SuppressWarnings("unchecked")
|
fun <T> getData(path: String, def: T, getter: Function<Any?, T>?, setter: Function<T, Any?>): ConfigData<T> {
|
||||||
public <T> ConfigData<T> getData(String path, T def, Function<Object, T> getter, Function<T, Object> setter) {
|
var data = datamap[path]
|
||||||
ConfigData<?> data = datamap.get(path);
|
if (data == null) datamap[path] =
|
||||||
if (data == null)
|
ConfigData(this, path, def, setter.apply(def), getter, setter).also { data = it }
|
||||||
datamap.put(path, data = new ConfigData<>(this, path, def, setter.apply(def), getter, setter));
|
@Suppress("UNCHECKED_CAST")
|
||||||
return (ConfigData<T>) data;
|
return data as ConfigData<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method overload may be used with any class. The given default value will be run through the getter.
|
* 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 path The path in config to use
|
||||||
* @param primitiveDef The <b>primitive</b> value to use by default
|
* @param primitiveDef The **primitive** value to use by default
|
||||||
* @param getter A function that converts a primitive representation to the correct value
|
* @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 setter A function that converts a value to a primitive representation
|
||||||
* @param <T> The type of this variable (can be any class)
|
* @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
|
* @return The data object that can be used to get or set the value
|
||||||
*/
|
</T> */
|
||||||
@SuppressWarnings("unchecked")
|
fun <T> getDataPrimDef(
|
||||||
public <T> ConfigData<T> getDataPrimDef(String path, Object primitiveDef, Function<Object, T> getter, Function<T, Object> setter) {
|
path: String,
|
||||||
ConfigData<?> data = datamap.get(path);
|
primitiveDef: Any?,
|
||||||
if (data == null)
|
getter: Function<Any?, T>,
|
||||||
datamap.put(path, data = new ConfigData<>(this, path, getter.apply(primitiveDef), primitiveDef, getter, setter));
|
setter: Function<T, Any?>?
|
||||||
return (ConfigData<T>) data;
|
): ConfigData<T> {
|
||||||
}
|
var data = datamap[path]
|
||||||
|
if (data == null) datamap[path] =
|
||||||
|
ConfigData(this, path, getter.apply(primitiveDef), primitiveDef, getter, setter).also { data = it }
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return data as ConfigData<T>
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method overload may be used with any class. The given default value will be run through the getter.
|
* 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 path The path in config to use
|
||||||
* @param primitiveDef The <b>primitive</b> value to use by default
|
* @param primitiveDef The **primitive** value to use by default
|
||||||
* @param getter A function that converts a primitive representation to the correct value
|
* @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 setter A function that converts a value to a primitive representation
|
||||||
* @param <T> The type of this variable (can be any class)
|
* @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
|
* @return The data object that can be used to get or set the value
|
||||||
*/
|
</T> */
|
||||||
@SuppressWarnings("unchecked")
|
fun <T> getReadOnlyDataPrimDef(
|
||||||
public <T> ReadOnlyConfigData<T> getReadOnlyDataPrimDef(String path, Object primitiveDef, Function<Object, T> getter, Function<T, Object> setter) {
|
path: String,
|
||||||
ConfigData<?> data = datamap.get(path);
|
primitiveDef: Any?,
|
||||||
if (data == null)
|
getter: Function<Any?, T>,
|
||||||
datamap.put(path, data = new ReadOnlyConfigData<>(this, path, getter.apply(primitiveDef), primitiveDef, getter, setter));
|
setter: Function<T, Any?>?
|
||||||
return (ReadOnlyConfigData<T>) data;
|
): ReadOnlyConfigData<T> {
|
||||||
}
|
var data = datamap[path]
|
||||||
|
if (data == null) datamap[path] =
|
||||||
|
ReadOnlyConfigData(this, path, getter.apply(primitiveDef), primitiveDef, getter, setter).also { data = it }
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return data as ReadOnlyConfigData<T>
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method overload should only be used with primitves or String.
|
* This method overload should only be used with primitves 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
|
||||||
* @param <T> The type of this variable (only use primitives or String)
|
* @param <T> The type of this variable (only use primitives or String)
|
||||||
* @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
|
||||||
*/
|
</T> */
|
||||||
@SuppressWarnings("unchecked")
|
fun <T> getData(path: String, def: Supplier<T>): ConfigData<T> {
|
||||||
public <T> ConfigData<T> getData(String path, Supplier<T> def) {
|
var data = datamap[path]
|
||||||
ConfigData<?> data = datamap.get(path);
|
if (data == null) {
|
||||||
if (data == null) {
|
val defval = def.get()
|
||||||
val defval = def.get();
|
datamap[path] = ConfigData(this, path, defval, defval, null, null).also { data = it }
|
||||||
datamap.put(path, data = new ConfigData<>(this, path, defval, defval, null, null));
|
}
|
||||||
}
|
@Suppress("UNCHECKED_CAST")
|
||||||
return (ConfigData<T>) data;
|
return data as ConfigData<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method overload may be used with any class.
|
* This method overload may be used with any class.
|
||||||
*
|
*
|
||||||
* @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
|
||||||
* @param getter A function that converts a primitive representation to the correct value
|
* @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 setter A function that converts a value to a primitive representation
|
||||||
* @param <T> The type of this variable (can be any class)
|
* @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
|
* @return The data object that can be used to get or set the value
|
||||||
*/
|
</T> */
|
||||||
@SuppressWarnings("unchecked")
|
fun <T> getData(
|
||||||
public <T> ConfigData<T> getData(String path, Supplier<T> def, Function<Object, T> getter, Function<T, Object> setter) {
|
path: String,
|
||||||
ConfigData<?> data = datamap.get(path);
|
def: Supplier<T>,
|
||||||
if (data == null) {
|
getter: Function<Any?, T>?,
|
||||||
val defval = def.get();
|
setter: Function<T, Any?>
|
||||||
datamap.put(path, data = new ConfigData<>(this, path, defval, setter.apply(defval), getter, setter));
|
): ConfigData<T> {
|
||||||
}
|
var data = datamap[path]
|
||||||
return (ConfigData<T>) data;
|
if (data == null) {
|
||||||
}
|
val defval = def.get()
|
||||||
|
datamap[path] = ConfigData(this, path, defval, setter.apply(defval), getter, setter).also { data = it }
|
||||||
|
}
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return data as ConfigData<T>
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method overload should only be used with primitves or String.
|
* This method overload should only be used with primitves or String.
|
||||||
*
|
*
|
||||||
* @param path The path in config to use
|
* @param path The path in config to use
|
||||||
* @param <T> The type of this variable (only use primitives or String)
|
* @param <T> The type of this variable (only use primitives or String)
|
||||||
* @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
|
||||||
*/
|
</T> */
|
||||||
@SuppressWarnings("unchecked")
|
fun <T> getListData(path: String): ListConfigData<T> {
|
||||||
public <T> ListConfigData<T> getListData(String path) {
|
var data = datamap[path]
|
||||||
ConfigData<?> data = datamap.get(path);
|
if (data == null) datamap[path] = ListConfigData(this, path, ListConfigData.List<T>()).also { data = it }
|
||||||
if (data == null)
|
@Suppress("UNCHECKED_CAST")
|
||||||
datamap.put(path, data = new ListConfigData<>(this, path, new ListConfigData.List<T>()));
|
return data as ListConfigData<T>
|
||||||
return (ListConfigData<T>) data;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schedules a save operation. Use after changing the ConfigurationSection directly.
|
* Schedules a save operation. Use after changing the ConfigurationSection directly.
|
||||||
*/
|
*/
|
||||||
public void signalChange() {
|
fun signalChange() {
|
||||||
ConfigData.signalChange(this);
|
ConfigData.signalChange(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears all caches and loads everything from yaml.
|
* Clears all caches and loads everything from yaml.
|
||||||
*/
|
*/
|
||||||
public void reset(ConfigurationSection config) {
|
fun reset(config: ConfigurationSection?) {
|
||||||
this.config = config;
|
this.config = config
|
||||||
datamap.forEach((path, data) -> data.reset());
|
datamap.forEach { (path: String?, data: ConfigData<*>) -> data.reset() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
companion object {
|
||||||
* Generates the config YAML.
|
/**
|
||||||
*
|
* Generates the config YAML.
|
||||||
* @param obj The object which has config methods
|
*
|
||||||
* @param configMap The result from {@link Component#getConfigMap(String, Map)}. May be null.
|
* @param obj The object which has config methods
|
||||||
*/
|
* @param configMap The result from [Component.getConfigMap]. May be null.
|
||||||
public static void pregenConfig(Object obj, @Nullable Map<String, IHaveConfig> configMap) {
|
*/
|
||||||
val ms = obj.getClass().getDeclaredMethods();
|
fun pregenConfig(obj: Any, configMap: Map<String, IHaveConfig?>?) {
|
||||||
for (val m : ms) {
|
val ms = obj.javaClass.declaredMethods
|
||||||
if (!m.getReturnType().getName().equals(ConfigData.class.getName())) continue;
|
for (m in ms) {
|
||||||
final String mName;
|
if (m.returnType.name != ConfigData::class.java.name) continue
|
||||||
{
|
val mName: String
|
||||||
var name = m.getName();
|
run {
|
||||||
var ind = name.lastIndexOf('$');
|
val name = m.name
|
||||||
if (ind == -1) mName = name;
|
val ind = name.lastIndexOf('$')
|
||||||
else mName = name.substring(ind + 1);
|
mName = if (ind == -1) name else name.substring(ind + 1)
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
m.setAccessible(true);
|
m.isAccessible = true
|
||||||
List<ConfigData<?>> configList;
|
var configList: List<ConfigData<*>>
|
||||||
if (m.getParameterCount() == 0) {
|
configList = if (m.parameterCount == 0) {
|
||||||
configList = Collections.singletonList((ConfigData<?>) m.invoke(obj));
|
listOf(m.invoke(obj) as ConfigData<*>)
|
||||||
} else if (m.getParameterCount() == 1 && m.getParameterTypes()[0] == IHaveConfig.class) {
|
} else if (m.parameterCount == 1 && m.parameterTypes[0] == IHaveConfig::class.java) {
|
||||||
if (configMap == null) continue; //Hope it will get called with the param later
|
if (configMap == null) continue //Hope it will get called with the param later
|
||||||
configList = configMap.entrySet().stream().map(kv ->
|
configMap.entries.stream().map { (key, value): Map.Entry<String, IHaveConfig?> ->
|
||||||
{
|
try {
|
||||||
try {
|
return@map m.invoke(obj, value) as ConfigData<*>
|
||||||
return (ConfigData<?>) m.invoke(obj, kv.getValue());
|
} catch (e: IllegalAccessException) {
|
||||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
val msg = "Failed to pregenerate $mName for $obj using config $key!"
|
||||||
String msg = "Failed to pregenerate " + mName + " for " + obj + " using config " + kv.getKey() + "!";
|
if (obj is Component<*>) TBMCCoreAPI.SendException(
|
||||||
if (obj instanceof Component<?>)
|
msg,
|
||||||
TBMCCoreAPI.SendException(msg, e, (Component<?>) obj);
|
e,
|
||||||
else if (obj instanceof JavaPlugin)
|
obj
|
||||||
TBMCCoreAPI.SendException(msg, e, (JavaPlugin) obj);
|
) else if (obj is JavaPlugin) TBMCCoreAPI.SendException(
|
||||||
else
|
msg,
|
||||||
TBMCCoreAPI.SendException(msg, e, false, Bukkit.getLogger()::warning);
|
e,
|
||||||
return null;
|
obj
|
||||||
}
|
) else TBMCCoreAPI.SendException(msg, e, false) { msg: String? ->
|
||||||
}).filter(Objects::nonNull).collect(Collectors.toList());
|
Bukkit.getLogger().warning(msg)
|
||||||
} else {
|
}
|
||||||
if (TBMCCoreAPI.IsTestServer())
|
return@map null
|
||||||
MainPlugin.Instance.getLogger().warning("Method " + mName + " returns a config but its parameters are unknown: " + Arrays.toString(m.getParameterTypes()));
|
} catch (e: InvocationTargetException) {
|
||||||
continue;
|
val msg = "Failed to pregenerate $mName for $obj using config $key!"
|
||||||
}
|
if (obj is Component<*>) TBMCCoreAPI.SendException(
|
||||||
for (val c : configList) {
|
msg,
|
||||||
if (c.getPath().length() == 0)
|
e,
|
||||||
c.setPath(mName);
|
obj
|
||||||
else if (!c.getPath().equals(mName))
|
) else if (obj is JavaPlugin) TBMCCoreAPI.SendException(
|
||||||
MainPlugin.Instance.getLogger().warning("Config name does not match: " + c.getPath() + " instead of " + mName);
|
msg,
|
||||||
c.get(); //Saves the default value if needed - also checks validity
|
e,
|
||||||
}
|
obj
|
||||||
} catch (Exception e) {
|
) else TBMCCoreAPI.SendException(msg, e, false) { msg: String? ->
|
||||||
String msg = "Failed to pregenerate " + mName + " for " + obj + "!";
|
Bukkit.getLogger().warning(msg)
|
||||||
if (obj instanceof Component<?>)
|
}
|
||||||
TBMCCoreAPI.SendException(msg, e, (Component<?>) obj);
|
return@map null
|
||||||
else if (obj instanceof JavaPlugin)
|
}
|
||||||
TBMCCoreAPI.SendException(msg, e, (JavaPlugin) obj);
|
}
|
||||||
else
|
.filter(Predicate<ConfigData<Any?>> { obj: ConfigData<Any?>? -> Objects.nonNull(obj) })
|
||||||
TBMCCoreAPI.SendException(msg, e, false, Bukkit.getLogger()::warning);
|
.collect(Collectors.toList())
|
||||||
}
|
} else {
|
||||||
}
|
if (TBMCCoreAPI.IsTestServer()) MainPlugin.Instance.logger.warning(
|
||||||
}
|
"Method " + mName + " returns a config but its parameters are unknown: " + Arrays.toString(
|
||||||
|
m.parameterTypes
|
||||||
|
)
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for (c in configList) {
|
||||||
|
if (c.path.length == 0) c.setPath(mName) else if (c.path != mName) MainPlugin.Instance.logger.warning(
|
||||||
|
"Config name does not match: " + c.path + " instead of " + mName
|
||||||
|
)
|
||||||
|
c.get() //Saves the default value if needed - also checks validity
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
val msg = "Failed to pregenerate $mName for $obj!"
|
||||||
|
if (obj is Component<*>) TBMCCoreAPI.SendException(
|
||||||
|
msg,
|
||||||
|
e,
|
||||||
|
obj
|
||||||
|
) else if (obj is JavaPlugin) TBMCCoreAPI.SendException(msg, e, obj) else TBMCCoreAPI.SendException(
|
||||||
|
msg,
|
||||||
|
e,
|
||||||
|
false
|
||||||
|
) { msg: String? -> Bukkit.getLogger().warning(msg) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue