Config improvements
- Added component wrapper to ensure null safety - Plugin/config properties can no longer be null
This commit is contained in:
parent
8d8708d14b
commit
7b7ad18818
4 changed files with 68 additions and 58 deletions
|
@ -0,0 +1,15 @@
|
|||
package buttondevteam.lib.architecture
|
||||
|
||||
import org.bukkit.configuration.ConfigurationSection
|
||||
import org.bukkit.plugin.java.JavaPlugin
|
||||
|
||||
/**
|
||||
* A wrapper for plugin components. This is used internally.
|
||||
*/
|
||||
class ButtonComponent<TP : JavaPlugin>(
|
||||
val plugin: TP,
|
||||
saveAction: Runnable,
|
||||
config: ConfigurationSection
|
||||
) {
|
||||
val config = IHaveConfig(saveAction, config)
|
||||
}
|
|
@ -16,12 +16,12 @@ import java.util.stream.Collectors
|
|||
* Configuration is based on class name
|
||||
*/
|
||||
@HasConfig(global = false) //Used for obtaining javadoc
|
||||
abstract class Component<TP : JavaPlugin?> {
|
||||
abstract class Component<TP : JavaPlugin> {
|
||||
var isEnabled = false
|
||||
private var wrapper: ButtonComponent<TP>? = null
|
||||
|
||||
var plugin: TP? = null
|
||||
|
||||
val config = IHaveConfig(null)
|
||||
val config get() = wrapper!!.config
|
||||
val plugin get() = wrapper!!.plugin
|
||||
|
||||
private val data //TODO
|
||||
: IHaveConfig? = null
|
||||
|
@ -32,11 +32,11 @@ abstract class Component<TP : JavaPlugin?> {
|
|||
.orElse(true))
|
||||
|
||||
fun log(message: String) {
|
||||
plugin!!.logger.info("[$className] $message")
|
||||
plugin.logger.info("[$className] $message")
|
||||
}
|
||||
|
||||
fun logWarn(message: String) {
|
||||
plugin!!.logger.warning("[$className] $message")
|
||||
plugin.logger.warning("[$className] $message")
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -45,7 +45,7 @@ abstract class Component<TP : JavaPlugin?> {
|
|||
*
|
||||
* @param plugin Plugin object
|
||||
*/
|
||||
protected open fun register(plugin: JavaPlugin?) {}
|
||||
protected open fun register(plugin: JavaPlugin) {}
|
||||
|
||||
/**
|
||||
* Unregisters the module, when called by the JavaPlugin class.
|
||||
|
@ -54,7 +54,7 @@ abstract class Component<TP : JavaPlugin?> {
|
|||
*
|
||||
* @param plugin Plugin object
|
||||
*/
|
||||
protected open fun unregister(plugin: JavaPlugin?) {}
|
||||
protected open fun unregister(plugin: JavaPlugin) {}
|
||||
|
||||
/**
|
||||
* Enables the module, when called by the JavaPlugin class. Call
|
||||
|
@ -100,25 +100,19 @@ abstract class Component<TP : JavaPlugin?> {
|
|||
* @param defaultProvider A mapping between config paths and config generators
|
||||
* @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 = config.config
|
||||
var cs = c.getConfigurationSection(key)
|
||||
if (cs == null) cs = c.createSection(key)
|
||||
val res = cs!!.getValues(false).entries.stream()
|
||||
.filter { (_, value): Map.Entry<String?, Any?> -> value is ConfigurationSection }
|
||||
.collect(
|
||||
Collectors.toMap<Map.Entry<String?, Any?>, String, IHaveConfig>(
|
||||
{ it.key },
|
||||
{ (_, value): Map.Entry<String?, Any?> ->
|
||||
val conf = IHaveConfig { plugin!!.saveConfig() }
|
||||
conf.reset(value as ConfigurationSection?)
|
||||
conf
|
||||
})
|
||||
)
|
||||
val res = cs.getValues(false).entries.stream()
|
||||
.filter { (_, value) -> value is ConfigurationSection }
|
||||
.collect(Collectors.toMap(
|
||||
{ it.key },
|
||||
{ (_, value) -> IHaveConfig(plugin::saveConfig, value as ConfigurationSection) }
|
||||
))
|
||||
if (res.isEmpty()) {
|
||||
for ((key1, value) in defaultProvider) {
|
||||
val conf = IHaveConfig { plugin!!.saveConfig() }
|
||||
conf.reset(cs.createSection(key1))
|
||||
val conf = IHaveConfig(plugin::saveConfig, cs.createSection(key1))
|
||||
value.accept(conf)
|
||||
res[key1] = conf
|
||||
}
|
||||
|
@ -159,7 +153,7 @@ abstract class Component<TP : JavaPlugin?> {
|
|||
return registerUnregisterComponent(plugin, component, false)
|
||||
}
|
||||
|
||||
fun <T : JavaPlugin> registerUnregisterComponent(
|
||||
private fun <T : JavaPlugin> registerUnregisterComponent(
|
||||
plugin: T,
|
||||
component: Component<T>,
|
||||
register: Boolean
|
||||
|
@ -184,9 +178,7 @@ abstract class Component<TP : JavaPlugin?> {
|
|||
)
|
||||
return false
|
||||
}
|
||||
component.plugin = plugin // TODO: Perhaps construct a new object with these initialized
|
||||
component.config.saveAction = Runnable { plugin.saveConfig() }
|
||||
updateConfig(plugin, component)
|
||||
val wrapper = ButtonComponent(plugin, { plugin.saveConfig() }, getConfigSection(plugin, component))
|
||||
component.register(plugin)
|
||||
components[component.javaClass] = component
|
||||
if (plugin is ButtonPlugin) (plugin as ButtonPlugin).componentStack.push(component)
|
||||
|
@ -250,7 +242,7 @@ abstract class Component<TP : JavaPlugin?> {
|
|||
if (component.isEnabled == enabled) return //Don't do anything
|
||||
if (enabled.also { component.isEnabled = it }) {
|
||||
try {
|
||||
updateConfig(component.plugin!!, component)
|
||||
getConfigSection(component.plugin!!, component)
|
||||
component.enable()
|
||||
if (ButtonPlugin.configGenAllowed(component)) {
|
||||
IHaveConfig.pregenConfig(component, null)
|
||||
|
@ -276,15 +268,13 @@ abstract class Component<TP : JavaPlugin?> {
|
|||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun updateConfig(plugin: JavaPlugin, component: Component<*>) {
|
||||
if (plugin.config != null) { //Production
|
||||
var compconf = plugin.config.getConfigurationSection("components")
|
||||
if (compconf == null) compconf = plugin.config.createSection("components")
|
||||
var configSect = compconf!!.getConfigurationSection(component.className)
|
||||
if (configSect == null) configSect = compconf.createSection(component.className)
|
||||
component.config.reset(configSect)
|
||||
} //Testing: it's already set
|
||||
private fun getConfigSection(plugin: JavaPlugin, component: Component<*>): ConfigurationSection {
|
||||
var compconf = plugin.config.getConfigurationSection("components")
|
||||
if (compconf == null) compconf = plugin.config.createSection("components")
|
||||
var configSect = compconf.getConfigurationSection(component.className)
|
||||
if (configSect == null) configSect = compconf.createSection(component.className)
|
||||
return configSect
|
||||
// TODO: Support tests (provide Bukkit configuration for tests)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -44,8 +44,9 @@ class ConfigData<T> internal constructor(
|
|||
value = null
|
||||
}
|
||||
|
||||
override fun get(): T? {
|
||||
if (value != null) return value //Speed things up
|
||||
override fun get(): T {
|
||||
val cachedValue = value
|
||||
if (cachedValue != null) return cachedValue //Speed things up
|
||||
val config = config?.config
|
||||
var `val`: Any?
|
||||
if (config == null || !config.isSet(path)) {
|
||||
|
@ -54,14 +55,14 @@ class ConfigData<T> internal constructor(
|
|||
} else `val` = config.get(path) //config==null: testing
|
||||
if (`val` == null) //If it's set to null explicitly
|
||||
`val` = pdef
|
||||
fun convert(_val: Any?, _pdef: Any?): Any? {
|
||||
return if (_pdef is Number) //If we expect a number
|
||||
if (_val is Number)
|
||||
ChromaUtils.convertNumber(_val as Number?, _pdef.javaClass as Class<out Number?>)
|
||||
else _pdef //If we didn't get a number, return default (which is a number)
|
||||
else if (_val is List<*> && _pdef != null && _pdef.javaClass.isArray)
|
||||
_val.toTypedArray()
|
||||
else _val
|
||||
fun convert(cval: Any?, cpdef: Any?): Any? {
|
||||
return if (cpdef is Number) //If we expect a number
|
||||
if (cval is Number)
|
||||
ChromaUtils.convertNumber(cval, cpdef.javaClass)
|
||||
else cpdef //If we didn't get a number, return default (which is a number)
|
||||
else if (cval is List<*> && cpdef != null && cpdef.javaClass.isArray)
|
||||
cval.toTypedArray()
|
||||
else cval
|
||||
}
|
||||
return getter.apply(convert(`val`, pdef)).also { value = it }
|
||||
}
|
||||
|
@ -113,16 +114,19 @@ class ConfigData<T> internal constructor(
|
|||
fun signalChange(config: IHaveConfig) {
|
||||
val cc = config.config
|
||||
val sa = config.saveAction
|
||||
val root = cc.root
|
||||
if (root == null) {
|
||||
MainPlugin.Instance.logger.warning("Attempted to save config with no root! Name: ${config.config.name}")
|
||||
return
|
||||
}
|
||||
if (!saveTasks.containsKey(cc.root)) {
|
||||
synchronized(saveTasks) {
|
||||
saveTasks.put(
|
||||
cc.root,
|
||||
root,
|
||||
SaveTask(Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.Instance, {
|
||||
synchronized(
|
||||
saveTasks
|
||||
) {
|
||||
saveTasks.remove(cc.getRoot())
|
||||
sa!!.run()
|
||||
synchronized(saveTasks) {
|
||||
saveTasks.remove(root)
|
||||
sa.run()
|
||||
}
|
||||
}, 100), sa)
|
||||
)
|
||||
|
@ -144,8 +148,8 @@ class ConfigData<T> internal constructor(
|
|||
return false
|
||||
}
|
||||
|
||||
fun <T> builder(config: IHaveConfig, path: String): ConfigDataBuilder<T> {
|
||||
return ConfigDataBuilder(config, path)
|
||||
fun <T> builder(config: IHaveConfig, path: String, primitiveDef: Any?, getter: Function<Any?, T>, setter: Function<T, Any?>): ConfigDataBuilder<T> {
|
||||
return ConfigDataBuilder(config, path, primitiveDef, getter, setter)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,13 +43,14 @@ class IHaveConfig(
|
|||
* @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
|
||||
*/
|
||||
fun <T> getConfig(
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <T> getConfig( // TODO: Remove
|
||||
path: String,
|
||||
def: T,
|
||||
getter: Function<Any?, T>? = null,
|
||||
setter: Function<T, Any?>? = null
|
||||
): ConfigDataBuilder<T> {
|
||||
return ConfigData.builder(this, path)
|
||||
return ConfigData.builder(this, path, if (setter != null) setter.apply(def) else def, getter ?: Function { it as T }, setter ?: Function { it })
|
||||
}
|
||||
|
||||
fun onConfigBuild(config: IConfigData<*>) {
|
||||
|
@ -77,7 +78,7 @@ class IHaveConfig(
|
|||
setter: Function<T, Any?>? = null,
|
||||
readOnly: Boolean = false
|
||||
): ConfigData<T> {
|
||||
return getData(path, getter ?: Function { it as T }, setter ?: Function { it }, def)
|
||||
return getData(path, getter ?: Function { it as T }, setter ?: Function { it }, def, readOnly)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -112,7 +113,7 @@ class IHaveConfig(
|
|||
* @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
|
||||
</T> */
|
||||
fun <T> getData(path: String, def: Supplier<T>): ConfigData<T> {
|
||||
fun <T> getData(path: String, def: Supplier<T>): ConfigData<T> { // TODO: Remove
|
||||
var data = datamap[path]
|
||||
if (data == null) {
|
||||
val defval = def.get()
|
||||
|
|
Loading…
Reference in a new issue