Styleable and updates to Configuration

This commit is contained in:
Alexander Nozik 2018-09-17 16:11:33 +03:00
parent 9395b6fa5f
commit 8829795a12
7 changed files with 113 additions and 34 deletions

View File

@ -5,7 +5,7 @@ plugins {
description = "The basic interfaces for DataForge meta-data" description = "The basic interfaces for DataForge meta-data"
group 'hep.dataforge' group 'hep.dataforge'
version '0.1.0-SNAPSHOT' version '0.1.1-SNAPSHOT'
repositories { repositories {
mavenCentral() mavenCentral()

View File

@ -1,5 +1,8 @@
package hep.dataforge.meta package hep.dataforge.meta
import hep.dataforge.names.Name
import hep.dataforge.names.plus
//TODO add validator to configuration //TODO add validator to configuration
/** /**
@ -10,35 +13,25 @@ class Configuration : MutableMetaNode<Configuration>() {
/** /**
* Attach configuration node instead of creating one * Attach configuration node instead of creating one
*/ */
override fun wrap(meta: Meta): Configuration { override fun wrap(name: Name, meta: Meta): Configuration {
return meta as? Configuration ?: Configuration().also { builder -> return meta as? Configuration ?: Configuration().also { builder ->
items.mapValues { entry -> meta.items.mapValues { entry ->
val item = entry.value val item = entry.value
builder[entry.key] = when (item) { builder[entry.key] = when (item) {
is MetaItem.ValueItem -> MetaItem.ValueItem(item.value) is MetaItem.ValueItem -> MetaItem.ValueItem(item.value)
is MetaItem.SingleNodeItem -> MetaItem.SingleNodeItem(wrap(item.node)) is MetaItem.SingleNodeItem -> MetaItem.SingleNodeItem(wrap(name + entry.key, item.node))
is MetaItem.MultiNodeItem -> MetaItem.MultiNodeItem(item.nodes.map { wrap(it) }) is MetaItem.MultiNodeItem -> MetaItem.MultiNodeItem(item.nodes.map { wrap(name + entry.key, it) })
} }
} }
} }
} }
override fun empty(): Configuration = Configuration()
/** override fun empty(): Configuration = Configuration()
* Universal set method
*/
operator fun set(key: String, value: Any?) {
when (value) {
null -> remove(key)
is Meta -> set(key, value)
else -> set(key, Value.of(value))
}
}
} }
interface Configurable { interface Configurable {
val config: Configuration val config: Configuration
} }
open class SimpleConfigurable(override val config:Configuration): Configurable open class SimpleConfigurable(override val config: Configuration) : Configurable

View File

@ -7,6 +7,8 @@ import kotlin.reflect.KProperty
/* Meta delegates */ /* Meta delegates */
//TODO add caching for sealed nodes
class ValueDelegate(private val key: String? = null, private val default: Value? = null) : ReadOnlyProperty<Metoid, Value?> { class ValueDelegate(private val key: String? = null, private val default: Value? = null) : ReadOnlyProperty<Metoid, Value?> {
override fun getValue(thisRef: Metoid, property: KProperty<*>): Value? { override fun getValue(thisRef: Metoid, property: KProperty<*>): Value? {
return thisRef.meta[key ?: property.name]?.value ?: default return thisRef.meta[key ?: property.name]?.value ?: default

View File

@ -38,7 +38,7 @@ operator fun Meta.get(name: Name): MetaItem<out Meta>? {
return when (name.length) { return when (name.length) {
0 -> error("Can't resolve element from empty name") 0 -> error("Can't resolve element from empty name")
1 -> items[name.first()!!.body] 1 -> items[name.first()!!.body]
else -> name.first()!!.let{ token -> items[token.body]?.nodes?.get(token.query)}?.get(name.cutFirst()) else -> name.first()!!.let { token -> items[token.body]?.nodes?.get(token.query) }?.get(name.cutFirst())
} }
} }
@ -48,14 +48,14 @@ operator fun Meta.get(key: String): MetaItem<out Meta>? = get(key.toName())
/** /**
* A meta node that ensures that all of its descendants has at least the same type * A meta node that ensures that all of its descendants has at least the same type
*/ */
abstract class MetaNode<M: MetaNode<M>>: Meta{ abstract class MetaNode<M : MetaNode<M>> : Meta {
abstract override val items: Map<String, MetaItem<M>> abstract override val items: Map<String, MetaItem<M>>
operator fun get(name: Name): MetaItem<M>? { operator fun get(name: Name): MetaItem<M>? {
return when (name.length) { return when (name.length) {
0 -> error("Can't resolve element from empty name") 0 -> error("Can't resolve element from empty name")
1 -> items[name.first()!!.body] 1 -> items[name.first()!!.body]
else -> name.first()!!.let{ token -> items[token.body]?.nodes?.get(token.query)}?.get(name.cutFirst()) else -> name.first()!!.let { token -> items[token.body]?.nodes?.get(token.query) }?.get(name.cutFirst())
} }
} }
@ -87,9 +87,13 @@ class SealedMeta(meta: Meta) : MetaNode<SealedMeta>() {
*/ */
fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta(this) fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta(this)
object EmptyMeta : Meta {
override val items: Map<String, MetaItem<out Meta>> = emptyMap()
}
/** /**
* Generic meta-holder object * Generic meta-holder object
*/ */
interface Metoid{ interface Metoid {
val meta: Meta val meta: Meta
} }

View File

@ -1,10 +1,12 @@
package hep.dataforge.meta package hep.dataforge.meta
import hep.dataforge.names.Name
/** /**
* DSL builder for meta. Is not intended to store mutable state * DSL builder for meta. Is not intended to store mutable state
*/ */
class MetaBuilder : MutableMetaNode<MetaBuilder>() { class MetaBuilder : MutableMetaNode<MetaBuilder>() {
override fun wrap(meta: Meta): MetaBuilder = meta.builder() override fun wrap(name: Name, meta: Meta): MetaBuilder = meta.builder()
override fun empty(): MetaBuilder = MetaBuilder() override fun empty(): MetaBuilder = MetaBuilder()
infix fun String.to(value: Any) { infix fun String.to(value: Any) {

View File

@ -10,7 +10,10 @@ class MetaListener(val owner: Any? = null, val action: (name: Name, oldItem: Met
interface MutableMeta<M : MutableMeta<M>> : Meta { interface MutableMeta<M : MutableMeta<M>> : Meta {
override val items: Map<String, MetaItem<M>>
operator fun set(name: Name, item: MetaItem<M>?) operator fun set(name: Name, item: MetaItem<M>?)
fun onChange(owner: Any? = null, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit)
fun removeListener(owner: Any)
} }
/** /**
@ -22,14 +25,14 @@ abstract class MutableMetaNode<M : MutableMetaNode<M>> : MetaNode<M>(), MutableM
/** /**
* Add change listener to this meta. Owner is declared to be able to remove listeners later. Listener without owner could not be removed * Add change listener to this meta. Owner is declared to be able to remove listeners later. Listener without owner could not be removed
*/ */
fun onChange(owner: Any? = null, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit) { override fun onChange(owner: Any?, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit) {
listeners.add(MetaListener(owner, action)) listeners.add(MetaListener(owner, action))
} }
/** /**
* Remove all listeners belonging to given owner * Remove all listeners belonging to given owner
*/ */
fun removeListener(owner: Any) { override fun removeListener(owner: Any) {
listeners.removeAll { it.owner === owner } listeners.removeAll { it.owner === owner }
} }
@ -61,13 +64,15 @@ abstract class MutableMetaNode<M : MutableMetaNode<M>> : MetaNode<M>(), MutableM
/** /**
* Transform given meta to node type of this meta tree * Transform given meta to node type of this meta tree
* @param name the name of the node where meta should be attached. Needed for correct assignment validators and styles
* @param meta the node itself
*/ */
protected abstract fun wrap(meta: Meta): M abstract fun wrap(name: Name, meta: Meta): M
/** /**
* Create empty node * Create empty node
*/ */
protected abstract fun empty(): M abstract fun empty(): M
override operator fun set(name: Name, item: MetaItem<M>?) { override operator fun set(name: Name, item: MetaItem<M>?) {
when (name.length) { when (name.length) {
@ -87,14 +92,29 @@ abstract class MutableMetaNode<M : MutableMetaNode<M>> : MetaNode<M>(), MutableM
} }
} }
fun remove(name: String) = set(name.toName(), null)
operator fun set(name: Name, value: Value) = set(name, MetaItem.ValueItem(value)) }
operator fun set(name: Name, meta: Meta) = set(name, MetaItem.SingleNodeItem(wrap(meta)))
operator fun set(name: Name, metas: List<Meta>) = set(name, MetaItem.MultiNodeItem(metas.map { wrap(it) }))
operator fun set(name: String, item: MetaItem<M>) = set(name.toName(), item) fun <M : MutableMeta<M>> M.remove(name: Name) = set(name, null)
operator fun set(name: String, value: Value) = set(name.toName(), MetaItem.ValueItem(value)) fun <M : MutableMeta<M>> M.remove(name: String) = remove(name.toName())
operator fun set(name: String, meta: Meta) = set(name.toName(), MetaItem.SingleNodeItem(wrap(meta)))
operator fun set(name: String, metas: List<Meta>) = set(name.toName(), MetaItem.MultiNodeItem(metas.map { wrap(it) })) operator fun <M : MutableMeta<M>> M.set(name: Name, value: Value) = set(name, MetaItem.ValueItem(value))
operator fun <M : MutableMetaNode<M>> M.set(name: Name, meta: Meta) = set(name, MetaItem.SingleNodeItem(wrap(name, meta)))
operator fun <M : MutableMetaNode<M>> M.set(name: Name, metas: List<Meta>) = set(name, MetaItem.MultiNodeItem(metas.map { wrap(name, it) }))
operator fun <M : MutableMeta<M>> M.set(name: String, item: MetaItem<M>) = set(name.toName(), item)
operator fun <M : MutableMeta<M>> M.set(name: String, value: Value) = set(name.toName(), MetaItem.ValueItem(value))
operator fun <M : MutableMetaNode<M>> M.set(name: String, meta: Meta) = set(name.toName(), meta)
operator fun <M : MutableMetaNode<M>> M.set(name: String, metas: List<Meta>) = set(name.toName(), metas)
/**
* Universal set method
*/
operator fun <M : MutableMeta<M>> M.set(key: String, value: Any?) {
when (value) {
null -> remove(key)
is Meta -> set(key, value)
else -> set(key, Value.of(value))
}
} }

View File

@ -0,0 +1,58 @@
package hep.dataforge.meta
import hep.dataforge.names.Name
/**
* A configuration decorator with applied style
*/
class StyledConfig(val config: Configuration, val style: Meta = EmptyMeta) : MutableMeta<StyledConfig> {
override fun onChange(owner: Any?, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit) {
config.onChange(owner, action)
}
override fun removeListener(owner: Any) {
config.removeListener(owner)
}
override fun set(name: Name, item: MetaItem<StyledConfig>?) {
when (item) {
null -> config.remove(name)
is MetaItem.ValueItem -> config[name] = item.value
is MetaItem.SingleNodeItem -> config[name] = item.node
is MetaItem.MultiNodeItem -> config[name] = item.nodes
}
}
override val items: Map<String, MetaItem<StyledConfig>>
get() = (config.items.keys + style.items.keys).associate { key ->
val value = config.items[key]
val styleValue = style[key]
val item: MetaItem<StyledConfig> = when (value) {
null -> when (styleValue) {
null -> error("Should be unreachable")
is MetaItem.ValueItem -> MetaItem.ValueItem(styleValue.value)
is MetaItem.SingleNodeItem -> MetaItem.SingleNodeItem(StyledConfig(config.empty(), styleValue.node))
is MetaItem.MultiNodeItem -> MetaItem.MultiNodeItem(styleValue.nodes.map { StyledConfig(config.empty(), it) })
}
is MetaItem.ValueItem -> MetaItem.ValueItem(value.value)
is MetaItem.SingleNodeItem -> MetaItem.SingleNodeItem(
StyledConfig(value.node, styleValue?.node ?: EmptyMeta)
)
is MetaItem.MultiNodeItem -> MetaItem.MultiNodeItem(value.nodes.map {
StyledConfig(it, styleValue?.node ?: EmptyMeta)
})
}
key to item
}
}
interface Styleable : Configurable {
val styledConfig: StyledConfig
override val config
get() = styledConfig.config
val style
get() = styledConfig.style
}