Merge MetaConverter and MetaSpec
This commit is contained in:
parent
282b43a05a
commit
fd1d98aa87
@ -8,6 +8,7 @@
|
||||
- Add Meta and MutableMeta delegates for convertable and serializeable
|
||||
|
||||
### Changed
|
||||
- Descriptor `children` renamed to `nodes`
|
||||
|
||||
### Deprecated
|
||||
- `node(key,converter)` in favor of `serializable` delegate
|
||||
|
@ -1,11 +1,7 @@
|
||||
package space.kscience.dataforge.properties
|
||||
|
||||
|
||||
import space.kscience.dataforge.meta.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.meta.*
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.startsWith
|
||||
@ -18,14 +14,14 @@ public class MetaProperty<T : Any>(
|
||||
) : Property<T?> {
|
||||
|
||||
override var value: T?
|
||||
get() = converter.nullableMetaToObject(meta[name])
|
||||
get() = converter.readNullable(meta[name])
|
||||
set(value) {
|
||||
meta[name] = converter.nullableObjectToMeta(value) ?: Meta.EMPTY
|
||||
meta[name] = converter.convertNullable(value) ?: Meta.EMPTY
|
||||
}
|
||||
|
||||
override fun onChange(owner: Any?, callback: (T?) -> Unit) {
|
||||
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]))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,9 +3,11 @@ package space.kscience.dataforge.descriptors
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
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.transformations.MetaConverter
|
||||
import space.kscience.dataforge.meta.int
|
||||
import space.kscience.dataforge.meta.string
|
||||
|
||||
private class TestScheme: Scheme(){
|
||||
|
||||
|
@ -31,7 +31,7 @@ private fun Meta.toJsonWithIndex(descriptor: MetaDescriptor?, index: String?): J
|
||||
val pairs: MutableList<Pair<String, JsonElement>> = items.entries.groupBy {
|
||||
it.key.body
|
||||
}.mapTo(ArrayList()) { (body, list) ->
|
||||
val childDescriptor = descriptor?.children?.get(body)
|
||||
val childDescriptor = descriptor?.nodes?.get(body)
|
||||
if (list.size == 1) {
|
||||
val (token, element) = list.first()
|
||||
//do not add an empty element
|
||||
|
@ -1,19 +1,19 @@
|
||||
package space.kscience.dataforge.meta.transformations
|
||||
package space.kscience.dataforge.meta
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.encodeToJsonElement
|
||||
import kotlinx.serialization.serializer
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
|
||||
/**
|
||||
* A converter of generic object to and from [Meta]
|
||||
*/
|
||||
public interface MetaConverter<T> {
|
||||
public interface MetaConverter<T>: MetaSpec<T> {
|
||||
|
||||
/**
|
||||
* Runtime type of [T]
|
||||
@ -23,32 +23,32 @@ public interface MetaConverter<T> {
|
||||
/**
|
||||
* 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 =
|
||||
metaToObjectOrNull(meta) ?: error("Meta $meta could not be interpreted by $this")
|
||||
override fun read(source: Meta): T =
|
||||
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 val meta: MetaConverter<Meta> = object : MetaConverter<Meta> {
|
||||
override val type: KType = typeOf<Meta>()
|
||||
|
||||
override fun metaToObjectOrNull(meta: Meta): Meta = meta
|
||||
override fun objectToMeta(obj: Meta): Meta = obj
|
||||
override fun readOrNull(source: Meta): Meta = source
|
||||
override fun convert(obj: Meta): Meta = obj
|
||||
}
|
||||
|
||||
public val value: MetaConverter<Value> = object : MetaConverter<Value> {
|
||||
override val type: KType = typeOf<Value>()
|
||||
|
||||
override fun metaToObjectOrNull(meta: Meta): Value? = meta.value
|
||||
override fun objectToMeta(obj: Value): Meta = Meta(obj)
|
||||
override fun readOrNull(source: Meta): Value? = source.value
|
||||
override fun convert(obj: Value): Meta = Meta(obj)
|
||||
}
|
||||
|
||||
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 objectToMeta(obj: String): Meta = Meta(obj.asValue())
|
||||
override fun readOrNull(source: Meta): String? = source.string
|
||||
override fun convert(obj: String): Meta = Meta(obj.asValue())
|
||||
}
|
||||
|
||||
public val boolean: MetaConverter<Boolean> = object : MetaConverter<Boolean> {
|
||||
@ -70,8 +70,8 @@ public interface MetaConverter<T> {
|
||||
valueType(ValueType.BOOLEAN)
|
||||
}
|
||||
|
||||
override fun metaToObjectOrNull(meta: Meta): Boolean? = meta.boolean
|
||||
override fun objectToMeta(obj: Boolean): Meta = Meta(obj.asValue())
|
||||
override fun readOrNull(source: Meta): Boolean? = source.boolean
|
||||
override fun convert(obj: Boolean): Meta = Meta(obj.asValue())
|
||||
}
|
||||
|
||||
public val number: MetaConverter<Number> = object : MetaConverter<Number> {
|
||||
@ -81,8 +81,8 @@ public interface MetaConverter<T> {
|
||||
valueType(ValueType.NUMBER)
|
||||
}
|
||||
|
||||
override fun metaToObjectOrNull(meta: Meta): Number? = meta.number
|
||||
override fun objectToMeta(obj: Number): Meta = Meta(obj.asValue())
|
||||
override fun readOrNull(source: Meta): Number? = source.number
|
||||
override fun convert(obj: Number): Meta = Meta(obj.asValue())
|
||||
}
|
||||
|
||||
public val double: MetaConverter<Double> = object : MetaConverter<Double> {
|
||||
@ -92,8 +92,8 @@ public interface MetaConverter<T> {
|
||||
valueType(ValueType.NUMBER)
|
||||
}
|
||||
|
||||
override fun metaToObjectOrNull(meta: Meta): Double? = meta.double
|
||||
override fun objectToMeta(obj: Double): Meta = Meta(obj.asValue())
|
||||
override fun readOrNull(source: Meta): Double? = source.double
|
||||
override fun convert(obj: Double): Meta = Meta(obj.asValue())
|
||||
}
|
||||
|
||||
public val float: MetaConverter<Float> = object : MetaConverter<Float> {
|
||||
@ -103,8 +103,8 @@ public interface MetaConverter<T> {
|
||||
valueType(ValueType.NUMBER)
|
||||
}
|
||||
|
||||
override fun metaToObjectOrNull(meta: Meta): Float? = meta.float
|
||||
override fun objectToMeta(obj: Float): Meta = Meta(obj.asValue())
|
||||
override fun readOrNull(source: Meta): Float? = source.float
|
||||
override fun convert(obj: Float): Meta = Meta(obj.asValue())
|
||||
}
|
||||
|
||||
public val int: MetaConverter<Int> = object : MetaConverter<Int> {
|
||||
@ -114,8 +114,8 @@ public interface MetaConverter<T> {
|
||||
valueType(ValueType.NUMBER)
|
||||
}
|
||||
|
||||
override fun metaToObjectOrNull(meta: Meta): Int? = meta.int
|
||||
override fun objectToMeta(obj: Int): Meta = Meta(obj.asValue())
|
||||
override fun readOrNull(source: Meta): Int? = source.int
|
||||
override fun convert(obj: Int): Meta = Meta(obj.asValue())
|
||||
}
|
||||
|
||||
public val long: MetaConverter<Long> = object : MetaConverter<Long> {
|
||||
@ -125,8 +125,8 @@ public interface MetaConverter<T> {
|
||||
valueType(ValueType.NUMBER)
|
||||
}
|
||||
|
||||
override fun metaToObjectOrNull(meta: Meta): Long? = meta.long
|
||||
override fun objectToMeta(obj: Long): Meta = Meta(obj.asValue())
|
||||
override fun readOrNull(source: Meta): Long? = source.long
|
||||
override fun convert(obj: Long): Meta = Meta(obj.asValue())
|
||||
}
|
||||
|
||||
public inline fun <reified E : Enum<E>> enum(): MetaConverter<E> = object : MetaConverter<E> {
|
||||
@ -138,9 +138,9 @@ public interface MetaConverter<T> {
|
||||
}
|
||||
|
||||
@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(
|
||||
@ -153,9 +153,9 @@ public interface MetaConverter<T> {
|
||||
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>()
|
||||
private val serializer: KSerializer<T> = serializer()
|
||||
|
||||
override fun metaToObjectOrNull(meta: Meta): T? {
|
||||
val json = meta.toJson(descriptor)
|
||||
override fun readOrNull(source: Meta): T? {
|
||||
val json = source.toJson(descriptor)
|
||||
return Json.decodeFromJsonElement(serializer, json)
|
||||
}
|
||||
|
||||
override fun objectToMeta(obj: T): Meta {
|
||||
override fun convert(obj: T): Meta {
|
||||
val json = Json.encodeToJsonElement(obj)
|
||||
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>.nullableObjectToMeta(obj: T?): Meta? = obj?.let { objectToMeta(it) }
|
||||
public fun <T : Any> MetaConverter<T>.readNullable(item: Meta?): T? = item?.let { read(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))
|
@ -1,7 +1,6 @@
|
||||
package space.kscience.dataforge.meta
|
||||
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
@ -20,7 +19,7 @@ public fun <T> MetaProvider.convertable(
|
||||
converter: MetaConverter<T>,
|
||||
key: Name? = null,
|
||||
): 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,
|
||||
): ReadOnlyProperty<Any?, List<T>> = ReadOnlyProperty{_, property ->
|
||||
val name = key ?: property.name.asName()
|
||||
getIndexed(name).values.map { converter.metaToObject(it) }
|
||||
getIndexed(name).values.map { converter.read(it) }
|
||||
}
|
||||
|
||||
@DFExperimental
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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.names.Name
|
||||
import kotlin.jvm.JvmInline
|
@ -1,7 +1,6 @@
|
||||
package space.kscience.dataforge.meta
|
||||
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
@ -33,12 +32,12 @@ public fun <T> MutableMetaProvider.convertable(
|
||||
object : ReadWriteProperty<Any?, T?> {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
|
||||
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?) {
|
||||
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>> {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): List<T> {
|
||||
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>) {
|
||||
val name = key ?: property.name.asName()
|
||||
setIndexed(name, value.map { converter.objectToMeta(it) })
|
||||
setIndexed(name, value.map { converter.convert(it) })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,9 +7,11 @@ import space.kscience.dataforge.meta.descriptors.validate
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.misc.ThreadSafe
|
||||
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
|
||||
*/
|
||||
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>(
|
||||
private val builder: () -> T,
|
||||
) : Specification<T> {
|
||||
) : MetaSpec<T> {
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
override fun write(target: MutableMeta): T = empty().also {
|
||||
public fun write(target: MutableMeta): T = empty().also {
|
||||
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)
|
||||
}
|
||||
|
||||
@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)
|
||||
|
@ -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)
|
@ -27,7 +27,7 @@ public enum class ValueRestriction {
|
||||
/**
|
||||
* The descriptor for a meta
|
||||
* @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 valueRestriction The requirements for node content
|
||||
* @param valueTypes list of allowed types for [Meta.value], null if all values are allowed.
|
||||
@ -39,7 +39,7 @@ public enum class ValueRestriction {
|
||||
@Serializable
|
||||
public data class MetaDescriptor(
|
||||
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 valueRestriction: ValueRestriction = ValueRestriction.NONE,
|
||||
public val valueTypes: List<ValueType>? = null,
|
||||
@ -47,6 +47,9 @@ public data class MetaDescriptor(
|
||||
public val defaultValue: Value? = null,
|
||||
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
|
||||
*/
|
||||
@ -55,7 +58,7 @@ public data class MetaDescriptor(
|
||||
defaultValue?.let { defaultValue ->
|
||||
this.value = defaultValue
|
||||
}
|
||||
children.forEach { (key, descriptor) ->
|
||||
nodes.forEach { (key, descriptor) ->
|
||||
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 operator fun MetaDescriptor.get(name: Name): MetaDescriptor? = when (name.length) {
|
||||
0 -> this
|
||||
1 -> children[name.firstOrNull()!!.toString()]
|
||||
1 -> nodes[name.firstOrNull()!!.toString()]
|
||||
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 (!validate(item.value)) return false
|
||||
|
||||
children.forEach { (key, childDescriptor) ->
|
||||
nodes.forEach { (key, childDescriptor) ->
|
||||
if (!childDescriptor.validate(item[key])) return false
|
||||
}
|
||||
return true
|
||||
|
@ -80,7 +80,7 @@ public class MetaDescriptorBuilder @PublishedApi internal constructor() {
|
||||
|
||||
public fun from(descriptor: MetaDescriptor) {
|
||||
description = descriptor.description
|
||||
children.putAll(descriptor.children.mapValues { it.value.toBuilder() })
|
||||
children.putAll(descriptor.nodes.mapValues { it.value.toBuilder() })
|
||||
multiple = descriptor.multiple
|
||||
valueRestriction = descriptor.valueRestriction
|
||||
valueTypes = descriptor.valueTypes
|
||||
@ -92,7 +92,7 @@ public class MetaDescriptorBuilder @PublishedApi internal constructor() {
|
||||
@PublishedApi
|
||||
internal fun build(): MetaDescriptor = MetaDescriptor(
|
||||
description = description,
|
||||
children = children.mapValues { it.value.build() },
|
||||
nodes = children.mapValues { it.value.build() },
|
||||
multiple = multiple,
|
||||
valueRestriction = valueRestriction,
|
||||
valueTypes = valueTypes,
|
||||
@ -143,7 +143,7 @@ public fun MetaDescriptorBuilder.required() {
|
||||
|
||||
private fun MetaDescriptor.toBuilder(): MetaDescriptorBuilder = MetaDescriptorBuilder().apply {
|
||||
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
|
||||
valueRestriction = this@toBuilder.valueRestriction
|
||||
valueTypes = this@toBuilder.valueTypes
|
||||
|
@ -20,7 +20,7 @@ class MetaDelegateTest {
|
||||
var myValue by string()
|
||||
var safeValue by double(2.2)
|
||||
var enumValue by enum(TestEnum.YES)
|
||||
var inner by spec(InnerScheme)
|
||||
var inner by scheme(InnerScheme)
|
||||
|
||||
companion object : SchemeSpec<TestScheme>(::TestScheme)
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import space.kscience.dataforge.data.DataTree
|
||||
import space.kscience.dataforge.data.GoalExecutionRestriction
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
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.MetaDescriptor
|
||||
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 val spec: Specification<C>
|
||||
public val spec: MetaSpec<C>
|
||||
override val descriptor: MetaDescriptor? get() = spec.descriptor
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
public suspend fun <T : Any, C : Any> TaskWithSpec<T, C>.execute(
|
||||
workspace: Workspace,
|
||||
taskName: Name,
|
||||
block: C.() -> Unit = {},
|
||||
): TaskResult<T> = execute(workspace, taskName, spec(block))
|
||||
//public suspend fun <T : Any, C : Scheme> TaskWithSpec<T, C>.execute(
|
||||
// workspace: Workspace,
|
||||
// taskName: Name,
|
||||
// block: C.() -> Unit = {},
|
||||
//): TaskResult<T> = execute(workspace, taskName, spec(block))
|
||||
|
||||
public class TaskResultBuilder<in T : Any>(
|
||||
public val workspace: Workspace,
|
||||
@ -76,7 +76,6 @@ public class TaskResultBuilder<in T : Any>(
|
||||
* @param descriptor of meta accepted by this task
|
||||
* @param builder for resulting data set
|
||||
*/
|
||||
@Suppress("FunctionName")
|
||||
public fun <T : Any> Task(
|
||||
resultType: KType,
|
||||
descriptor: MetaDescriptor? = null,
|
||||
@ -98,7 +97,6 @@ public fun <T : Any> Task(
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("FunctionName")
|
||||
public inline fun <reified T : Any> Task(
|
||||
descriptor: MetaDescriptor? = null,
|
||||
noinline builder: suspend TaskResultBuilder<T>.() -> Unit,
|
||||
@ -116,10 +114,10 @@ public inline fun <reified T : Any> Task(
|
||||
@Suppress("FunctionName")
|
||||
public fun <T : Any, C : MetaRepr> Task(
|
||||
resultType: KType,
|
||||
specification: Specification<C>,
|
||||
specification: MetaSpec<C>,
|
||||
builder: suspend TaskResultBuilder<T>.(C) -> Unit,
|
||||
): TaskWithSpec<T, C> = object : TaskWithSpec<T, C> {
|
||||
override val spec: Specification<C> = specification
|
||||
override val spec: MetaSpec<C> = specification
|
||||
|
||||
override suspend fun execute(
|
||||
workspace: Workspace,
|
||||
@ -135,8 +133,7 @@ public fun <T : Any, C : MetaRepr> Task(
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("FunctionName")
|
||||
public inline fun <reified T : Any, C : MetaRepr> Task(
|
||||
specification: Specification<C>,
|
||||
specification: MetaSpec<C>,
|
||||
noinline builder: suspend TaskResultBuilder<T>.(C) -> Unit,
|
||||
): Task<T> = Task(typeOf<T>(), specification, builder)
|
@ -7,8 +7,8 @@ import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.data.*
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.MetaRepr
|
||||
import space.kscience.dataforge.meta.MetaSpec
|
||||
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.MetaDescriptorBuilder
|
||||
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(
|
||||
specification: Specification<C>,
|
||||
specification: MetaSpec<C>,
|
||||
noinline builder: suspend TaskResultBuilder<T>.(C) -> Unit,
|
||||
): PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, TaskReference<T>>> = PropertyDelegateProvider { _, property ->
|
||||
val taskName = Name.parse(property.name)
|
||||
|
Loading…
Reference in New Issue
Block a user