Configurable and Scheme revision
This commit is contained in:
parent
736ec621b0
commit
b83821af51
@ -1,8 +1,11 @@
|
||||
[![JetBrains Research](https://jb.gg/badges/research.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
|
||||
[![DOI](https://zenodo.org/badge/148831678.svg)](https://zenodo.org/badge/latestdoi/148831678)
|
||||
|
||||
![Gradle build](https://github.com/mipt-npm/dataforge-core/workflows/Gradle%20build/badge.svg)
|
||||
|
||||
[ ![Download](https://api.bintray.com/packages/mipt-npm/dataforge/dataforge-meta/images/download.svg) ](https://bintray.com/mipt-npm/dataforge/dataforge-meta/_latestVersion)
|
||||
|
||||
[![DOI](https://zenodo.org/badge/148831678.svg)](https://zenodo.org/badge/latestdoi/148831678)
|
||||
|
||||
|
||||
# Questions and Answers #
|
||||
|
||||
|
@ -6,7 +6,7 @@ plugins {
|
||||
id("scientifik.publish") version toolsVersion apply false
|
||||
}
|
||||
|
||||
val dataforgeVersion by extra("0.1.5-dev-6")
|
||||
val dataforgeVersion by extra("0.1.5-dev-7")
|
||||
|
||||
val bintrayRepo by extra("dataforge")
|
||||
val githubProject by extra("dataforge-core")
|
||||
|
@ -4,7 +4,7 @@ import hep.dataforge.meta.*
|
||||
import hep.dataforge.names.toName
|
||||
|
||||
|
||||
class DataFilter(override val config: Config) : Specific {
|
||||
class DataFilter : Scheme() {
|
||||
/**
|
||||
* A source node for the filter
|
||||
*/
|
||||
@ -22,9 +22,7 @@ class DataFilter(override val config: Config) : Specific {
|
||||
|
||||
fun isEmpty(): Boolean = config.isEmpty()
|
||||
|
||||
companion object : Specification<DataFilter> {
|
||||
override fun wrap(config: Config): DataFilter = DataFilter(config)
|
||||
}
|
||||
companion object : SchemeSpec<DataFilter>(::DataFilter)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -133,7 +133,7 @@ object ConfigSerializer : KSerializer<Config> {
|
||||
override val descriptor: SerialDescriptor = MetaSerializer.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): Config {
|
||||
return MetaSerializer.deserialize(decoder).toConfig()
|
||||
return MetaSerializer.deserialize(decoder).asConfig()
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, obj: Config) {
|
||||
|
@ -1,29 +1,24 @@
|
||||
package hep.dataforge.descriptors
|
||||
|
||||
import hep.dataforge.descriptors.Described.Companion.DESCRIPTOR_NODE
|
||||
import hep.dataforge.meta.MetaRepr
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.meta.node
|
||||
|
||||
/**
|
||||
* An object which provides its descriptor
|
||||
*/
|
||||
interface Described {
|
||||
val descriptor: NodeDescriptor
|
||||
val descriptor: NodeDescriptor?
|
||||
|
||||
companion object {
|
||||
const val DESCRIPTOR_NODE = "@descriptor"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If meta node supplies explicit descriptor, return it, otherwise try to use descriptor node from meta itself
|
||||
*/
|
||||
val MetaRepr.descriptor: NodeDescriptor?
|
||||
get() {
|
||||
return if (this is Described) {
|
||||
descriptor
|
||||
} else {
|
||||
toMeta()[DESCRIPTOR_NODE].node?.let { NodeDescriptor.wrap(it) }
|
||||
}
|
||||
}
|
||||
///**
|
||||
// * If meta node supplies explicit descriptor, return it, otherwise try to use descriptor node from meta itself
|
||||
// */
|
||||
//val MetaRepr.descriptor: NodeDescriptor?
|
||||
// get() {
|
||||
// return if (this is Described) {
|
||||
// descriptor
|
||||
// } else {
|
||||
// toMeta()[DESCRIPTOR_NODE].node?.let { NodeDescriptor.wrap(it) }
|
||||
// }
|
||||
// }
|
@ -0,0 +1,28 @@
|
||||
package hep.dataforge.descriptors
|
||||
|
||||
import hep.dataforge.meta.MetaBase
|
||||
import hep.dataforge.meta.MetaItem
|
||||
import hep.dataforge.names.NameToken
|
||||
import hep.dataforge.values.Null
|
||||
|
||||
class DescriptorMeta(val descriptor: NodeDescriptor) : MetaBase() {
|
||||
override val items: Map<NameToken, MetaItem<*>>
|
||||
get() = descriptor.items.entries.associate { entry ->
|
||||
NameToken(entry.key) to entry.value.defaultItem()
|
||||
}
|
||||
}
|
||||
|
||||
fun NodeDescriptor.defaultItem(): MetaItem.NodeItem<*> =
|
||||
MetaItem.NodeItem(default ?: DescriptorMeta(this))
|
||||
|
||||
fun ValueDescriptor.defaultItem(): MetaItem.ValueItem = MetaItem.ValueItem(default ?: Null)
|
||||
|
||||
/**
|
||||
* Build a default [MetaItem] from descriptor.
|
||||
*/
|
||||
fun ItemDescriptor.defaultItem(): MetaItem<*> {
|
||||
return when (this) {
|
||||
is ValueDescriptor -> defaultItem()
|
||||
is NodeDescriptor -> defaultItem()
|
||||
}
|
||||
}
|
@ -4,12 +4,13 @@ import hep.dataforge.meta.*
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.NameToken
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.names.isEmpty
|
||||
import hep.dataforge.values.False
|
||||
import hep.dataforge.values.True
|
||||
import hep.dataforge.values.Value
|
||||
import hep.dataforge.values.ValueType
|
||||
|
||||
sealed class ItemDescriptor(override val config: Config) : Specific {
|
||||
sealed class ItemDescriptor : Scheme() {
|
||||
|
||||
/**
|
||||
* True if same name siblings with this name are allowed
|
||||
@ -30,7 +31,7 @@ sealed class ItemDescriptor(override val config: Config) : Specific {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
var attributes by child()
|
||||
var attributes by config()
|
||||
|
||||
/**
|
||||
* True if the item is required
|
||||
@ -46,7 +47,7 @@ sealed class ItemDescriptor(override val config: Config) : Specific {
|
||||
*
|
||||
* @author Alexander Nozik
|
||||
*/
|
||||
class NodeDescriptor(config: Config) : ItemDescriptor(config) {
|
||||
class NodeDescriptor : ItemDescriptor() {
|
||||
|
||||
/**
|
||||
* True if the node is required
|
||||
@ -60,7 +61,7 @@ class NodeDescriptor(config: Config) : ItemDescriptor(config) {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
var default: Config? by child()
|
||||
var default: Config? by nullableConfig()
|
||||
|
||||
/**
|
||||
* The map of children node descriptors
|
||||
@ -134,18 +135,28 @@ class NodeDescriptor(config: Config) : ItemDescriptor(config) {
|
||||
|
||||
//override val descriptor: NodeDescriptor = empty("descriptor")
|
||||
|
||||
companion object : Specification<NodeDescriptor> {
|
||||
companion object : SchemeSpec<NodeDescriptor>(::NodeDescriptor) {
|
||||
|
||||
// const val ITEM_KEY = "item"
|
||||
const val NODE_KEY = "node"
|
||||
const val VALUE_KEY = "value"
|
||||
|
||||
override fun wrap(config: Config): NodeDescriptor = NodeDescriptor(config)
|
||||
//override fun wrap(config: Config): NodeDescriptor = NodeDescriptor(config)
|
||||
|
||||
//TODO infer descriptor from spec
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a descriptor item associated with given name or null if item for given name not provided
|
||||
*/
|
||||
operator fun ItemDescriptor.get(name: Name): ItemDescriptor? {
|
||||
if (name.isEmpty()) return this
|
||||
return when (this) {
|
||||
is ValueDescriptor -> null // empty name already checked
|
||||
is NodeDescriptor -> items[name.first()!!.toString()]?.get(name.cutFirst())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A descriptor for meta value
|
||||
@ -154,7 +165,7 @@ class NodeDescriptor(config: Config) : ItemDescriptor(config) {
|
||||
*
|
||||
* @author Alexander Nozik
|
||||
*/
|
||||
class ValueDescriptor(config: Config) : ItemDescriptor(config) {
|
||||
class ValueDescriptor : ItemDescriptor() {
|
||||
|
||||
|
||||
/**
|
||||
@ -180,8 +191,8 @@ class ValueDescriptor(config: Config) : ItemDescriptor(config) {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
var type: List<ValueType> by value {
|
||||
it?.list?.map { v -> ValueType.valueOf(v.string) } ?: emptyList()
|
||||
var type: List<ValueType> by item {
|
||||
it?.value?.list?.map { v -> ValueType.valueOf(v.string) } ?: emptyList()
|
||||
}
|
||||
|
||||
fun type(vararg t: ValueType) {
|
||||
@ -222,10 +233,7 @@ class ValueDescriptor(config: Config) : ItemDescriptor(config) {
|
||||
this.allowedValues = v.map { Value.of(it) }
|
||||
}
|
||||
|
||||
companion object : Specification<ValueDescriptor> {
|
||||
|
||||
override fun wrap(config: Config): ValueDescriptor = ValueDescriptor(config)
|
||||
|
||||
companion object : SchemeSpec<ValueDescriptor>(::ValueDescriptor) {
|
||||
inline fun <reified E : Enum<E>> enum(name: String) = ValueDescriptor {
|
||||
type(ValueType.STRING)
|
||||
this.allowedValues = enumValues<E>().map { Value.of(it.name) }
|
||||
|
@ -57,7 +57,7 @@ class Config : AbstractMutableMeta<Config>() {
|
||||
/**
|
||||
* Attach configuration node instead of creating one
|
||||
*/
|
||||
override fun wrapNode(meta: Meta): Config = meta.toConfig()
|
||||
override fun wrapNode(meta: Meta): Config = meta.asConfig()
|
||||
|
||||
override fun empty(): Config = Config()
|
||||
|
||||
@ -68,22 +68,12 @@ class Config : AbstractMutableMeta<Config>() {
|
||||
|
||||
operator fun Config.get(token: NameToken): MetaItem<Config>? = items[token]
|
||||
|
||||
fun Meta.toConfig(): Config = this as? Config ?: Config().also { builder ->
|
||||
fun Meta.asConfig(): Config = this as? Config ?: Config().also { builder ->
|
||||
this.items.mapValues { entry ->
|
||||
val item = entry.value
|
||||
builder[entry.key.asName()] = when (item) {
|
||||
is MetaItem.ValueItem -> item.value
|
||||
is MetaItem.NodeItem -> MetaItem.NodeItem(item.node.toConfig())
|
||||
is MetaItem.NodeItem -> MetaItem.NodeItem(item.node.asConfig())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Configurable {
|
||||
val config: Config
|
||||
}
|
||||
|
||||
fun <T : Configurable> T.configure(meta: Meta): T = this.apply { config.update(meta) }
|
||||
|
||||
fun <T : Configurable> T.configure(action: MetaBuilder.() -> Unit): T = configure(buildMeta(action))
|
||||
|
||||
open class SimpleConfigurable(override val config: Config) : Configurable
|
@ -0,0 +1,44 @@
|
||||
package hep.dataforge.meta
|
||||
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.toName
|
||||
|
||||
/**
|
||||
* A container that holds a [Config] and a default item provider.
|
||||
* Default item provider could be use for example to reference parent configuration.
|
||||
* It is not possible to know if some property is declared by provider just by looking on [Configurable],
|
||||
* this information should be provided externally.
|
||||
*/
|
||||
interface Configurable {
|
||||
/**
|
||||
* Backing config
|
||||
*/
|
||||
val config: Config
|
||||
|
||||
/**
|
||||
* Default meta item provider
|
||||
*/
|
||||
fun getDefaultItem(name: Name): MetaItem<*>? = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a property with default
|
||||
*/
|
||||
fun Configurable.getProperty(name: Name): MetaItem<*>? = config[name] ?: getDefaultItem(name)
|
||||
|
||||
fun Configurable.getProperty(key: String) = getProperty(key.toName())
|
||||
|
||||
/**
|
||||
* Set a configurable property
|
||||
*/
|
||||
fun Configurable.setProperty(name: Name, item: MetaItem<*>?) {
|
||||
config[name] = item
|
||||
}
|
||||
|
||||
fun Configurable.setProperty(key: String, item: MetaItem<*>?) {
|
||||
setProperty(key.toName(), item)
|
||||
}
|
||||
|
||||
fun <T : Configurable> T.configure(meta: Meta): T = this.apply { config.update(meta) }
|
||||
|
||||
fun <T : Configurable> T.configure(action: Config.() -> Unit): T = apply { config.apply(action) }
|
@ -1,9 +1,10 @@
|
||||
package hep.dataforge.meta
|
||||
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.NameToken
|
||||
|
||||
/**
|
||||
* A meta laminate consisting of multiple immutable meta layers. For mutable front layer, use [Styled].
|
||||
* A meta laminate consisting of multiple immutable meta layers. For mutable front layer, use [Scheme].
|
||||
*/
|
||||
class Laminate(layers: List<Meta>) : MetaBase() {
|
||||
|
||||
@ -17,10 +18,11 @@ class Laminate(layers: List<Meta>) : MetaBase() {
|
||||
|
||||
constructor(vararg layers: Meta?) : this(layers.filterNotNull())
|
||||
|
||||
override val items: Map<NameToken, MetaItem<Meta>>
|
||||
get() = layers.map { it.items.keys }.flatten().associateWith { key ->
|
||||
override val items: Map<NameToken, MetaItem<Meta>> by lazy {
|
||||
layers.map { it.items.keys }.flatten().associateWith { key ->
|
||||
layers.asSequence().map { it.items[key] }.filterNotNull().let(replaceRule)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate sealed meta using [mergeRule]
|
||||
@ -77,6 +79,16 @@ class Laminate(layers: List<Meta>) : MetaBase() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performance optimized version of get method
|
||||
*/
|
||||
fun Laminate.getFirst(name: Name): MetaItem<*>? {
|
||||
layers.forEach { layer ->
|
||||
layer[name]?.let { return it }
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new [Laminate] adding given layer to the top
|
||||
*/
|
||||
|
@ -5,6 +5,7 @@ import hep.dataforge.meta.MetaItem.NodeItem
|
||||
import hep.dataforge.meta.MetaItem.ValueItem
|
||||
import hep.dataforge.names.*
|
||||
import hep.dataforge.values.EnumValue
|
||||
import hep.dataforge.values.Null
|
||||
import hep.dataforge.values.Value
|
||||
import hep.dataforge.values.boolean
|
||||
|
||||
@ -22,6 +23,17 @@ sealed class MetaItem<out M : Meta> {
|
||||
data class NodeItem<M : Meta>(val node: M) : MetaItem<M>() {
|
||||
override fun toString(): String = node.toString()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun of(arg: Any?): MetaItem<*> {
|
||||
return when (arg) {
|
||||
null -> ValueItem(Null)
|
||||
is MetaItem<*> -> arg
|
||||
is Meta -> NodeItem(arg)
|
||||
else -> ValueItem(Value.of(arg))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -45,7 +57,7 @@ interface Meta : MetaRepr {
|
||||
*/
|
||||
val items: Map<NameToken, MetaItem<*>>
|
||||
|
||||
override fun toMeta(): Meta = this
|
||||
override fun toMeta(): Meta = seal()
|
||||
|
||||
override fun equals(other: Any?): Boolean
|
||||
|
||||
@ -69,7 +81,7 @@ interface Meta : MetaRepr {
|
||||
/**
|
||||
* Perform recursive item search using given [name]. Each [NameToken] is treated as a name in [Meta.items] of a parent node.
|
||||
*
|
||||
* If [name] is empty reture current [Meta] as a [NodeItem]
|
||||
* If [name] is empty return current [Meta] as a [NodeItem]
|
||||
*/
|
||||
operator fun Meta?.get(name: Name): MetaItem<*>? {
|
||||
if (this == null) return null
|
||||
@ -129,17 +141,8 @@ interface MetaNode<out M : MetaNode<M>> : Meta {
|
||||
/**
|
||||
* The same as [Meta.get], but with specific node type
|
||||
*/
|
||||
operator fun <M : MetaNode<M>> M?.get(name: Name): MetaItem<M>? {
|
||||
if (this == null) return null
|
||||
if (name.isEmpty()) return NodeItem(this)
|
||||
return name.first()?.let { token ->
|
||||
val tail = name.cutFirst()
|
||||
when (tail.length) {
|
||||
0 -> items[token]
|
||||
else -> items[token]?.node?.get(tail)
|
||||
}
|
||||
}
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
operator fun <M : MetaNode<M>> M?.get(name: Name): MetaItem<M>? = (this as Meta)[name] as MetaItem<M>?
|
||||
|
||||
operator fun <M : MetaNode<M>> M?.get(key: String): MetaItem<M>? = this[key.toName()]
|
||||
|
||||
|
@ -105,7 +105,7 @@ operator fun MutableMeta<*>.set(name: Name, value: Any?) {
|
||||
null -> remove(name)
|
||||
is MetaItem<*> -> setItem(name, value)
|
||||
is Meta -> setNode(name, value)
|
||||
is Specific -> setNode(name, value.config)
|
||||
is Configurable -> setNode(name, value.config)
|
||||
else -> setValue(name, Value.of(value))
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,78 @@
|
||||
package hep.dataforge.meta
|
||||
|
||||
import hep.dataforge.descriptors.*
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.NameToken
|
||||
import hep.dataforge.names.plus
|
||||
|
||||
open class Scheme() : Configurable, Described {
|
||||
constructor(config: Config, defaultProvider: (Name) -> MetaItem<*>?) : this() {
|
||||
this.config = config
|
||||
this.defaultProvider = defaultProvider
|
||||
}
|
||||
|
||||
//constructor(config: Config, default: Meta) : this(config, { default[it] })
|
||||
constructor(config: Config) : this(config, { null })
|
||||
|
||||
final override lateinit var config: Config
|
||||
internal set
|
||||
|
||||
lateinit var defaultProvider: (Name) -> MetaItem<*>?
|
||||
internal set
|
||||
|
||||
override val descriptor: NodeDescriptor? = null
|
||||
|
||||
override fun getDefaultItem(name: Name): MetaItem<*>? {
|
||||
return defaultProvider(name) ?: descriptor?.get(name)?.defaultItem()
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a default layer which returns items from [defaultProvider] and falls back to descriptor
|
||||
* values if default value is unavailable.
|
||||
* Values from [defaultProvider] completely replace
|
||||
*/
|
||||
open val defaultLayer: Meta get() = DefaultLayer(Name.EMPTY)
|
||||
|
||||
private inner class DefaultLayer(val path: Name) : MetaBase() {
|
||||
override val items: Map<NameToken, MetaItem<*>> =
|
||||
(descriptor?.get(path) as? NodeDescriptor)?.items?.entries?.associate { (key, descriptor) ->
|
||||
val token = NameToken(key)
|
||||
val fullName = path + token
|
||||
val item: MetaItem<*> = when (descriptor) {
|
||||
is ValueDescriptor -> getDefaultItem(fullName) ?: descriptor.defaultItem()
|
||||
is NodeDescriptor -> MetaItem.NodeItem(DefaultLayer(fullName))
|
||||
}
|
||||
token to item
|
||||
} ?: emptyMap()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A specification for simplified generation of wrappers
|
||||
*/
|
||||
open class SchemeSpec<T : Scheme>(val builder: () -> T) : Specification<T> {
|
||||
override fun wrap(config: Config, defaultProvider: (Name) -> MetaItem<*>?): T {
|
||||
return builder().apply {
|
||||
this.config = config
|
||||
this.defaultProvider = defaultProvider
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class MetaScheme(
|
||||
val meta: Meta,
|
||||
override val descriptor: NodeDescriptor? = null,
|
||||
config: Config = Config()
|
||||
) : Scheme(config, meta::get) {
|
||||
override val defaultLayer: Meta get() = meta
|
||||
}
|
||||
|
||||
fun Meta.toScheme() = MetaScheme(this)
|
||||
|
||||
fun <T : Configurable> Meta.toScheme(spec: Specification<T>, block: T.() -> Unit) = spec.wrap(this).apply(block)
|
||||
|
||||
/**
|
||||
* Create a snapshot laminate
|
||||
*/
|
||||
fun Scheme.toMeta(): Laminate = Laminate(config, defaultLayer)
|
@ -1,101 +0,0 @@
|
||||
package hep.dataforge.meta
|
||||
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.asName
|
||||
import kotlin.jvm.JvmName
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
/**
|
||||
* Marker interface for classes with specifications
|
||||
*/
|
||||
interface Specific : Configurable
|
||||
|
||||
//TODO separate mutable config from immutable meta to allow free wrapping of meta
|
||||
|
||||
operator fun Specific.get(name: String): MetaItem<*>? = config[name]
|
||||
|
||||
/**
|
||||
* Editor for specific objects
|
||||
*/
|
||||
inline operator fun <S : Specific> S.invoke(block: S.() -> Unit): Unit {
|
||||
run(block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to apply custom configuration in a type safe way to simple untyped configuration.
|
||||
* By convention [Specific] companion should inherit this class
|
||||
*
|
||||
*/
|
||||
interface Specification<T : Specific> {
|
||||
/**
|
||||
* Update given configuration using given type as a builder
|
||||
*/
|
||||
fun update(config: Config, action: T.() -> Unit): T {
|
||||
return wrap(config).apply(action)
|
||||
}
|
||||
|
||||
operator fun invoke(action: T.() -> Unit) = update(Config(), action)
|
||||
|
||||
fun empty() = wrap(Config())
|
||||
|
||||
/**
|
||||
* Wrap generic configuration producing instance of desired type
|
||||
*/
|
||||
fun wrap(config: Config): T
|
||||
|
||||
//TODO replace by free wrapper
|
||||
fun wrap(meta: Meta): T = wrap(meta.toConfig())
|
||||
}
|
||||
|
||||
fun <T : Specific> specification(wrapper: (Config) -> T): Specification<T> =
|
||||
object : Specification<T> {
|
||||
override fun wrap(config: Config): T = wrapper(config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply specified configuration to configurable
|
||||
*/
|
||||
fun <T : Configurable, C : Specific, S : Specification<C>> T.configure(spec: S, action: C.() -> Unit) =
|
||||
apply { spec.update(config, action) }
|
||||
|
||||
/**
|
||||
* Update configuration using given specification
|
||||
*/
|
||||
fun <C : Specific, S : Specification<C>> Specific.update(spec: S, action: C.() -> Unit) =
|
||||
apply { spec.update(config, action) }
|
||||
|
||||
/**
|
||||
* Create a style based on given specification
|
||||
*/
|
||||
fun <C : Specific, S : Specification<C>> S.createStyle(action: C.() -> Unit): Meta =
|
||||
Config().also { update(it, action) }
|
||||
|
||||
class SpecDelegate<T : Specific, S : Specification<T>>(
|
||||
val target: Specific,
|
||||
val spec: S,
|
||||
val key: Name? = null
|
||||
) : ReadWriteProperty<Any?, T> {
|
||||
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
||||
val name = key ?: property.name.asName()
|
||||
return target.config[name]?.node?.let { spec.wrap(it) } ?: (spec.empty().also {
|
||||
target.config[name] = it.config
|
||||
})
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||
target.config[key ?: property.name.asName()] = value.config
|
||||
}
|
||||
}
|
||||
|
||||
fun <T : Specific, S : Specification<T>> Specific.spec(
|
||||
spec: S,
|
||||
key: Name? = null
|
||||
): SpecDelegate<T, S> = SpecDelegate(this, spec, key)
|
||||
|
||||
fun <T : Specific> MetaItem<*>.spec(spec: Specification<T>): T? = node?.let { spec.wrap(it) }
|
||||
|
||||
@JvmName("configSpec")
|
||||
fun <T : Specific> MetaItem<Config>.spec(spec: Specification<T>): T? = node?.let { spec.wrap(it) }
|
||||
|
@ -0,0 +1,60 @@
|
||||
package hep.dataforge.meta
|
||||
|
||||
import hep.dataforge.names.Name
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
/**
|
||||
* Allows to apply custom configuration in a type safe way to simple untyped configuration.
|
||||
* By convention [Scheme] companion should inherit this class
|
||||
*
|
||||
*/
|
||||
interface Specification<T : Configurable> {
|
||||
/**
|
||||
* Update given configuration using given type as a builder
|
||||
*/
|
||||
fun update(config: Config, action: T.() -> Unit): T {
|
||||
return wrap(config).apply(action)
|
||||
}
|
||||
|
||||
operator fun invoke(action: T.() -> Unit) = update(Config(), action)
|
||||
|
||||
fun empty() = wrap()
|
||||
|
||||
/**
|
||||
* Wrap generic configuration producing instance of desired type
|
||||
*/
|
||||
fun wrap(config: Config = Config(), defaultProvider: (Name) -> MetaItem<*>? = { null }): T
|
||||
|
||||
/**
|
||||
* Wrap a configuration using static meta as default
|
||||
*/
|
||||
fun wrap(config: Config = Config(), default: Meta): T = wrap(config){default[it]}
|
||||
|
||||
/**
|
||||
* Wrap a configuration using static meta as default
|
||||
*/
|
||||
fun wrap(default: Meta): T = wrap(Config()){default[it]}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply specified configuration to configurable
|
||||
*/
|
||||
fun <T : Configurable, C : Configurable, S : Specification<C>> T.configure(spec: S, action: C.() -> Unit) =
|
||||
apply { spec.update(config, action) }
|
||||
|
||||
/**
|
||||
* Update configuration using given specification
|
||||
*/
|
||||
fun <C : Configurable, S : Specification<C>> Configurable.update(spec: S, action: C.() -> Unit) =
|
||||
apply { spec.update(config, action) }
|
||||
|
||||
/**
|
||||
* Create a style based on given specification
|
||||
*/
|
||||
fun <C : Configurable, S : Specification<C>> S.createStyle(action: C.() -> Unit): Meta =
|
||||
Config().also { update(it, action) }
|
||||
|
||||
fun <T : Configurable> MetaItem<*>.spec(spec: Specification<T>): T? = node?.let { spec.wrap(Config(), it) }
|
||||
|
||||
@JvmName("configSpec")
|
||||
fun <T : Configurable> MetaItem<Config>.spec(spec: Specification<T>): T? = node?.let { spec.wrap(it) }
|
@ -1,72 +0,0 @@
|
||||
package hep.dataforge.meta
|
||||
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.NameToken
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
|
||||
/**
|
||||
* A meta object with read-only meta base and changeable configuration on top of it
|
||||
* @param base - unchangeable base
|
||||
* @param style - the style
|
||||
*/
|
||||
class Styled(val base: Meta, val style: Config = Config().empty()) : AbstractMutableMeta<Styled>() {
|
||||
override fun wrapNode(meta: Meta): Styled = Styled(meta)
|
||||
|
||||
override fun empty(): Styled = Styled(EmptyMeta)
|
||||
|
||||
override val items: Map<NameToken, MetaItem<Styled>>
|
||||
get() = (base.items.keys + style.items.keys).associate { key ->
|
||||
val value = base.items[key]
|
||||
val styleValue = style[key]
|
||||
val item: MetaItem<Styled> = when (value) {
|
||||
null -> when (styleValue) {
|
||||
null -> error("Should be unreachable")
|
||||
is MetaItem.NodeItem -> MetaItem.NodeItem(Styled(style.empty(), styleValue.node))
|
||||
is MetaItem.ValueItem -> styleValue
|
||||
}
|
||||
is MetaItem.ValueItem -> value
|
||||
is MetaItem.NodeItem -> MetaItem.NodeItem(
|
||||
Styled(value.node, styleValue?.node ?: Config.empty())
|
||||
)
|
||||
}
|
||||
key to item
|
||||
}
|
||||
|
||||
override fun set(name: Name, item: MetaItem<*>?) {
|
||||
if (item == null) {
|
||||
style.remove(name)
|
||||
} else {
|
||||
style[name] = item
|
||||
}
|
||||
}
|
||||
|
||||
fun onChange(owner: Any?, action: (Name, before: MetaItem<*>?, after: MetaItem<*>?) -> Unit) {
|
||||
//TODO test correct behavior
|
||||
style.onChange(owner) { name, before, after -> action(name, before ?: base[name], after ?: base[name]) }
|
||||
}
|
||||
|
||||
fun removeListener(owner: Any?) {
|
||||
style.removeListener(owner)
|
||||
}
|
||||
}
|
||||
|
||||
fun Styled.configure(meta: Meta) = apply { style.update(meta) }
|
||||
|
||||
fun Meta.withStyle(style: Meta = EmptyMeta) = if (this is Styled) {
|
||||
this.apply { this.configure(style) }
|
||||
} else {
|
||||
Styled(this, style.toConfig())
|
||||
}
|
||||
|
||||
class StyledNodeDelegate(val owner: Styled, val key: String?) : ReadWriteProperty<Any?, Meta> {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Meta {
|
||||
return owner[key ?: property.name]?.node ?: EmptyMeta
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Meta) {
|
||||
owner.style[key ?: property.name] = value
|
||||
}
|
||||
|
||||
}
|
@ -1,137 +0,0 @@
|
||||
package hep.dataforge.meta
|
||||
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.values.DoubleArrayValue
|
||||
import hep.dataforge.values.Null
|
||||
import hep.dataforge.values.Value
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
|
||||
//Configurable delegates
|
||||
|
||||
/**
|
||||
* A property delegate that uses custom key
|
||||
*/
|
||||
fun Configurable.value(default: Any = Null, key: Name? = null): MutableValueDelegate<Config> =
|
||||
MutableValueDelegate(config, key, Value.of(default))
|
||||
|
||||
fun <T> Configurable.value(
|
||||
default: T? = null,
|
||||
key: Name? = null,
|
||||
writer: (T) -> Value = { Value.of(it) },
|
||||
reader: (Value?) -> T
|
||||
): ReadWriteDelegateWrapper<Value?, T> =
|
||||
MutableValueDelegate(config, key, default?.let { Value.of(it) }).transform(reader = reader, writer = writer)
|
||||
|
||||
fun Configurable.string(default: String? = null, key: Name? = null): MutableStringDelegate<Config> =
|
||||
MutableStringDelegate(config, key, default)
|
||||
|
||||
fun Configurable.boolean(default: Boolean? = null, key: Name? = null): MutableBooleanDelegate<Config> =
|
||||
MutableBooleanDelegate(config, key, default)
|
||||
|
||||
fun Configurable.number(default: Number? = null, key: Name? = null): MutableNumberDelegate<Config> =
|
||||
MutableNumberDelegate(config, key, default)
|
||||
|
||||
/* Number delegates*/
|
||||
|
||||
fun Configurable.int(default: Int? = null, key: Name? = null) =
|
||||
number(default, key).int
|
||||
|
||||
fun Configurable.double(default: Double? = null, key: Name? = null) =
|
||||
number(default, key).double
|
||||
|
||||
fun Configurable.long(default: Long? = null, key: Name? = null) =
|
||||
number(default, key).long
|
||||
|
||||
fun Configurable.short(default: Short? = null, key: Name? = null) =
|
||||
number(default, key).short
|
||||
|
||||
fun Configurable.float(default: Float? = null, key: Name? = null) =
|
||||
number(default, key).float
|
||||
|
||||
|
||||
@JvmName("safeString")
|
||||
fun Configurable.string(default: String, key: Name? = null) =
|
||||
MutableSafeStringDelegate(config, key) { default }
|
||||
|
||||
@JvmName("safeBoolean")
|
||||
fun Configurable.boolean(default: Boolean, key: Name? = null) =
|
||||
MutableSafeBooleanDelegate(config, key) { default }
|
||||
|
||||
@JvmName("safeNumber")
|
||||
fun Configurable.number(default: Number, key: Name? = null) =
|
||||
MutableSafeNumberDelegate(config, key) { default }
|
||||
|
||||
@JvmName("safeString")
|
||||
fun Configurable.string(key: Name? = null, default: () -> String) =
|
||||
MutableSafeStringDelegate(config, key, default)
|
||||
|
||||
@JvmName("safeBoolean")
|
||||
fun Configurable.boolean(key: Name? = null, default: () -> Boolean) =
|
||||
MutableSafeBooleanDelegate(config, key, default)
|
||||
|
||||
@JvmName("safeNumber")
|
||||
fun Configurable.number(key: Name? = null, default: () -> Number) =
|
||||
MutableSafeNumberDelegate(config, key, default)
|
||||
|
||||
|
||||
/* Safe number delegates*/
|
||||
|
||||
@JvmName("safeInt")
|
||||
fun Configurable.int(default: Int, key: Name? = null) =
|
||||
number(default, key).int
|
||||
|
||||
@JvmName("safeDouble")
|
||||
fun Configurable.double(default: Double, key: Name? = null) =
|
||||
number(default, key).double
|
||||
|
||||
@JvmName("safeLong")
|
||||
fun Configurable.long(default: Long, key: Name? = null) =
|
||||
number(default, key).long
|
||||
|
||||
@JvmName("safeShort")
|
||||
fun Configurable.short(default: Short, key: Name? = null) =
|
||||
number(default, key).short
|
||||
|
||||
@JvmName("safeFloat")
|
||||
fun Configurable.float(default: Float, key: Name? = null) =
|
||||
number(default, key).float
|
||||
|
||||
/**
|
||||
* Enum delegate
|
||||
*/
|
||||
inline fun <reified E : Enum<E>> Configurable.enum(default: E, key: Name? = null) =
|
||||
MutableSafeEnumvDelegate(config, key, default) { enumValueOf(it) }
|
||||
|
||||
/* Node delegates */
|
||||
|
||||
fun Configurable.child(key: Name? = null): MutableNodeDelegate<Config> = MutableNodeDelegate(config, key)
|
||||
|
||||
fun <T : Specific> Configurable.spec(spec: Specification<T>, key: Name? = null) =
|
||||
MutableMorphDelegate(config, key) { spec.wrap(it) }
|
||||
|
||||
fun <T : Specific> Configurable.spec(builder: (Config) -> T, key: Name? = null) =
|
||||
MutableMorphDelegate(config, key) { specification(builder).wrap(it) }
|
||||
|
||||
/*
|
||||
* Extra delegates for special cases
|
||||
*/
|
||||
|
||||
fun Configurable.stringList(vararg strings: String, key: Name? = null): ReadWriteDelegateWrapper<Value?, List<String>> =
|
||||
value(strings.asList(), key) { it?.list?.map { value -> value.string } ?: emptyList() }
|
||||
|
||||
fun Configurable.numberList(vararg numbers: Number, key: Name? = null): ReadWriteDelegateWrapper<Value?, List<Number>> =
|
||||
value(numbers.asList(), key) { it?.list?.map { value -> value.number } ?: emptyList() }
|
||||
|
||||
/**
|
||||
* A special delegate for double arrays
|
||||
*/
|
||||
fun Configurable.doubleArray(key: Name? = null): ReadWriteDelegateWrapper<Value?, DoubleArray> =
|
||||
value(doubleArrayOf(), key) {
|
||||
(it as? DoubleArrayValue)?.value
|
||||
?: it?.list?.map { value -> value.number.toDouble() }?.toDoubleArray()
|
||||
?: doubleArrayOf()
|
||||
}
|
||||
|
||||
fun <T : Configurable> Configurable.child(key: Name? = null, converter: (Meta) -> T) =
|
||||
MutableMorphDelegate(config, key, converter)
|
@ -0,0 +1,236 @@
|
||||
package hep.dataforge.meta
|
||||
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.values.*
|
||||
import kotlin.jvm.JvmName
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
|
||||
//delegates
|
||||
|
||||
/**
|
||||
* A delegate that uses a [Configurable] object and delegate read and write operations to its properties
|
||||
*/
|
||||
open class ConfigurableDelegate(
|
||||
val owner: Configurable,
|
||||
val key: Name? = null,
|
||||
open val default: MetaItem<*>? = null
|
||||
) : ReadWriteProperty<Any?, MetaItem<*>?> {
|
||||
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): MetaItem<*>? {
|
||||
val name = key ?: property.name.asName()
|
||||
return owner.getProperty(name) ?: default
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: MetaItem<*>?) {
|
||||
val name = key ?: property.name.asName()
|
||||
owner.setProperty(name, value)
|
||||
}
|
||||
|
||||
fun <T> transform(
|
||||
writer: (T) -> MetaItem<*>? = { MetaItem.of(it) },
|
||||
reader: (MetaItem<*>?) -> T
|
||||
): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
||||
return reader(this@ConfigurableDelegate.getValue(thisRef, property))
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||
this@ConfigurableDelegate.setValue(thisRef, property, writer(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LazyConfigurableDelegate(
|
||||
configurable: Configurable,
|
||||
key: Name? = null,
|
||||
defaultProvider: () -> MetaItem<*>? = { null }
|
||||
) : ConfigurableDelegate(configurable, key) {
|
||||
override val default by lazy(defaultProvider)
|
||||
}
|
||||
|
||||
/**
|
||||
* A property delegate that uses custom key
|
||||
*/
|
||||
fun Configurable.item(default: Any?, key: Name? = null): ConfigurableDelegate =
|
||||
ConfigurableDelegate(this, key, MetaItem.of(default))
|
||||
|
||||
/**
|
||||
* Generation of item delegate with lazy default.
|
||||
* Lazy default could be used also for validation
|
||||
*/
|
||||
fun Configurable.lazyItem(key: Name? = null, default: () -> Any?): ConfigurableDelegate =
|
||||
LazyConfigurableDelegate(this, key) { default()?.let { MetaItem.of(it) } }
|
||||
|
||||
fun <T> Configurable.item(
|
||||
default: T? = null,
|
||||
key: Name? = null,
|
||||
writer: (T) -> MetaItem<*>? = { MetaItem.of(it) },
|
||||
reader: (MetaItem<*>?) -> T
|
||||
): ReadWriteProperty<Any?, T> =
|
||||
ConfigurableDelegate(this, key, default?.let { MetaItem.of(it) }).transform(reader = reader, writer = writer)
|
||||
|
||||
fun Configurable.value(default: Any? = null, key: Name? = null): ReadWriteProperty<Any?, Value?> =
|
||||
item(default, key).transform { it.value }
|
||||
|
||||
fun <T> Configurable.value(
|
||||
default: T? = null,
|
||||
key: Name? = null,
|
||||
writer: (T) -> Value? = { Value.of(it) },
|
||||
reader: (Value?) -> T
|
||||
): ReadWriteProperty<Any?, T> =
|
||||
ConfigurableDelegate(this, key, default?.let { MetaItem.of(it) }).transform(
|
||||
reader = { reader(it.value) },
|
||||
writer = { writer(it)?.let { MetaItem.ValueItem(it) } }
|
||||
)
|
||||
|
||||
fun Configurable.string(default: String? = null, key: Name? = null): ReadWriteProperty<Any?, String?> =
|
||||
item(default, key).transform { it.value?.string }
|
||||
|
||||
fun Configurable.boolean(default: Boolean? = null, key: Name? = null): ReadWriteProperty<Any?, Boolean?> =
|
||||
item(default, key).transform { it.value?.boolean }
|
||||
|
||||
fun Configurable.number(default: Number? = null, key: Name? = null): ReadWriteProperty<Any?, Number?> =
|
||||
item(default, key).transform { it.value?.number }
|
||||
|
||||
/* Number delegates*/
|
||||
|
||||
fun Configurable.int(default: Int? = null, key: Name? = null): ReadWriteProperty<Any?, Int?> =
|
||||
item(default, key).transform { it.value?.int }
|
||||
|
||||
fun Configurable.double(default: Double? = null, key: Name? = null): ReadWriteProperty<Any?, Double?> =
|
||||
item(default, key).transform { it.value?.double }
|
||||
|
||||
fun Configurable.long(default: Long? = null, key: Name? = null): ReadWriteProperty<Any?, Long?> =
|
||||
item(default, key).transform { it.value?.long }
|
||||
|
||||
fun Configurable.short(default: Short? = null, key: Name? = null): ReadWriteProperty<Any?, Short?> =
|
||||
item(default, key).transform { it.value?.short }
|
||||
|
||||
fun Configurable.float(default: Float? = null, key: Name? = null): ReadWriteProperty<Any?, Float?> =
|
||||
item(default, key).transform { it.value?.float }
|
||||
|
||||
|
||||
@JvmName("safeString")
|
||||
fun Configurable.string(default: String, key: Name? = null): ReadWriteProperty<Any?, String> =
|
||||
item(default, key).transform { it.value!!.string }
|
||||
|
||||
@JvmName("safeBoolean")
|
||||
fun Configurable.boolean(default: Boolean, key: Name? = null): ReadWriteProperty<Any?, Boolean> =
|
||||
item(default, key).transform { it.value!!.boolean }
|
||||
|
||||
@JvmName("safeNumber")
|
||||
fun Configurable.number(default: Number, key: Name? = null): ReadWriteProperty<Any?, Number> =
|
||||
item(default, key).transform { it.value!!.number }
|
||||
|
||||
/* Lazy initializers for values */
|
||||
|
||||
@JvmName("lazyString")
|
||||
fun Configurable.string(key: Name? = null, default: () -> String): ReadWriteProperty<Any?, String> =
|
||||
lazyItem(key, default).transform { it.value!!.string }
|
||||
|
||||
@JvmName("lazyBoolean")
|
||||
fun Configurable.boolean(key: Name? = null, default: () -> Boolean): ReadWriteProperty<Any?, Boolean> =
|
||||
lazyItem(key, default).transform { it.value!!.boolean }
|
||||
|
||||
@JvmName("lazyNumber")
|
||||
fun Configurable.number(key: Name? = null, default: () -> Number): ReadWriteProperty<Any?, Number> =
|
||||
lazyItem(key, default).transform { it.value!!.number }
|
||||
|
||||
/* Safe number delegates*/
|
||||
|
||||
@JvmName("safeInt")
|
||||
fun Configurable.int(default: Int, key: Name? = null): ReadWriteProperty<Any?, Int> =
|
||||
item(default, key).transform { it.value!!.int }
|
||||
|
||||
@JvmName("safeDouble")
|
||||
fun Configurable.double(default: Double, key: Name? = null): ReadWriteProperty<Any?, Double> =
|
||||
item(default, key).transform { it.value!!.double }
|
||||
|
||||
@JvmName("safeLong")
|
||||
fun Configurable.long(default: Long, key: Name? = null): ReadWriteProperty<Any?, Long> =
|
||||
item(default, key).transform { it.value!!.long }
|
||||
|
||||
@JvmName("safeShort")
|
||||
fun Configurable.short(default: Short, key: Name? = null): ReadWriteProperty<Any?, Short> =
|
||||
item(default, key).transform { it.value!!.short }
|
||||
|
||||
@JvmName("safeFloat")
|
||||
fun Configurable.float(default: Float, key: Name? = null): ReadWriteProperty<Any?, Float> =
|
||||
item(default, key).transform { it.value!!.float }
|
||||
|
||||
/**
|
||||
* Enum delegate
|
||||
*/
|
||||
inline fun <reified E : Enum<E>> Configurable.enum(default: E, key: Name? = null): ReadWriteProperty<Any?, E?> =
|
||||
item(default, key).transform { it.enum<E>() }
|
||||
|
||||
/*
|
||||
* Extra delegates for special cases
|
||||
*/
|
||||
|
||||
fun Configurable.stringList(vararg strings: String, key: Name? = null): ReadWriteProperty<Any?, List<String>> =
|
||||
item(listOf(*strings), key) {
|
||||
it?.value?.stringList ?: emptyList()
|
||||
}
|
||||
|
||||
fun Configurable.numberList(vararg numbers: Number, key: Name? = null): ReadWriteProperty<Any?, List<Number>> =
|
||||
item(listOf(*numbers), key) { item ->
|
||||
item?.value?.list?.map { it.number } ?: emptyList()
|
||||
}
|
||||
|
||||
/**
|
||||
* A special delegate for double arrays
|
||||
*/
|
||||
fun Configurable.doubleArray(vararg doubles: Double, key: Name? = null): ReadWriteProperty<Any?, DoubleArray> =
|
||||
item(doubleArrayOf(*doubles), key) {
|
||||
(it.value as? DoubleArrayValue)?.value
|
||||
?: it?.value?.list?.map { value -> value.number.toDouble() }?.toDoubleArray()
|
||||
?: doubleArrayOf()
|
||||
}
|
||||
|
||||
|
||||
/* Node delegates */
|
||||
|
||||
fun Configurable.nullableConfig(key: Name? = null): ReadWriteProperty<Any?, Config?> =
|
||||
object : ReadWriteProperty<Any?, Config?> {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Config? {
|
||||
val name = key ?: property.name.asName()
|
||||
return config[name].node
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Config?) {
|
||||
val name = key ?: property.name.asName()
|
||||
config[name] = value
|
||||
}
|
||||
}
|
||||
|
||||
fun Configurable.config(key: Name? = null, default: Config.() -> Unit = {}): ReadWriteProperty<Any?, Config> =
|
||||
object : ReadWriteProperty<Any?, Config> {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Config {
|
||||
val name = key ?: property.name.asName()
|
||||
return config[name].node ?: Config().apply(default).also { config[name] = it }
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Config) {
|
||||
val name = key ?: property.name.asName()
|
||||
config[name] = value
|
||||
}
|
||||
}
|
||||
|
||||
fun <T : Configurable> Configurable.spec(spec: Specification<T>, key: Name? = null): ReadWriteProperty<Any?, T?> =
|
||||
object : ReadWriteProperty<Any?, T?> {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
|
||||
val name = key ?: property.name.asName()
|
||||
return config[name].node?.let { spec.wrap(it) }
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
|
||||
val name = key ?: property.name.asName()
|
||||
config[name] = value?.config
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -350,24 +350,6 @@ class MutableNodeDelegate<M : MutableMeta<M>>(
|
||||
}
|
||||
}
|
||||
|
||||
class MutableMorphDelegate<T : Configurable>(
|
||||
val meta: MutableMeta<*>,
|
||||
private val key: Name? = null,
|
||||
private val converter: (Meta) -> T
|
||||
) : ReadWriteProperty<Any?, T?> {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
|
||||
return meta[key ?: property.name.asName()]?.node?.let(converter)
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
|
||||
if (value == null) {
|
||||
meta.remove(key ?: property.name.asName())
|
||||
} else {
|
||||
meta[key ?: property.name.asName()] = value.config
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ReadWriteDelegateWrapper<T, R>(
|
||||
val delegate: ReadWriteProperty<Any?, T>,
|
||||
val reader: (T) -> R,
|
||||
|
@ -22,6 +22,7 @@ val Value.boolean
|
||||
val Value.int get() = number.toInt()
|
||||
val Value.double get() = number.toDouble()
|
||||
val Value.float get() = number.toFloat()
|
||||
val Value.short get() = number.toShort()
|
||||
val Value.long get() = number.toLong()
|
||||
|
||||
val Value.stringList: List<String> get() = list.map { it.string }
|
||||
|
@ -13,14 +13,13 @@ class MetaDelegateTest {
|
||||
@Test
|
||||
fun delegateTest() {
|
||||
|
||||
class InnerSpec(override val config: Config) : Specific {
|
||||
class InnerSpec : Scheme() {
|
||||
var innerValue by string()
|
||||
}
|
||||
|
||||
val innerSpec = specification(::InnerSpec)
|
||||
val innerSpec = object : SchemeSpec<InnerSpec>(::InnerSpec){}
|
||||
|
||||
val testObject = object : Specific {
|
||||
override val config: Config = Config()
|
||||
val testObject = object : Scheme(Config()) {
|
||||
var myValue by string()
|
||||
var safeValue by double(2.2)
|
||||
var enumValue by enum(TestEnum.YES)
|
||||
|
@ -14,7 +14,7 @@ class MutableMetaTest{
|
||||
"b" put 22
|
||||
"c" put "StringValue"
|
||||
}
|
||||
}.toConfig()
|
||||
}.asConfig()
|
||||
|
||||
meta.remove("aNode.c")
|
||||
assertEquals(meta["aNode.c"], null)
|
||||
|
@ -4,19 +4,22 @@ import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
|
||||
class StyledTest{
|
||||
class SchemeTest{
|
||||
@Test
|
||||
fun testSNS(){
|
||||
val meta = buildMeta {
|
||||
fun testMetaScheme(){
|
||||
val styled = buildMeta {
|
||||
repeat(10){
|
||||
"b.a[$it]" put {
|
||||
"d" put it
|
||||
}
|
||||
}
|
||||
}.seal().withStyle()
|
||||
}.toScheme()
|
||||
|
||||
val meta = styled.toMeta()
|
||||
|
||||
assertEquals(10, meta.values().count())
|
||||
|
||||
val bNode = meta["b"].node
|
||||
val bNode = styled.getProperty("b").node
|
||||
|
||||
val aNodes = bNode?.getIndexed("a")
|
||||
|
@ -1,21 +1,26 @@
|
||||
package hep.dataforge.meta
|
||||
|
||||
import hep.dataforge.names.Name
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class SpecificationTest {
|
||||
class TestSpecific(override val config: Config) : Specific {
|
||||
class TestStyled(config: Config, defaultProvider: (Name) -> MetaItem<*>?) :
|
||||
Scheme(config, defaultProvider) {
|
||||
var list by numberList(1, 2, 3)
|
||||
|
||||
companion object : Specification<TestSpecific> {
|
||||
override fun wrap(config: Config): TestSpecific = TestSpecific(config)
|
||||
companion object : Specification<TestStyled> {
|
||||
override fun wrap(
|
||||
config: Config,
|
||||
defaultProvider: (Name) -> MetaItem<*>?
|
||||
): TestStyled = TestStyled(config, defaultProvider)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testSpecific() {
|
||||
val testObject = TestSpecific {
|
||||
val testObject = TestStyled {
|
||||
list = emptyList()
|
||||
}
|
||||
assertEquals(emptyList(), testObject.list)
|
||||
|
Loading…
Reference in New Issue
Block a user