Fixed a lot of things
This commit is contained in:
parent
8829795a12
commit
7b83e17fcb
39
src/main/kotlin/hep/dataforge/meta/Config.kt
Normal file
39
src/main/kotlin/hep/dataforge/meta/Config.kt
Normal file
@ -0,0 +1,39 @@
|
||||
package hep.dataforge.meta
|
||||
|
||||
import hep.dataforge.names.Name
|
||||
|
||||
//TODO add validator to configuration
|
||||
|
||||
/**
|
||||
* Mutable meta representing object state
|
||||
*/
|
||||
class Config : MutableMetaNode<Config>() {
|
||||
|
||||
/**
|
||||
* Attach configuration node instead of creating one
|
||||
*/
|
||||
override fun wrap(name: Name, meta: Meta): Config = meta.toConfig()
|
||||
|
||||
override fun empty(): Config = Config()
|
||||
}
|
||||
|
||||
fun Meta.toConfig(): Config = this as? Config ?: Config().also { builder ->
|
||||
this.items.mapValues { entry ->
|
||||
val item = entry.value
|
||||
builder[entry.key] = when (item) {
|
||||
is MetaItem.ValueItem -> MetaItem.ValueItem(item.value)
|
||||
is MetaItem.SingleNodeItem -> MetaItem.SingleNodeItem(item.node.toConfig())
|
||||
is MetaItem.MultiNodeItem -> MetaItem.MultiNodeItem(item.nodes.map { it.toConfig() })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Configurable {
|
||||
val config: Config
|
||||
}
|
||||
|
||||
fun <T : Configurable> T.configure(meta: Meta): T = this.apply { config.update(meta) }
|
||||
|
||||
fun <T : Configurable> T.configure(action: Config.() -> Unit): T = this.apply { config.apply(action) }
|
||||
|
||||
open class SimpleConfigurable(override val config: Config) : Configurable
|
@ -1,37 +0,0 @@
|
||||
package hep.dataforge.meta
|
||||
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.plus
|
||||
|
||||
//TODO add validator to configuration
|
||||
|
||||
/**
|
||||
* Mutable meta representing object state
|
||||
*/
|
||||
class Configuration : MutableMetaNode<Configuration>() {
|
||||
|
||||
/**
|
||||
* Attach configuration node instead of creating one
|
||||
*/
|
||||
override fun wrap(name: Name, meta: Meta): Configuration {
|
||||
return meta as? Configuration ?: Configuration().also { builder ->
|
||||
meta.items.mapValues { entry ->
|
||||
val item = entry.value
|
||||
builder[entry.key] = when (item) {
|
||||
is MetaItem.ValueItem -> MetaItem.ValueItem(item.value)
|
||||
is MetaItem.SingleNodeItem -> MetaItem.SingleNodeItem(wrap(name + entry.key, item.node))
|
||||
is MetaItem.MultiNodeItem -> MetaItem.MultiNodeItem(item.nodes.map { wrap(name + entry.key, it) })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun empty(): Configuration = Configuration()
|
||||
}
|
||||
|
||||
interface Configurable {
|
||||
val config: Configuration
|
||||
}
|
||||
|
||||
open class SimpleConfigurable(override val config: Configuration) : Configurable
|
@ -95,7 +95,7 @@ fun Metoid.number(default: Number, key: String? = null) = SafeNumberDelegate(key
|
||||
|
||||
inline fun <reified E : Enum<E>> Metoid.enum(default: E, key: String? = null) = SafeEnumDelegate(key, default) { enumValueOf(it) }
|
||||
|
||||
/* Configuration delegates */
|
||||
/* Config delegates */
|
||||
|
||||
class ValueConfigDelegate(private val key: String? = null, private val default: Value? = null) : ReadWriteProperty<Configurable, Value?> {
|
||||
override fun getValue(thisRef: Configurable, property: KProperty<*>): Value? {
|
||||
@ -103,7 +103,12 @@ class ValueConfigDelegate(private val key: String? = null, private val default:
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: Configurable, property: KProperty<*>, value: Value?) {
|
||||
thisRef.config[key ?: property.name] = value
|
||||
val name = key ?: property.name
|
||||
if(value == null){
|
||||
thisRef.config.remove(name)
|
||||
} else {
|
||||
thisRef.config[name] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,9 +186,9 @@ class SafeEnumvConfigDelegate<E : Enum<E>>(private val key: String? = null, priv
|
||||
|
||||
//Child node delegate
|
||||
|
||||
class ChildConfigDelegate<T : Configurable>(private val key: String? = null, private val converter: (Configuration) -> T) : ReadWriteProperty<Configurable, T> {
|
||||
class ChildConfigDelegate<T : Configurable>(private val key: String? = null, private val converter: (Config) -> T) : ReadWriteProperty<Configurable, T> {
|
||||
override fun getValue(thisRef: Configurable, property: KProperty<*>): T {
|
||||
return converter(thisRef.config.get(key ?: property.name)?.node ?: Configuration())
|
||||
return converter(thisRef.config[key ?: property.name]?.node ?: Config())
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: Configurable, property: KProperty<*>, value: T) {
|
||||
@ -207,7 +212,7 @@ fun Configurable.number(default: Number? = null, key: String? = null) = NumberCo
|
||||
|
||||
fun Configurable.child(key: String? = null) = ChildConfigDelegate(key) { SimpleConfigurable(it) }
|
||||
|
||||
fun <T : Configurable> Configurable.child(key: String? = null, converter: (Configuration) -> T) = ChildConfigDelegate(key, converter)
|
||||
fun <T : Configurable> Configurable.child(key: String? = null, converter: (Config) -> T) = ChildConfigDelegate(key, converter)
|
||||
|
||||
//fun <T : Configurable> Configurable.spec(spec: Specification<T>, key: String? = null) = ChildConfigDelegate<T>(key) { spec.wrap(this) }
|
||||
|
||||
|
@ -118,3 +118,30 @@ operator fun <M : MutableMeta<M>> M.set(key: String, value: Any?) {
|
||||
else -> set(key, Value.of(value))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update existing mutable node with another node. The rules are following:
|
||||
* * value replaces anything
|
||||
* * node updates node and replaces anything but node
|
||||
* * node list updates node list if number of nodes in the list is the same and replaces anything otherwise
|
||||
*/
|
||||
fun <M : MutableMetaNode<M>> M.update(meta: Meta) {
|
||||
meta.items.forEach { entry ->
|
||||
val value = entry.value
|
||||
when (value) {
|
||||
is MetaItem.ValueItem -> this[entry.key] = value.value
|
||||
is MetaItem.SingleNodeItem -> (this[entry.key] as? MetaItem.SingleNodeItem)
|
||||
?.node?.update(value.node) ?: kotlin.run { this[entry.key] = value.node }
|
||||
is MetaItem.MultiNodeItem -> {
|
||||
val existing = this[entry.key]
|
||||
if (existing is MetaItem.MultiNodeItem && existing.nodes.size == value.nodes.size) {
|
||||
existing.nodes.forEachIndexed { index, m ->
|
||||
m.update(value.nodes[index])
|
||||
}
|
||||
} else {
|
||||
this[entry.key] = value.nodes
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
49
src/main/kotlin/hep/dataforge/meta/Specification.kt
Normal file
49
src/main/kotlin/hep/dataforge/meta/Specification.kt
Normal file
@ -0,0 +1,49 @@
|
||||
package hep.dataforge.meta
|
||||
|
||||
/**
|
||||
* Marker interface for specifications
|
||||
*/
|
||||
interface Specification: Configurable{
|
||||
operator fun get(name: String): MetaItem<Config>? = config.get(name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Specification allows to apply custom configuration in a type safe way to simple untyped configuration
|
||||
*/
|
||||
interface SpecificationBuilder<T : Specification> {
|
||||
/**
|
||||
* Update given configuration using given type as a builder
|
||||
*/
|
||||
fun update(config: Config, action: T.() -> Unit) {
|
||||
wrap(config).apply(action)
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap generic configuration producing instance of desired type
|
||||
*/
|
||||
fun wrap(config: Config): T
|
||||
|
||||
fun wrap(meta: Meta): T = wrap(meta.toConfig())
|
||||
}
|
||||
|
||||
fun <T : Specification> specification(wrapper: (Config) -> T): SpecificationBuilder<T> = object : SpecificationBuilder<T> {
|
||||
override fun wrap(config: Config): T = wrapper(config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply specified configuration to configurable
|
||||
*/
|
||||
fun <T : Configurable, C : Specification, S : SpecificationBuilder<C>> T.configure(spec: S, action: C.() -> Unit) = apply { spec.update(config, action) }
|
||||
|
||||
/**
|
||||
* Update configuration using given specification
|
||||
*/
|
||||
fun <C : Specification, S : SpecificationBuilder<C>> Specification.update(spec: S, action: C.() -> Unit) = apply { spec.update(config, action) }
|
||||
|
||||
/**
|
||||
* Create a style based on given specification
|
||||
*/
|
||||
fun <C : Specification, S : SpecificationBuilder<C>> S.createStyle(action: C.() -> Unit): Meta = Config().also { update(it, action) }
|
||||
|
||||
|
||||
fun <C : Specification> Specification.spec(spec: SpecificationBuilder<C>, key: String? = null) = ChildConfigDelegate<C>(key) { spec.wrap(config) }
|
@ -5,7 +5,7 @@ import hep.dataforge.names.Name
|
||||
/**
|
||||
* A configuration decorator with applied style
|
||||
*/
|
||||
class StyledConfig(val config: Configuration, val style: Meta = EmptyMeta) : MutableMeta<StyledConfig> {
|
||||
class StyledConfig(val config: Config, val style: Meta = EmptyMeta) : MutableMeta<StyledConfig> {
|
||||
|
||||
override fun onChange(owner: Any?, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit) {
|
||||
config.onChange(owner, action)
|
||||
@ -47,6 +47,8 @@ class StyledConfig(val config: Configuration, val style: Meta = EmptyMeta) : Mut
|
||||
}
|
||||
}
|
||||
|
||||
fun Config.withStyle(style: Meta = EmptyMeta) = StyledConfig(this, style)
|
||||
|
||||
interface Styleable : Configurable {
|
||||
val styledConfig: StyledConfig
|
||||
|
||||
|
@ -12,7 +12,7 @@ class MetaDelegateTest {
|
||||
|
||||
@Test
|
||||
fun delegateTest() {
|
||||
val testObject = object : SimpleConfigurable(Configuration()) {
|
||||
val testObject = object : SimpleConfigurable(Config()) {
|
||||
var myValue by string()
|
||||
var safeValue by number(2.2)
|
||||
var enumValue by enum(TestEnum.YES)
|
||||
|
Loading…
Reference in New Issue
Block a user