Fixed a lot of things

This commit is contained in:
Alexander Nozik 2018-09-17 18:54:49 +03:00
parent 8829795a12
commit 7b83e17fcb
7 changed files with 129 additions and 44 deletions

View 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

View File

@ -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

View File

@ -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) }

View File

@ -118,3 +118,30 @@ operator fun <M : MutableMeta<M>> M.set(key: String, value: Any?) {
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
}
}
}
}
}

View 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) }

View File

@ -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

View File

@ -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)