Merge MetaConverter and MetaSpec

This commit is contained in:
Alexander Nozik 2023-12-31 12:54:38 +03:00
parent 282b43a05a
commit fd1d98aa87
16 changed files with 209 additions and 229 deletions

View File

@ -8,6 +8,7 @@
- Add Meta and MutableMeta delegates for convertable and serializeable - Add Meta and MutableMeta delegates for convertable and serializeable
### Changed ### Changed
- Descriptor `children` renamed to `nodes`
### Deprecated ### Deprecated
- `node(key,converter)` in favor of `serializable` delegate - `node(key,converter)` in favor of `serializable` delegate

View File

@ -1,11 +1,7 @@
package space.kscience.dataforge.properties package space.kscience.dataforge.properties
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.ObservableMutableMeta
import space.kscience.dataforge.meta.transformations.MetaConverter
import space.kscience.dataforge.meta.transformations.nullableMetaToObject
import space.kscience.dataforge.meta.transformations.nullableObjectToMeta
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.startsWith import space.kscience.dataforge.names.startsWith
@ -18,14 +14,14 @@ public class MetaProperty<T : Any>(
) : Property<T?> { ) : Property<T?> {
override var value: T? override var value: T?
get() = converter.nullableMetaToObject(meta[name]) get() = converter.readNullable(meta[name])
set(value) { set(value) {
meta[name] = converter.nullableObjectToMeta(value) ?: Meta.EMPTY meta[name] = converter.convertNullable(value) ?: Meta.EMPTY
} }
override fun onChange(owner: Any?, callback: (T?) -> Unit) { override fun onChange(owner: Any?, callback: (T?) -> Unit) {
meta.onChange(owner) { name -> meta.onChange(owner) { name ->
if (name.startsWith(this@MetaProperty.name)) callback(converter.nullableMetaToObject(this[name])) if (name.startsWith(this@MetaProperty.name)) callback(converter.readNullable(this[name]))
} }
} }

View File

@ -3,9 +3,11 @@ package space.kscience.dataforge.descriptors
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.Scheme
import space.kscience.dataforge.meta.SchemeSpec
import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.transformations.MetaConverter import space.kscience.dataforge.meta.int
import space.kscience.dataforge.meta.string
private class TestScheme: Scheme(){ private class TestScheme: Scheme(){

View File

@ -31,7 +31,7 @@ private fun Meta.toJsonWithIndex(descriptor: MetaDescriptor?, index: String?): J
val pairs: MutableList<Pair<String, JsonElement>> = items.entries.groupBy { val pairs: MutableList<Pair<String, JsonElement>> = items.entries.groupBy {
it.key.body it.key.body
}.mapTo(ArrayList()) { (body, list) -> }.mapTo(ArrayList()) { (body, list) ->
val childDescriptor = descriptor?.children?.get(body) val childDescriptor = descriptor?.nodes?.get(body)
if (list.size == 1) { if (list.size == 1) {
val (token, element) = list.first() val (token, element) = list.first()
//do not add an empty element //do not add an empty element

View File

@ -1,19 +1,19 @@
package space.kscience.dataforge.meta.transformations package space.kscience.dataforge.meta
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.encodeToJsonElement import kotlinx.serialization.json.encodeToJsonElement
import kotlinx.serialization.serializer import kotlinx.serialization.serializer
import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import kotlin.reflect.KType import kotlin.reflect.KType
import kotlin.reflect.typeOf import kotlin.reflect.typeOf
/** /**
* A converter of generic object to and from [Meta] * A converter of generic object to and from [Meta]
*/ */
public interface MetaConverter<T> { public interface MetaConverter<T>: MetaSpec<T> {
/** /**
* Runtime type of [T] * Runtime type of [T]
@ -23,32 +23,32 @@ public interface MetaConverter<T> {
/** /**
* A descriptor for resulting meta * A descriptor for resulting meta
*/ */
public val descriptor: MetaDescriptor get() = MetaDescriptor.EMPTY override val descriptor: MetaDescriptor get() = MetaDescriptor.EMPTY
/** /**
* Attempt conversion of [meta] to an object or return null if conversion failed * Attempt conversion of [source] to an object or return null if conversion failed
*/ */
public fun metaToObjectOrNull(meta: Meta): T? override fun readOrNull(source: Meta): T?
public fun metaToObject(meta: Meta): T = override fun read(source: Meta): T =
metaToObjectOrNull(meta) ?: error("Meta $meta could not be interpreted by $this") readOrNull(source) ?: error("Meta $source could not be interpreted by $this")
public fun objectToMeta(obj: T): Meta public fun convert(obj: T): Meta
public companion object { public companion object {
public val meta: MetaConverter<Meta> = object : MetaConverter<Meta> { public val meta: MetaConverter<Meta> = object : MetaConverter<Meta> {
override val type: KType = typeOf<Meta>() override val type: KType = typeOf<Meta>()
override fun metaToObjectOrNull(meta: Meta): Meta = meta override fun readOrNull(source: Meta): Meta = source
override fun objectToMeta(obj: Meta): Meta = obj override fun convert(obj: Meta): Meta = obj
} }
public val value: MetaConverter<Value> = object : MetaConverter<Value> { public val value: MetaConverter<Value> = object : MetaConverter<Value> {
override val type: KType = typeOf<Value>() override val type: KType = typeOf<Value>()
override fun metaToObjectOrNull(meta: Meta): Value? = meta.value override fun readOrNull(source: Meta): Value? = source.value
override fun objectToMeta(obj: Value): Meta = Meta(obj) override fun convert(obj: Value): Meta = Meta(obj)
} }
public val string: MetaConverter<String> = object : MetaConverter<String> { public val string: MetaConverter<String> = object : MetaConverter<String> {
@ -59,8 +59,8 @@ public interface MetaConverter<T> {
} }
override fun metaToObjectOrNull(meta: Meta): String? = meta.string override fun readOrNull(source: Meta): String? = source.string
override fun objectToMeta(obj: String): Meta = Meta(obj.asValue()) override fun convert(obj: String): Meta = Meta(obj.asValue())
} }
public val boolean: MetaConverter<Boolean> = object : MetaConverter<Boolean> { public val boolean: MetaConverter<Boolean> = object : MetaConverter<Boolean> {
@ -70,8 +70,8 @@ public interface MetaConverter<T> {
valueType(ValueType.BOOLEAN) valueType(ValueType.BOOLEAN)
} }
override fun metaToObjectOrNull(meta: Meta): Boolean? = meta.boolean override fun readOrNull(source: Meta): Boolean? = source.boolean
override fun objectToMeta(obj: Boolean): Meta = Meta(obj.asValue()) override fun convert(obj: Boolean): Meta = Meta(obj.asValue())
} }
public val number: MetaConverter<Number> = object : MetaConverter<Number> { public val number: MetaConverter<Number> = object : MetaConverter<Number> {
@ -81,8 +81,8 @@ public interface MetaConverter<T> {
valueType(ValueType.NUMBER) valueType(ValueType.NUMBER)
} }
override fun metaToObjectOrNull(meta: Meta): Number? = meta.number override fun readOrNull(source: Meta): Number? = source.number
override fun objectToMeta(obj: Number): Meta = Meta(obj.asValue()) override fun convert(obj: Number): Meta = Meta(obj.asValue())
} }
public val double: MetaConverter<Double> = object : MetaConverter<Double> { public val double: MetaConverter<Double> = object : MetaConverter<Double> {
@ -92,8 +92,8 @@ public interface MetaConverter<T> {
valueType(ValueType.NUMBER) valueType(ValueType.NUMBER)
} }
override fun metaToObjectOrNull(meta: Meta): Double? = meta.double override fun readOrNull(source: Meta): Double? = source.double
override fun objectToMeta(obj: Double): Meta = Meta(obj.asValue()) override fun convert(obj: Double): Meta = Meta(obj.asValue())
} }
public val float: MetaConverter<Float> = object : MetaConverter<Float> { public val float: MetaConverter<Float> = object : MetaConverter<Float> {
@ -103,8 +103,8 @@ public interface MetaConverter<T> {
valueType(ValueType.NUMBER) valueType(ValueType.NUMBER)
} }
override fun metaToObjectOrNull(meta: Meta): Float? = meta.float override fun readOrNull(source: Meta): Float? = source.float
override fun objectToMeta(obj: Float): Meta = Meta(obj.asValue()) override fun convert(obj: Float): Meta = Meta(obj.asValue())
} }
public val int: MetaConverter<Int> = object : MetaConverter<Int> { public val int: MetaConverter<Int> = object : MetaConverter<Int> {
@ -114,8 +114,8 @@ public interface MetaConverter<T> {
valueType(ValueType.NUMBER) valueType(ValueType.NUMBER)
} }
override fun metaToObjectOrNull(meta: Meta): Int? = meta.int override fun readOrNull(source: Meta): Int? = source.int
override fun objectToMeta(obj: Int): Meta = Meta(obj.asValue()) override fun convert(obj: Int): Meta = Meta(obj.asValue())
} }
public val long: MetaConverter<Long> = object : MetaConverter<Long> { public val long: MetaConverter<Long> = object : MetaConverter<Long> {
@ -125,8 +125,8 @@ public interface MetaConverter<T> {
valueType(ValueType.NUMBER) valueType(ValueType.NUMBER)
} }
override fun metaToObjectOrNull(meta: Meta): Long? = meta.long override fun readOrNull(source: Meta): Long? = source.long
override fun objectToMeta(obj: Long): Meta = Meta(obj.asValue()) override fun convert(obj: Long): Meta = Meta(obj.asValue())
} }
public inline fun <reified E : Enum<E>> enum(): MetaConverter<E> = object : MetaConverter<E> { public inline fun <reified E : Enum<E>> enum(): MetaConverter<E> = object : MetaConverter<E> {
@ -138,9 +138,9 @@ public interface MetaConverter<T> {
} }
@Suppress("USELESS_CAST") @Suppress("USELESS_CAST")
override fun metaToObjectOrNull(meta: Meta): E = meta.enum<E>() as? E ?: error("The Item is not a Enum") override fun readOrNull(source: Meta): E = source.enum<E>() as? E ?: error("The Item is not a Enum")
override fun objectToMeta(obj: E): Meta = Meta(obj.asValue()) override fun convert(obj: E): Meta = Meta(obj.asValue())
} }
public fun <T> valueList( public fun <T> valueList(
@ -153,9 +153,9 @@ public interface MetaConverter<T> {
valueType(ValueType.LIST) valueType(ValueType.LIST)
} }
override fun metaToObjectOrNull(meta: Meta): List<T>? = meta.value?.list?.map(reader) override fun readOrNull(source: Meta): List<T>? = source.value?.list?.map(reader)
override fun objectToMeta(obj: List<T>): Meta = Meta(obj.map(writer).asValue()) override fun convert(obj: List<T>): Meta = Meta(obj.map(writer).asValue())
} }
/** /**
@ -168,12 +168,12 @@ public interface MetaConverter<T> {
override val type: KType = typeOf<T>() override val type: KType = typeOf<T>()
private val serializer: KSerializer<T> = serializer() private val serializer: KSerializer<T> = serializer()
override fun metaToObjectOrNull(meta: Meta): T? { override fun readOrNull(source: Meta): T? {
val json = meta.toJson(descriptor) val json = source.toJson(descriptor)
return Json.decodeFromJsonElement(serializer, json) return Json.decodeFromJsonElement(serializer, json)
} }
override fun objectToMeta(obj: T): Meta { override fun convert(obj: T): Meta {
val json = Json.encodeToJsonElement(obj) val json = Json.encodeToJsonElement(obj)
return json.toMeta(descriptor) return json.toMeta(descriptor)
} }
@ -183,7 +183,7 @@ public interface MetaConverter<T> {
} }
} }
public fun <T : Any> MetaConverter<T>.nullableMetaToObject(item: Meta?): T? = item?.let { metaToObject(it) } public fun <T : Any> MetaConverter<T>.readNullable(item: Meta?): T? = item?.let { read(it) }
public fun <T : Any> MetaConverter<T>.nullableObjectToMeta(obj: T?): Meta? = obj?.let { objectToMeta(it) } public fun <T : Any> MetaConverter<T>.convertNullable(obj: T?): Meta? = obj?.let { convert(it) }
public fun <T> MetaConverter<T>.valueToObject(value: Value): T? = metaToObject(Meta(value)) public fun <T> MetaConverter<T>.readValue(value: Value): T? = read(Meta(value))

View File

@ -1,7 +1,6 @@
package space.kscience.dataforge.meta package space.kscience.dataforge.meta
import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.transformations.MetaConverter
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
@ -20,7 +19,7 @@ public fun <T> MetaProvider.convertable(
converter: MetaConverter<T>, converter: MetaConverter<T>,
key: Name? = null, key: Name? = null,
): ReadOnlyProperty<Any?, T?> = ReadOnlyProperty { _, property -> ): ReadOnlyProperty<Any?, T?> = ReadOnlyProperty { _, property ->
get(key ?: property.name.asName())?.let { converter.metaToObject(it) } get(key ?: property.name.asName())?.let { converter.read(it) }
} }
/** /**
@ -46,7 +45,7 @@ public fun <T> Meta.listOfConvertable(
key: Name? = null, key: Name? = null,
): ReadOnlyProperty<Any?, List<T>> = ReadOnlyProperty{_, property -> ): ReadOnlyProperty<Any?, List<T>> = ReadOnlyProperty{_, property ->
val name = key ?: property.name.asName() val name = key ?: property.name.asName()
getIndexed(name).values.map { converter.metaToObject(it) } getIndexed(name).values.map { converter.read(it) }
} }
@DFExperimental @DFExperimental

View File

@ -0,0 +1,18 @@
package space.kscience.dataforge.meta
import space.kscience.dataforge.meta.descriptors.Described
public interface MetaSpec<out T> : Described {
/**
* Read the source meta into an object and return null if Meta could not be interpreted as a target type
*/
public fun readOrNull(source: Meta): T?
/**
* Read generic read-only meta with this [MetaSpec] producing instance of the desired type.
* Throws an error if conversion could not be done.
*/
public fun read(source: Meta): T = readOrNull(source) ?: error("Meta $source could not be interpreted by $this")
}

View File

@ -1,6 +1,5 @@
package space.kscience.dataforge.meta.transformations package space.kscience.dataforge.meta
import space.kscience.dataforge.meta.*
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import kotlin.jvm.JvmInline import kotlin.jvm.JvmInline

View File

@ -1,7 +1,6 @@
package space.kscience.dataforge.meta package space.kscience.dataforge.meta
import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.transformations.MetaConverter
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
@ -33,12 +32,12 @@ public fun <T> MutableMetaProvider.convertable(
object : ReadWriteProperty<Any?, T?> { object : ReadWriteProperty<Any?, T?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T? { override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
val name = key ?: property.name.asName() val name = key ?: property.name.asName()
return get(name)?.let { converter.metaToObject(it) } return get(name)?.let { converter.read(it) }
} }
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) { override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
val name = key ?: property.name.asName() val name = key ?: property.name.asName()
set(name, value?.let { converter.objectToMeta(it) }) set(name, value?.let { converter.convert(it) })
} }
} }
@ -66,12 +65,12 @@ public fun <T> MutableMeta.listOfConvertable(
): ReadWriteProperty<Any?, List<T>> = object : ReadWriteProperty<Any?, List<T>> { ): ReadWriteProperty<Any?, List<T>> = object : ReadWriteProperty<Any?, List<T>> {
override fun getValue(thisRef: Any?, property: KProperty<*>): List<T> { override fun getValue(thisRef: Any?, property: KProperty<*>): List<T> {
val name = key ?: property.name.asName() val name = key ?: property.name.asName()
return getIndexed(name).values.map { converter.metaToObject(it) } return getIndexed(name).values.map { converter.read(it) }
} }
override fun setValue(thisRef: Any?, property: KProperty<*>, value: List<T>) { override fun setValue(thisRef: Any?, property: KProperty<*>, value: List<T>) {
val name = key ?: property.name.asName() val name = key ?: property.name.asName()
setIndexed(name, value.map { converter.objectToMeta(it) }) setIndexed(name, value.map { converter.convert(it) })
} }
} }

View File

@ -7,9 +7,11 @@ import space.kscience.dataforge.meta.descriptors.validate
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.misc.ThreadSafe import space.kscience.dataforge.misc.ThreadSafe
import space.kscience.dataforge.names.* import space.kscience.dataforge.names.*
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
/** /**
* A base for delegate-based or descriptor-based scheme. [Scheme] has an empty constructor to simplify usage from [Specification]. * A base for delegate-based or descriptor-based scheme. [Scheme] has an empty constructor to simplify usage from [MetaSpec].
* Default item provider and [MetaDescriptor] are optional * Default item provider and [MetaDescriptor] are optional
*/ */
public open class Scheme : Described, MetaRepr, MutableMetaProvider, Configurable { public open class Scheme : Described, MetaRepr, MutableMetaProvider, Configurable {
@ -165,23 +167,124 @@ public inline fun <T : Scheme> T.copy(spec: SchemeSpec<T>, block: T.() -> Unit =
*/ */
public open class SchemeSpec<out T : Scheme>( public open class SchemeSpec<out T : Scheme>(
private val builder: () -> T, private val builder: () -> T,
) : Specification<T> { ) : MetaSpec<T> {
override val descriptor: MetaDescriptor? get() = null override val descriptor: MetaDescriptor? get() = null
override fun read(source: Meta): T = builder().also { override fun readOrNull(source: Meta): T = builder().also {
it.initialize(MutableMeta(), source, descriptor) it.initialize(MutableMeta(), source, descriptor)
} }
override fun write(target: MutableMeta): T = empty().also { public fun write(target: MutableMeta): T = empty().also {
it.initialize(target, Meta.EMPTY, descriptor) it.initialize(target, Meta.EMPTY, descriptor)
} }
override fun empty(): T = builder().also { /**
* Generate an empty object
*/
public fun empty(): T = builder().also {
it.initialize(MutableMeta(), Meta.EMPTY, descriptor) it.initialize(MutableMeta(), Meta.EMPTY, descriptor)
} }
@Suppress("OVERRIDE_BY_INLINE") /**
final override inline operator fun invoke(action: T.() -> Unit): T = empty().apply(action) * A convenience method to use specifications in builders
*/
public inline operator fun invoke(action: T.() -> Unit): T = empty().apply(action)
} }
/**
* Update a [MutableMeta] using given specification
*/
public fun <T : Scheme> MutableMeta.updateWith(
spec: SchemeSpec<T>,
action: T.() -> Unit,
): T = spec.write(this).apply(action)
/**
* Update configuration using given specification
*/
public fun <T : Scheme> Configurable.updateWith(
spec: SchemeSpec<T>,
action: T.() -> Unit,
): T = spec.write(meta).apply(action)
/**
* A delegate that uses a [MetaSpec] to wrap a child of this provider
*/
public fun <T : Scheme> MutableMeta.scheme(
spec: SchemeSpec<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 spec.write(getOrCreate(name))
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
val name = key ?: property.name.asName()
set(name, value.toMeta())
}
}
public fun <T : Scheme> Scheme.scheme(
spec: SchemeSpec<T>,
key: Name? = null,
): ReadWriteProperty<Any?, T> = meta.scheme(spec, key)
/**
* A delegate that uses a [MetaSpec] to wrap a child of this provider.
* Returns null if meta with given name does not exist.
*/
public fun <T : Scheme> MutableMeta.schemeOrNull(
spec: SchemeSpec<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 if (get(name) == null) null else spec.write(getOrCreate(name))
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
val name = key ?: property.name.asName()
if (value == null) remove(name)
else set(name, value.toMeta())
}
}
public fun <T : Scheme> Scheme.schemeOrNull(
spec: SchemeSpec<T>,
key: Name? = null,
): ReadWriteProperty<Any?, T?> = meta.schemeOrNull(spec, key)
/**
* A delegate that uses a [MetaSpec] to wrap a list of child providers.
* If children are mutable, the changes in list elements are reflected on them.
* The list is a snapshot of children state, so change in structure is not reflected on its composition.
*/
@DFExperimental
public fun <T : Scheme> MutableMeta.listOfScheme(
spec: SchemeSpec<T>,
key: Name? = null,
): ReadWriteProperty<Any?, List<T>> = object : ReadWriteProperty<Any?, List<T>> {
override fun getValue(thisRef: Any?, property: KProperty<*>): List<T> {
val name = key ?: property.name.asName()
return getIndexed(name).values.map { spec.write(it as MutableMeta) }
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: List<T>) {
val name = key ?: property.name.asName()
setIndexed(name, value.map { it.toMeta() })
}
}
@DFExperimental
public fun <T : Scheme> Scheme.listOfScheme(
spec: SchemeSpec<T>,
key: Name? = null,
): ReadWriteProperty<Any?, List<T>> = meta.listOfScheme(spec, key)

View File

@ -1,137 +0,0 @@
package space.kscience.dataforge.meta
import space.kscience.dataforge.meta.descriptors.Described
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
public interface ReadOnlySpecification<out T : Any> : Described {
/**
* Read generic read-only meta with this [Specification] producing instance of desired type.
* The source is not mutated even if it is in theory mutable
*/
public fun read(source: Meta): T
/**
* Generate an empty object
*/
public fun empty(): T
/**
* A convenience method to use specifications in builders
*/
public operator fun invoke(action: T.() -> Unit): T = empty().apply(action)
}
/**
* Allows to apply custom configuration in a type safe way to simple untyped configuration.
* By convention [Scheme] companion should inherit this class
*
*/
public interface Specification<out T : Any> : ReadOnlySpecification<T> {
/**
* Wrap [MutableMeta], using it as inner storage (changes to [Specification] are reflected on [MutableMeta]
*/
public fun write(target: MutableMeta): T
}
/**
* Update a [MutableMeta] using given specification
*/
public fun <T : Any> MutableMeta.updateWith(
spec: Specification<T>,
action: T.() -> Unit,
): T = spec.write(this).apply(action)
/**
* Update configuration using given specification
*/
public fun <T : Any> Configurable.updateWith(
spec: Specification<T>,
action: T.() -> Unit,
): T = spec.write(meta).apply(action)
//
//public fun <M : MutableTypedMeta<M>> MutableMeta.withSpec(spec: Specification<M>): M? =
// spec.write(it)
/**
* A delegate that uses a [Specification] to wrap a child of this provider
*/
public fun <T : Scheme> MutableMeta.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 spec.write(getOrCreate(name))
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
val name = key ?: property.name.asName()
set(name, value.toMeta())
}
}
public fun <T : Scheme> Scheme.spec(
spec: Specification<T>,
key: Name? = null,
): ReadWriteProperty<Any?, T> = meta.spec(spec, key)
/**
* A delegate that uses a [Specification] to wrap a child of this provider.
* Returns null if meta with given name does not exist.
*/
public fun <T : Scheme> MutableMeta.specOrNull(
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 if (get(name) == null) null else spec.write(getOrCreate(name))
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
val name = key ?: property.name.asName()
if (value == null) remove(name)
else set(name, value.toMeta())
}
}
public fun <T : Scheme> Scheme.specOrNull(
spec: Specification<T>,
key: Name? = null,
): ReadWriteProperty<Any?, T?> = meta.specOrNull(spec, key)
/**
* A delegate that uses a [Specification] to wrap a list of child providers.
* If children are mutable, the changes in list elements are reflected on them.
* The list is a snapshot of children state, so change in structure is not reflected on its composition.
*/
@DFExperimental
public fun <T : Scheme> MutableMeta.listOfSpec(
spec: Specification<T>,
key: Name? = null,
): ReadWriteProperty<Any?, List<T>> = object : ReadWriteProperty<Any?, List<T>> {
override fun getValue(thisRef: Any?, property: KProperty<*>): List<T> {
val name = key ?: property.name.asName()
return getIndexed(name).values.map { spec.write(it as MutableMeta) }
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: List<T>) {
val name = key ?: property.name.asName()
setIndexed(name, value.map { it.toMeta() })
}
}
@DFExperimental
public fun <T : Scheme> Scheme.listOfSpec(
spec: Specification<T>,
key: Name? = null,
): ReadWriteProperty<Any?, List<T>> = meta.listOfSpec(spec, key)

View File

@ -27,7 +27,7 @@ public enum class ValueRestriction {
/** /**
* The descriptor for a meta * The descriptor for a meta
* @param description description text * @param description description text
* @param children child descriptors for this node * @param nodes child descriptors for this node
* @param multiple True if same name siblings with this name are allowed * @param multiple True if same name siblings with this name are allowed
* @param valueRestriction The requirements for node content * @param valueRestriction The requirements for node content
* @param valueTypes list of allowed types for [Meta.value], null if all values are allowed. * @param valueTypes list of allowed types for [Meta.value], null if all values are allowed.
@ -39,7 +39,7 @@ public enum class ValueRestriction {
@Serializable @Serializable
public data class MetaDescriptor( public data class MetaDescriptor(
public val description: String? = null, public val description: String? = null,
public val children: Map<String, MetaDescriptor> = emptyMap(), public val nodes: Map<String, MetaDescriptor> = emptyMap(),
public val multiple: Boolean = false, public val multiple: Boolean = false,
public val valueRestriction: ValueRestriction = ValueRestriction.NONE, public val valueRestriction: ValueRestriction = ValueRestriction.NONE,
public val valueTypes: List<ValueType>? = null, public val valueTypes: List<ValueType>? = null,
@ -47,6 +47,9 @@ public data class MetaDescriptor(
public val defaultValue: Value? = null, public val defaultValue: Value? = null,
public val attributes: Meta = Meta.EMPTY, public val attributes: Meta = Meta.EMPTY,
) { ) {
@Deprecated("Replace by nodes", ReplaceWith("nodes"))
public val children: Map<String, MetaDescriptor> get() = nodes
/** /**
* A node constructed of default values for this descriptor and its children * A node constructed of default values for this descriptor and its children
*/ */
@ -55,7 +58,7 @@ public data class MetaDescriptor(
defaultValue?.let { defaultValue -> defaultValue?.let { defaultValue ->
this.value = defaultValue this.value = defaultValue
} }
children.forEach { (key, descriptor) -> nodes.forEach { (key, descriptor) ->
set(key, descriptor.defaultNode) set(key, descriptor.defaultNode)
} }
} }
@ -67,13 +70,13 @@ public data class MetaDescriptor(
} }
} }
public val MetaDescriptor.required: Boolean get() = valueRestriction == ValueRestriction.REQUIRED || children.values.any { required } public val MetaDescriptor.required: Boolean get() = valueRestriction == ValueRestriction.REQUIRED || nodes.values.any { required }
public val MetaDescriptor.allowedValues: List<Value>? get() = attributes[MetaDescriptor.ALLOWED_VALUES_KEY]?.value?.list public val MetaDescriptor.allowedValues: List<Value>? get() = attributes[MetaDescriptor.ALLOWED_VALUES_KEY]?.value?.list
public operator fun MetaDescriptor.get(name: Name): MetaDescriptor? = when (name.length) { public operator fun MetaDescriptor.get(name: Name): MetaDescriptor? = when (name.length) {
0 -> this 0 -> this
1 -> children[name.firstOrNull()!!.toString()] 1 -> nodes[name.firstOrNull()!!.toString()]
else -> get(name.firstOrNull()!!.asName())?.get(name.cutFirst()) else -> get(name.firstOrNull()!!.asName())?.get(name.cutFirst())
} }
@ -95,7 +98,7 @@ public fun MetaDescriptor.validate(item: Meta?): Boolean {
if (item == null) return !required if (item == null) return !required
if (!validate(item.value)) return false if (!validate(item.value)) return false
children.forEach { (key, childDescriptor) -> nodes.forEach { (key, childDescriptor) ->
if (!childDescriptor.validate(item[key])) return false if (!childDescriptor.validate(item[key])) return false
} }
return true return true

View File

@ -80,7 +80,7 @@ public class MetaDescriptorBuilder @PublishedApi internal constructor() {
public fun from(descriptor: MetaDescriptor) { public fun from(descriptor: MetaDescriptor) {
description = descriptor.description description = descriptor.description
children.putAll(descriptor.children.mapValues { it.value.toBuilder() }) children.putAll(descriptor.nodes.mapValues { it.value.toBuilder() })
multiple = descriptor.multiple multiple = descriptor.multiple
valueRestriction = descriptor.valueRestriction valueRestriction = descriptor.valueRestriction
valueTypes = descriptor.valueTypes valueTypes = descriptor.valueTypes
@ -92,7 +92,7 @@ public class MetaDescriptorBuilder @PublishedApi internal constructor() {
@PublishedApi @PublishedApi
internal fun build(): MetaDescriptor = MetaDescriptor( internal fun build(): MetaDescriptor = MetaDescriptor(
description = description, description = description,
children = children.mapValues { it.value.build() }, nodes = children.mapValues { it.value.build() },
multiple = multiple, multiple = multiple,
valueRestriction = valueRestriction, valueRestriction = valueRestriction,
valueTypes = valueTypes, valueTypes = valueTypes,
@ -143,7 +143,7 @@ public fun MetaDescriptorBuilder.required() {
private fun MetaDescriptor.toBuilder(): MetaDescriptorBuilder = MetaDescriptorBuilder().apply { private fun MetaDescriptor.toBuilder(): MetaDescriptorBuilder = MetaDescriptorBuilder().apply {
description = this@toBuilder.description description = this@toBuilder.description
children = this@toBuilder.children.mapValuesTo(LinkedHashMap()) { it.value.toBuilder() } children = this@toBuilder.nodes.mapValuesTo(LinkedHashMap()) { it.value.toBuilder() }
multiple = this@toBuilder.multiple multiple = this@toBuilder.multiple
valueRestriction = this@toBuilder.valueRestriction valueRestriction = this@toBuilder.valueRestriction
valueTypes = this@toBuilder.valueTypes valueTypes = this@toBuilder.valueTypes

View File

@ -20,7 +20,7 @@ class MetaDelegateTest {
var myValue by string() var myValue by string()
var safeValue by double(2.2) var safeValue by double(2.2)
var enumValue by enum(TestEnum.YES) var enumValue by enum(TestEnum.YES)
var inner by spec(InnerScheme) var inner by scheme(InnerScheme)
companion object : SchemeSpec<TestScheme>(::TestScheme) companion object : SchemeSpec<TestScheme>(::TestScheme)
} }

View File

@ -6,7 +6,7 @@ import space.kscience.dataforge.data.DataTree
import space.kscience.dataforge.data.GoalExecutionRestriction import space.kscience.dataforge.data.GoalExecutionRestriction
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MetaRepr import space.kscience.dataforge.meta.MetaRepr
import space.kscience.dataforge.meta.Specification import space.kscience.dataforge.meta.MetaSpec
import space.kscience.dataforge.meta.descriptors.Described import space.kscience.dataforge.meta.descriptors.Described
import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.misc.DfType import space.kscience.dataforge.misc.DfType
@ -43,10 +43,10 @@ public interface Task<out T : Any> : Described {
} }
/** /**
* A [Task] with [Specification] for wrapping and unwrapping task configuration * A [Task] with [MetaSpec] for wrapping and unwrapping task configuration
*/ */
public interface TaskWithSpec<out T : Any, C : Any> : Task<T> { public interface TaskWithSpec<out T : Any, C : Any> : Task<T> {
public val spec: Specification<C> public val spec: MetaSpec<C>
override val descriptor: MetaDescriptor? get() = spec.descriptor override val descriptor: MetaDescriptor? get() = spec.descriptor
public suspend fun execute(workspace: Workspace, taskName: Name, configuration: C): TaskResult<T> public suspend fun execute(workspace: Workspace, taskName: Name, configuration: C): TaskResult<T>
@ -55,11 +55,11 @@ public interface TaskWithSpec<out T : Any, C : Any> : Task<T> {
execute(workspace, taskName, spec.read(taskMeta)) execute(workspace, taskName, spec.read(taskMeta))
} }
public suspend fun <T : Any, C : Any> TaskWithSpec<T, C>.execute( //public suspend fun <T : Any, C : Scheme> TaskWithSpec<T, C>.execute(
workspace: Workspace, // workspace: Workspace,
taskName: Name, // taskName: Name,
block: C.() -> Unit = {}, // block: C.() -> Unit = {},
): TaskResult<T> = execute(workspace, taskName, spec(block)) //): TaskResult<T> = execute(workspace, taskName, spec(block))
public class TaskResultBuilder<in T : Any>( public class TaskResultBuilder<in T : Any>(
public val workspace: Workspace, public val workspace: Workspace,
@ -76,7 +76,6 @@ public class TaskResultBuilder<in T : Any>(
* @param descriptor of meta accepted by this task * @param descriptor of meta accepted by this task
* @param builder for resulting data set * @param builder for resulting data set
*/ */
@Suppress("FunctionName")
public fun <T : Any> Task( public fun <T : Any> Task(
resultType: KType, resultType: KType,
descriptor: MetaDescriptor? = null, descriptor: MetaDescriptor? = null,
@ -98,7 +97,6 @@ public fun <T : Any> Task(
} }
} }
@Suppress("FunctionName")
public inline fun <reified T : Any> Task( public inline fun <reified T : Any> Task(
descriptor: MetaDescriptor? = null, descriptor: MetaDescriptor? = null,
noinline builder: suspend TaskResultBuilder<T>.() -> Unit, noinline builder: suspend TaskResultBuilder<T>.() -> Unit,
@ -116,10 +114,10 @@ public inline fun <reified T : Any> Task(
@Suppress("FunctionName") @Suppress("FunctionName")
public fun <T : Any, C : MetaRepr> Task( public fun <T : Any, C : MetaRepr> Task(
resultType: KType, resultType: KType,
specification: Specification<C>, specification: MetaSpec<C>,
builder: suspend TaskResultBuilder<T>.(C) -> Unit, builder: suspend TaskResultBuilder<T>.(C) -> Unit,
): TaskWithSpec<T, C> = object : TaskWithSpec<T, C> { ): TaskWithSpec<T, C> = object : TaskWithSpec<T, C> {
override val spec: Specification<C> = specification override val spec: MetaSpec<C> = specification
override suspend fun execute( override suspend fun execute(
workspace: Workspace, workspace: Workspace,
@ -135,8 +133,7 @@ public fun <T : Any, C : MetaRepr> Task(
} }
} }
@Suppress("FunctionName")
public inline fun <reified T : Any, C : MetaRepr> Task( public inline fun <reified T : Any, C : MetaRepr> Task(
specification: Specification<C>, specification: MetaSpec<C>,
noinline builder: suspend TaskResultBuilder<T>.(C) -> Unit, noinline builder: suspend TaskResultBuilder<T>.(C) -> Unit,
): Task<T> = Task(typeOf<T>(), specification, builder) ): Task<T> = Task(typeOf<T>(), specification, builder)

View File

@ -7,8 +7,8 @@ import space.kscience.dataforge.context.Global
import space.kscience.dataforge.data.* import space.kscience.dataforge.data.*
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MetaRepr import space.kscience.dataforge.meta.MetaRepr
import space.kscience.dataforge.meta.MetaSpec
import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.meta.Specification
import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.MetaDescriptorBuilder import space.kscience.dataforge.meta.descriptors.MetaDescriptorBuilder
import space.kscience.dataforge.misc.DFBuilder import space.kscience.dataforge.misc.DFBuilder
@ -68,7 +68,7 @@ public inline fun <reified T : Any> TaskContainer.task(
} }
public inline fun <reified T : Any, C : MetaRepr> TaskContainer.task( public inline fun <reified T : Any, C : MetaRepr> TaskContainer.task(
specification: Specification<C>, specification: MetaSpec<C>,
noinline builder: suspend TaskResultBuilder<T>.(C) -> Unit, noinline builder: suspend TaskResultBuilder<T>.(C) -> Unit,
): PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, TaskReference<T>>> = PropertyDelegateProvider { _, property -> ): PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, TaskReference<T>>> = PropertyDelegateProvider { _, property ->
val taskName = Name.parse(property.name) val taskName = Name.parse(property.name)