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
|
* 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> {
|
||||||
var isEnabled = false
|
var isEnabled = false
|
||||||
|
private var wrapper: ButtonComponent<TP>? = null
|
||||||
|
|
||||||
var plugin: TP? = null
|
val config get() = wrapper!!.config
|
||||||
|
val plugin get() = wrapper!!.plugin
|
||||||
val config = IHaveConfig(null)
|
|
||||||
|
|
||||||
private val data //TODO
|
private val data //TODO
|
||||||
: IHaveConfig? = null
|
: IHaveConfig? = null
|
||||||
|
@ -32,11 +32,11 @@ abstract class Component<TP : JavaPlugin?> {
|
||||||
.orElse(true))
|
.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")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -45,7 +45,7 @@ abstract class Component<TP : JavaPlugin?> {
|
||||||
*
|
*
|
||||||
* @param plugin Plugin object
|
* @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.
|
* Unregisters the module, when called by the JavaPlugin class.
|
||||||
|
@ -54,7 +54,7 @@ abstract class Component<TP : JavaPlugin?> {
|
||||||
*
|
*
|
||||||
* @param plugin Plugin object
|
* @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
|
* 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
|
* @param defaultProvider A mapping between config paths and config generators
|
||||||
* @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 = config.config
|
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()
|
val res = cs.getValues(false).entries.stream()
|
||||||
.filter { (_, value): Map.Entry<String?, Any?> -> value is ConfigurationSection }
|
.filter { (_, value) -> value is ConfigurationSection }
|
||||||
.collect(
|
.collect(Collectors.toMap(
|
||||||
Collectors.toMap<Map.Entry<String?, Any?>, String, IHaveConfig>(
|
|
||||||
{ it.key },
|
{ it.key },
|
||||||
{ (_, value): Map.Entry<String?, Any?> ->
|
{ (_, value) -> IHaveConfig(plugin::saveConfig, value as ConfigurationSection) }
|
||||||
val conf = IHaveConfig { plugin!!.saveConfig() }
|
))
|
||||||
conf.reset(value as ConfigurationSection?)
|
|
||||||
conf
|
|
||||||
})
|
|
||||||
)
|
|
||||||
if (res.isEmpty()) {
|
if (res.isEmpty()) {
|
||||||
for ((key1, value) in defaultProvider) {
|
for ((key1, value) in defaultProvider) {
|
||||||
val conf = IHaveConfig { plugin!!.saveConfig() }
|
val conf = IHaveConfig(plugin::saveConfig, cs.createSection(key1))
|
||||||
conf.reset(cs.createSection(key1))
|
|
||||||
value.accept(conf)
|
value.accept(conf)
|
||||||
res[key1] = conf
|
res[key1] = conf
|
||||||
}
|
}
|
||||||
|
@ -159,7 +153,7 @@ abstract class Component<TP : JavaPlugin?> {
|
||||||
return registerUnregisterComponent(plugin, component, false)
|
return registerUnregisterComponent(plugin, component, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : JavaPlugin> registerUnregisterComponent(
|
private fun <T : JavaPlugin> registerUnregisterComponent(
|
||||||
plugin: T,
|
plugin: T,
|
||||||
component: Component<T>,
|
component: Component<T>,
|
||||||
register: Boolean
|
register: Boolean
|
||||||
|
@ -184,9 +178,7 @@ abstract class Component<TP : JavaPlugin?> {
|
||||||
)
|
)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
component.plugin = plugin // TODO: Perhaps construct a new object with these initialized
|
val wrapper = ButtonComponent(plugin, { plugin.saveConfig() }, getConfigSection(plugin, component))
|
||||||
component.config.saveAction = Runnable { plugin.saveConfig() }
|
|
||||||
updateConfig(plugin, component)
|
|
||||||
component.register(plugin)
|
component.register(plugin)
|
||||||
components[component.javaClass] = component
|
components[component.javaClass] = component
|
||||||
if (plugin is ButtonPlugin) (plugin as ButtonPlugin).componentStack.push(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 (component.isEnabled == enabled) return //Don't do anything
|
||||||
if (enabled.also { component.isEnabled = it }) {
|
if (enabled.also { component.isEnabled = it }) {
|
||||||
try {
|
try {
|
||||||
updateConfig(component.plugin!!, component)
|
getConfigSection(component.plugin!!, component)
|
||||||
component.enable()
|
component.enable()
|
||||||
if (ButtonPlugin.configGenAllowed(component)) {
|
if (ButtonPlugin.configGenAllowed(component)) {
|
||||||
IHaveConfig.pregenConfig(component, null)
|
IHaveConfig.pregenConfig(component, null)
|
||||||
|
@ -276,15 +268,13 @@ abstract class Component<TP : JavaPlugin?> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
private fun getConfigSection(plugin: JavaPlugin, component: Component<*>): ConfigurationSection {
|
||||||
fun updateConfig(plugin: JavaPlugin, component: Component<*>) {
|
|
||||||
if (plugin.config != null) { //Production
|
|
||||||
var compconf = plugin.config.getConfigurationSection("components")
|
var compconf = plugin.config.getConfigurationSection("components")
|
||||||
if (compconf == null) compconf = plugin.config.createSection("components")
|
if (compconf == null) compconf = plugin.config.createSection("components")
|
||||||
var configSect = compconf!!.getConfigurationSection(component.className)
|
var configSect = compconf.getConfigurationSection(component.className)
|
||||||
if (configSect == null) configSect = compconf.createSection(component.className)
|
if (configSect == null) configSect = compconf.createSection(component.className)
|
||||||
component.config.reset(configSect)
|
return configSect
|
||||||
} //Testing: it's already set
|
// TODO: Support tests (provide Bukkit configuration for tests)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -44,8 +44,9 @@ class ConfigData<T> internal constructor(
|
||||||
value = null
|
value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun get(): T? {
|
override fun get(): T {
|
||||||
if (value != null) return value //Speed things up
|
val cachedValue = value
|
||||||
|
if (cachedValue != null) return cachedValue //Speed things up
|
||||||
val config = config?.config
|
val config = config?.config
|
||||||
var `val`: Any?
|
var `val`: Any?
|
||||||
if (config == null || !config.isSet(path)) {
|
if (config == null || !config.isSet(path)) {
|
||||||
|
@ -54,14 +55,14 @@ class ConfigData<T> internal constructor(
|
||||||
} else `val` = config.get(path) //config==null: testing
|
} else `val` = config.get(path) //config==null: testing
|
||||||
if (`val` == null) //If it's set to null explicitly
|
if (`val` == null) //If it's set to null explicitly
|
||||||
`val` = pdef
|
`val` = pdef
|
||||||
fun convert(_val: Any?, _pdef: Any?): Any? {
|
fun convert(cval: Any?, cpdef: Any?): Any? {
|
||||||
return if (_pdef is Number) //If we expect a number
|
return if (cpdef is Number) //If we expect a number
|
||||||
if (_val is Number)
|
if (cval is Number)
|
||||||
ChromaUtils.convertNumber(_val as Number?, _pdef.javaClass as Class<out Number?>)
|
ChromaUtils.convertNumber(cval, cpdef.javaClass)
|
||||||
else _pdef //If we didn't get a number, return default (which is a number)
|
else cpdef //If we didn't get a number, return default (which is a number)
|
||||||
else if (_val is List<*> && _pdef != null && _pdef.javaClass.isArray)
|
else if (cval is List<*> && cpdef != null && cpdef.javaClass.isArray)
|
||||||
_val.toTypedArray()
|
cval.toTypedArray()
|
||||||
else _val
|
else cval
|
||||||
}
|
}
|
||||||
return getter.apply(convert(`val`, pdef)).also { value = it }
|
return getter.apply(convert(`val`, pdef)).also { value = it }
|
||||||
}
|
}
|
||||||
|
@ -113,16 +114,19 @@ class ConfigData<T> internal constructor(
|
||||||
fun signalChange(config: IHaveConfig) {
|
fun signalChange(config: IHaveConfig) {
|
||||||
val cc = config.config
|
val cc = config.config
|
||||||
val sa = config.saveAction
|
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)) {
|
if (!saveTasks.containsKey(cc.root)) {
|
||||||
synchronized(saveTasks) {
|
synchronized(saveTasks) {
|
||||||
saveTasks.put(
|
saveTasks.put(
|
||||||
cc.root,
|
root,
|
||||||
SaveTask(Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.Instance, {
|
SaveTask(Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.Instance, {
|
||||||
synchronized(
|
synchronized(saveTasks) {
|
||||||
saveTasks
|
saveTasks.remove(root)
|
||||||
) {
|
sa.run()
|
||||||
saveTasks.remove(cc.getRoot())
|
|
||||||
sa!!.run()
|
|
||||||
}
|
}
|
||||||
}, 100), sa)
|
}, 100), sa)
|
||||||
)
|
)
|
||||||
|
@ -144,8 +148,8 @@ class ConfigData<T> internal constructor(
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> builder(config: IHaveConfig, path: String): ConfigDataBuilder<T> {
|
fun <T> builder(config: IHaveConfig, path: String, primitiveDef: Any?, getter: Function<Any?, T>, setter: Function<T, Any?>): ConfigDataBuilder<T> {
|
||||||
return ConfigDataBuilder(config, path)
|
return ConfigDataBuilder(config, path, primitiveDef, getter, setter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -43,13 +43,14 @@ class IHaveConfig(
|
||||||
* @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
|
||||||
*/
|
*/
|
||||||
fun <T> getConfig(
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun <T> getConfig( // TODO: Remove
|
||||||
path: String,
|
path: String,
|
||||||
def: T,
|
def: T,
|
||||||
getter: Function<Any?, T>? = null,
|
getter: Function<Any?, T>? = null,
|
||||||
setter: Function<T, Any?>? = null
|
setter: Function<T, Any?>? = null
|
||||||
): ConfigDataBuilder<T> {
|
): 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<*>) {
|
fun onConfigBuild(config: IConfigData<*>) {
|
||||||
|
@ -77,7 +78,7 @@ class IHaveConfig(
|
||||||
setter: Function<T, Any?>? = null,
|
setter: Function<T, Any?>? = null,
|
||||||
readOnly: Boolean = false
|
readOnly: Boolean = false
|
||||||
): ConfigData<T> {
|
): 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)
|
* @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> */
|
</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]
|
var data = datamap[path]
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
val defval = def.get()
|
val defval = def.get()
|
||||||
|
|
Loading…
Reference in a new issue