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) }
|
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?> {
|
class ValueConfigDelegate(private val key: String? = null, private val default: Value? = null) : ReadWriteProperty<Configurable, Value?> {
|
||||||
override fun getValue(thisRef: Configurable, property: KProperty<*>): 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?) {
|
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
|
//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 {
|
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) {
|
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 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) }
|
//fun <T : Configurable> Configurable.spec(spec: Specification<T>, key: String? = null) = ChildConfigDelegate<T>(key) { spec.wrap(this) }
|
||||||
|
|
||||||
|
@ -117,4 +117,31 @@ operator fun <M : MutableMeta<M>> M.set(key: String, value: Any?) {
|
|||||||
is Meta -> set(key, value)
|
is Meta -> set(key, value)
|
||||||
else -> set(key, Value.of(value))
|
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
|
* 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) {
|
override fun onChange(owner: Any?, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit) {
|
||||||
config.onChange(owner, action)
|
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 {
|
interface Styleable : Configurable {
|
||||||
val styledConfig: StyledConfig
|
val styledConfig: StyledConfig
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ class MetaDelegateTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun delegateTest() {
|
fun delegateTest() {
|
||||||
val testObject = object : SimpleConfigurable(Configuration()) {
|
val testObject = object : SimpleConfigurable(Config()) {
|
||||||
var myValue by string()
|
var myValue by string()
|
||||||
var safeValue by number(2.2)
|
var safeValue by number(2.2)
|
||||||
var enumValue by enum(TestEnum.YES)
|
var enumValue by enum(TestEnum.YES)
|
||||||
|
Loading…
Reference in New Issue
Block a user