Compare commits
4 Commits
6e20fc3929
...
25281d0f6d
Author | SHA1 | Date | |
---|---|---|---|
25281d0f6d | |||
991f77c45a | |||
fd1d98aa87 | |||
282b43a05a |
@ -8,6 +8,8 @@
|
|||||||
- 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`
|
||||||
|
- `MetaConverter` now inherits `MetaSpec` (former `Specifiction`). So `MetaConverter` could be used more universally.
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
- `node(key,converter)` in favor of `serializable` delegate
|
- `node(key,converter)` in favor of `serializable` delegate
|
||||||
|
@ -8,7 +8,7 @@ plugins {
|
|||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
group = "space.kscience"
|
group = "space.kscience"
|
||||||
version = "0.7.2-dev-2"
|
version = "0.8.0-dev-1"
|
||||||
}
|
}
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
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.misc.DFExperimental
|
|
||||||
import space.kscience.dataforge.names.Name
|
|
||||||
import space.kscience.dataforge.names.startsWith
|
|
||||||
|
|
||||||
@DFExperimental
|
|
||||||
public class MetaProperty<T : Any>(
|
|
||||||
public val meta: ObservableMutableMeta,
|
|
||||||
public val name: Name,
|
|
||||||
public val converter: MetaConverter<T>,
|
|
||||||
) : Property<T?> {
|
|
||||||
|
|
||||||
override var value: T?
|
|
||||||
get() = converter.nullableMetaToObject(meta[name])
|
|
||||||
set(value) {
|
|
||||||
meta[name] = converter.nullableObjectToMeta(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]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removeChangeListener(owner: Any?) {
|
|
||||||
meta.removeListener(owner)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
package space.kscience.dataforge.properties
|
|
||||||
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
|
||||||
|
|
||||||
@DFExperimental
|
|
||||||
public interface Property<T> {
|
|
||||||
public var value: T
|
|
||||||
|
|
||||||
public fun onChange(owner: Any? = null, callback: (T) -> Unit)
|
|
||||||
public fun removeChangeListener(owner: Any? = null)
|
|
||||||
}
|
|
||||||
|
|
||||||
@DFExperimental
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
|
||||||
public fun <T> Property<T>.toFlow(): StateFlow<T> = MutableStateFlow(value).also { stateFlow ->
|
|
||||||
onChange {
|
|
||||||
stateFlow.value = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reflect all changes in the [source] property onto this property. Does not reflect changes back.
|
|
||||||
*
|
|
||||||
* @return a mirroring job
|
|
||||||
*/
|
|
||||||
@DFExperimental
|
|
||||||
public fun <T> Property<T>.mirror(source: Property<T>) {
|
|
||||||
source.onChange(this) {
|
|
||||||
this.value = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bi-directional connection between properties
|
|
||||||
*/
|
|
||||||
@DFExperimental
|
|
||||||
public fun <T> Property<T>.bind(other: Property<T>) {
|
|
||||||
onChange(other) {
|
|
||||||
other.value = it
|
|
||||||
}
|
|
||||||
other.onChange {
|
|
||||||
this.value = it
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,51 @@
|
|||||||
|
package space.kscience.dataforge.properties
|
||||||
|
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
|
import kotlinx.coroutines.flow.*
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import space.kscience.dataforge.meta.*
|
||||||
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
|
|
||||||
|
@DFExperimental
|
||||||
|
public fun <T> ObservableMeta.asFlow(converter: MetaSpec<T>): Flow<T> = callbackFlow {
|
||||||
|
onChange(this){
|
||||||
|
trySend(converter.read(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
awaitClose{
|
||||||
|
removeListener(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@DFExperimental
|
||||||
|
public fun <T> MutableMeta.listenTo(
|
||||||
|
scope: CoroutineScope,
|
||||||
|
converter: MetaConverter<T>,
|
||||||
|
flow: Flow<T>,
|
||||||
|
): Job = flow.onEach {
|
||||||
|
update(converter.convert(it))
|
||||||
|
}.launchIn(scope)
|
||||||
|
|
||||||
|
@DFExperimental
|
||||||
|
public fun <T> ObservableMutableMeta.bind(
|
||||||
|
scope: CoroutineScope,
|
||||||
|
converter: MetaConverter<T>,
|
||||||
|
flow: MutableSharedFlow<T>,
|
||||||
|
): Job = scope.launch{
|
||||||
|
listenTo(this, converter,flow)
|
||||||
|
onChange(flow){
|
||||||
|
launch {
|
||||||
|
flow.emit(converter.read(this@onChange))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flow.onCompletion {
|
||||||
|
removeListener(flow)
|
||||||
|
}
|
||||||
|
}.also {
|
||||||
|
it.invokeOnCompletion {
|
||||||
|
removeListener(flow)
|
||||||
|
}
|
||||||
|
}
|
@ -1,31 +0,0 @@
|
|||||||
package space.kscience.dataforge.properties
|
|
||||||
|
|
||||||
|
|
||||||
import space.kscience.dataforge.meta.Scheme
|
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
|
||||||
import space.kscience.dataforge.names.parseAsName
|
|
||||||
import space.kscience.dataforge.names.startsWith
|
|
||||||
import kotlin.reflect.KMutableProperty1
|
|
||||||
|
|
||||||
@DFExperimental
|
|
||||||
public fun <S : Scheme, T : Any> S.property(property: KMutableProperty1<S, T?>): Property<T?> =
|
|
||||||
object : Property<T?> {
|
|
||||||
override var value: T?
|
|
||||||
get() = property.get(this@property)
|
|
||||||
set(value) {
|
|
||||||
property.set(this@property, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onChange(owner: Any?, callback: (T?) -> Unit) {
|
|
||||||
this@property.meta.onChange(this) { name ->
|
|
||||||
if (name.startsWith(property.name.parseAsName(true))) {
|
|
||||||
callback(property.get(this@property))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removeChangeListener(owner: Any?) {
|
|
||||||
this@property.meta.removeListener(this@property)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
package space.kscience.dataforge.properties
|
|
||||||
|
|
||||||
import space.kscience.dataforge.meta.Scheme
|
|
||||||
import space.kscience.dataforge.meta.SchemeSpec
|
|
||||||
import space.kscience.dataforge.meta.int
|
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
|
||||||
import kotlin.test.Test
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
|
|
||||||
internal class TestScheme : Scheme() {
|
|
||||||
var a by int()
|
|
||||||
var b by int()
|
|
||||||
companion object : SchemeSpec<TestScheme>(::TestScheme)
|
|
||||||
}
|
|
||||||
|
|
||||||
@DFExperimental
|
|
||||||
class MetaPropertiesTest {
|
|
||||||
@Test
|
|
||||||
fun testBinding() {
|
|
||||||
val scheme = TestScheme.empty()
|
|
||||||
val a = scheme.property(TestScheme::a)
|
|
||||||
val b = scheme.property(TestScheme::b)
|
|
||||||
a.bind(b)
|
|
||||||
scheme.a = 2
|
|
||||||
assertEquals(2, scheme.b)
|
|
||||||
assertEquals(2, b.value)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
package space.kscience.dataforge.properties
|
|
||||||
|
|
||||||
import org.w3c.dom.HTMLInputElement
|
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
|
||||||
|
|
||||||
@DFExperimental
|
|
||||||
public fun HTMLInputElement.bindValue(property: Property<String>) {
|
|
||||||
if (this.onchange != null) error("Input element already bound")
|
|
||||||
this.onchange = {
|
|
||||||
property.value = this.value
|
|
||||||
Unit
|
|
||||||
}
|
|
||||||
property.onChange(this) {
|
|
||||||
if (value != it) {
|
|
||||||
value = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@DFExperimental
|
|
||||||
public fun HTMLInputElement.bindChecked(property: Property<Boolean>) {
|
|
||||||
if (this.onchange != null) error("Input element already bound")
|
|
||||||
this.onchange = {
|
|
||||||
property.value = this.checked
|
|
||||||
Unit
|
|
||||||
}
|
|
||||||
property.onChange(this) {
|
|
||||||
if (checked != it) {
|
|
||||||
checked = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,126 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018 Alexander Nozik.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package space.kscience.dataforge.descriptors
|
|
||||||
|
|
||||||
//@MustBeDocumented
|
|
||||||
//annotation class Attribute(
|
|
||||||
// val key: String,
|
|
||||||
// val value: String
|
|
||||||
//)
|
|
||||||
//
|
|
||||||
//@MustBeDocumented
|
|
||||||
//annotation class Attributes(
|
|
||||||
// val attrs: Array<Attribute>
|
|
||||||
//)
|
|
||||||
//
|
|
||||||
//@MustBeDocumented
|
|
||||||
//annotation class ItemDef(
|
|
||||||
// val info: String = "",
|
|
||||||
// val multiple: Boolean = false,
|
|
||||||
// val required: Boolean = false
|
|
||||||
//)
|
|
||||||
//
|
|
||||||
//@Target(AnnotationTarget.PROPERTY)
|
|
||||||
//@MustBeDocumented
|
|
||||||
//annotation class ValueDef(
|
|
||||||
// val type: Array<ValueType> = [ValueType.STRING],
|
|
||||||
// val def: String = "",
|
|
||||||
// val allowed: Array<String> = [],
|
|
||||||
// val enumeration: KClass<*> = Any::class
|
|
||||||
//)
|
|
||||||
|
|
||||||
///**
|
|
||||||
// * Description text for meta property, node or whole object
|
|
||||||
// */
|
|
||||||
//@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
|
|
||||||
//@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
//@MustBeDocumented
|
|
||||||
//annotation class Description(val value: String)
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Annotation for value property which states that lists are expected
|
|
||||||
// */
|
|
||||||
//@Target(AnnotationTarget.PROPERTY)
|
|
||||||
//@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
//@MustBeDocumented
|
|
||||||
//annotation class Multiple
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Descriptor target
|
|
||||||
// * The DataForge path to the resource containing the description. Following targets are supported:
|
|
||||||
// * 1. resource
|
|
||||||
// * 1. file
|
|
||||||
// * 1. class
|
|
||||||
// * 1. method
|
|
||||||
// * 1. property
|
|
||||||
// *
|
|
||||||
// *
|
|
||||||
// * Does not work if [type] is provided
|
|
||||||
// */
|
|
||||||
//@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER)
|
|
||||||
//@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
//@MustBeDocumented
|
|
||||||
//annotation class Descriptor(val value: String)
|
|
||||||
//
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Aggregator class for descriptor nodes
|
|
||||||
// */
|
|
||||||
//@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.VALUE_PARAMETER)
|
|
||||||
//@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
//@MustBeDocumented
|
|
||||||
//annotation class DescriptorNodes(vararg val nodes: NodeDef)
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Aggregator class for descriptor values
|
|
||||||
// */
|
|
||||||
//@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.VALUE_PARAMETER)
|
|
||||||
//@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
//@MustBeDocumented
|
|
||||||
//annotation class DescriptorValues(vararg val nodes: ValueDef)
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Alternative name for property descriptor declaration
|
|
||||||
// */
|
|
||||||
//@Target(AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER)
|
|
||||||
//@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
//@MustBeDocumented
|
|
||||||
//annotation class DescriptorName(val name: String)
|
|
||||||
//
|
|
||||||
//@Target(AnnotationTarget.PROPERTY)
|
|
||||||
//@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
//@MustBeDocumented
|
|
||||||
//annotation class DescriptorValue(val def: ValueDef)
|
|
||||||
////TODO enter fields directly?
|
|
||||||
//
|
|
||||||
//@Target(AnnotationTarget.PROPERTY)
|
|
||||||
//@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
//@MustBeDocumented
|
|
||||||
//annotation class ValueProperty(
|
|
||||||
// val name: String = "",
|
|
||||||
// val type: Array<ValueType> = arrayOf(ValueType.STRING),
|
|
||||||
// val multiple: Boolean = false,
|
|
||||||
// val def: String = "",
|
|
||||||
// val enumeration: KClass<*> = Any::class,
|
|
||||||
// val tags: Array<String> = emptyArray()
|
|
||||||
//)
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//@Target(AnnotationTarget.PROPERTY)
|
|
||||||
//@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
//@MustBeDocumented
|
|
||||||
//annotation class NodeProperty(val name: String = "")
|
|
@ -1,53 +1,124 @@
|
|||||||
package space.kscience.dataforge.descriptors
|
package space.kscience.dataforge.descriptors
|
||||||
|
|
||||||
|
import kotlinx.serialization.ExperimentalSerializationApi
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.json.decodeFromStream
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import space.kscience.dataforge.meta.Scheme
|
||||||
|
import space.kscience.dataforge.meta.SchemeSpec
|
||||||
|
import space.kscience.dataforge.meta.ValueType
|
||||||
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||||
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptorBuilder
|
||||||
|
import space.kscience.dataforge.meta.descriptors.node
|
||||||
|
import java.net.URL
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.reflect.full.isSubclassOf
|
||||||
|
import kotlin.reflect.full.memberProperties
|
||||||
|
import kotlin.reflect.typeOf
|
||||||
|
|
||||||
//inline fun <reified T : Scheme> T.buildDescriptor(): NodeDescriptor = NodeDescriptor {
|
|
||||||
// T::class.apply {
|
|
||||||
// findAnnotation<ItemDef>()?.let { def ->
|
|
||||||
// info = def.info
|
|
||||||
// required = def.required
|
|
||||||
// multiple = def.multiple
|
|
||||||
// }
|
|
||||||
// findAnnotation<Attribute>()?.let { attr ->
|
|
||||||
// attributes {
|
|
||||||
// this[attr.key] = attr.value.parseValue()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// findAnnotation<Attributes>()?.attrs?.forEach { attr ->
|
|
||||||
// attributes {
|
|
||||||
// this[attr.key] = attr.value.parseValue()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// T::class.memberProperties.forEach { property ->
|
|
||||||
// val delegate = property.getDelegate(this@buildDescriptor)
|
|
||||||
//
|
|
||||||
// val descriptor: ItemDescriptor = when (delegate) {
|
|
||||||
// is ConfigurableDelegate -> buildPropertyDescriptor(property, delegate)
|
|
||||||
// is ReadWriteDelegateWrapper<*, *> -> {
|
|
||||||
// if (delegate.delegate is ConfigurableDelegate) {
|
|
||||||
// buildPropertyDescriptor(property, delegate.delegate as ConfigurableDelegate)
|
|
||||||
// } else {
|
|
||||||
// return@forEach
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else -> return@forEach
|
|
||||||
// }
|
|
||||||
// defineItem(property.name, descriptor)
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//inline fun <T : Scheme, reified V : Any?> buildPropertyDescriptor(
|
/**
|
||||||
// property: KProperty1<T, V>,
|
* Description text for meta property, node or whole object
|
||||||
// delegate: ConfigurableDelegate
|
*/
|
||||||
//): ItemDescriptor {
|
@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
|
||||||
// when {
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
// V::class.isSubclassOf(Scheme::class) -> NodeDescriptor {
|
@MustBeDocumented
|
||||||
// default = delegate.default.node
|
public annotation class Description(val value: String)
|
||||||
// }
|
|
||||||
// V::class.isSubclassOf(Meta::class) -> NodeDescriptor {
|
@Target(AnnotationTarget.PROPERTY)
|
||||||
// default = delegate.default.node
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
// }
|
@MustBeDocumented
|
||||||
//
|
public annotation class Multiple()
|
||||||
// }
|
|
||||||
//}
|
@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
@MustBeDocumented
|
||||||
|
public annotation class DescriptorResource(val resourceName: String)
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
@MustBeDocumented
|
||||||
|
public annotation class DescriptorUrl(val url: String)
|
||||||
|
|
||||||
|
|
||||||
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
|
private fun MetaDescriptorBuilder.loadDescriptorFromUrl(url: URL) {
|
||||||
|
url.openStream().use {
|
||||||
|
from(Json.decodeFromStream(MetaDescriptor.serializer(), it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MetaDescriptorBuilder.loadDescriptorFromResource(resource: DescriptorResource) {
|
||||||
|
val url = {}.javaClass.getResource(resource.resourceName)
|
||||||
|
if (url != null) {
|
||||||
|
loadDescriptorFromUrl(url)
|
||||||
|
} else {
|
||||||
|
LoggerFactory.getLogger("System")
|
||||||
|
.error("Can't find descriptor resource with name ${resource.resourceName}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public fun <T : Any> MetaDescriptor.Companion.forClass(
|
||||||
|
kClass: KClass<T>,
|
||||||
|
mod: MetaDescriptorBuilder.() -> Unit = {},
|
||||||
|
): MetaDescriptor = MetaDescriptor {
|
||||||
|
when {
|
||||||
|
kClass.isSubclassOf(Number::class) -> valueType(ValueType.NUMBER)
|
||||||
|
kClass == String::class -> ValueType.STRING
|
||||||
|
kClass == Boolean::class -> ValueType.BOOLEAN
|
||||||
|
kClass == DoubleArray::class -> ValueType.LIST
|
||||||
|
}
|
||||||
|
|
||||||
|
kClass.annotations.forEach {
|
||||||
|
when (it) {
|
||||||
|
is Description -> description = it.value
|
||||||
|
|
||||||
|
is DescriptorResource -> loadDescriptorFromResource(it)
|
||||||
|
|
||||||
|
is DescriptorUrl -> loadDescriptorFromUrl(URL(it.url))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kClass.memberProperties.forEach { property ->
|
||||||
|
|
||||||
|
var flag = false
|
||||||
|
|
||||||
|
val descriptor = MetaDescriptor {
|
||||||
|
//use base type descriptor as a base
|
||||||
|
(property.returnType.classifier as? KClass<*>)?.let {
|
||||||
|
from(forClass(it))
|
||||||
|
}
|
||||||
|
property.annotations.forEach {
|
||||||
|
when (it) {
|
||||||
|
is Description -> {
|
||||||
|
description = it.value
|
||||||
|
flag = true
|
||||||
|
}
|
||||||
|
|
||||||
|
is Multiple -> {
|
||||||
|
multiple = true
|
||||||
|
flag = true
|
||||||
|
}
|
||||||
|
|
||||||
|
is DescriptorResource -> {
|
||||||
|
loadDescriptorFromResource(it)
|
||||||
|
flag = true
|
||||||
|
}
|
||||||
|
|
||||||
|
is DescriptorUrl -> {
|
||||||
|
loadDescriptorFromUrl(URL(it.url))
|
||||||
|
flag = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flag) {
|
||||||
|
node(property.name, descriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mod()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
public inline fun <reified T : Scheme> SchemeSpec<T>.autoDescriptor( noinline mod: MetaDescriptorBuilder.() -> Unit = {}): MetaDescriptor =
|
||||||
|
MetaDescriptor.forClass(typeOf<T>().classifier as KClass<T>, mod)
|
@ -0,0 +1,31 @@
|
|||||||
|
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.Scheme
|
||||||
|
import space.kscience.dataforge.meta.SchemeSpec
|
||||||
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||||
|
import space.kscience.dataforge.meta.int
|
||||||
|
import space.kscience.dataforge.meta.string
|
||||||
|
|
||||||
|
private class TestScheme: Scheme(){
|
||||||
|
|
||||||
|
@Description("A")
|
||||||
|
val a by string()
|
||||||
|
|
||||||
|
@Description("B")
|
||||||
|
val b by int()
|
||||||
|
|
||||||
|
companion object: SchemeSpec<TestScheme>(::TestScheme){
|
||||||
|
override val descriptor: MetaDescriptor = autoDescriptor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestAutoDescriptors {
|
||||||
|
@Test
|
||||||
|
fun autoDescriptor(){
|
||||||
|
val autoDescriptor = MetaDescriptor.forClass(TestScheme::class)
|
||||||
|
println(Json{prettyPrint = true}.encodeToString(autoDescriptor))
|
||||||
|
}
|
||||||
|
}
|
@ -71,6 +71,11 @@ internal class ByteArrayBinary(
|
|||||||
|
|
||||||
override fun view(offset: Int, binarySize: Int): ByteArrayBinary =
|
override fun view(offset: Int, binarySize: Int): ByteArrayBinary =
|
||||||
ByteArrayBinary(array, start + offset, binarySize)
|
ByteArrayBinary(array, start + offset, binarySize)
|
||||||
|
|
||||||
|
override fun toString(): String =
|
||||||
|
"ByteArrayBinary(array=$array, start=$start, size=$size)"
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun ByteArray.asBinary(): Binary = ByteArrayBinary(this)
|
public fun ByteArray.asBinary(): Binary = ByteArrayBinary(this)
|
||||||
|
@ -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
|
||||||
|
@ -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,6 @@ public interface MetaConverter<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun <T : Any> MetaConverter<T>.nullableMetaToObject(item: Meta?): T? = item?.let { metaToObject(it) }
|
public fun <T : Any> MetaConverter<T>.convertNullable(obj: T?): Meta? = obj?.let { convert(it) }
|
||||||
public fun <T : Any> MetaConverter<T>.nullableObjectToMeta(obj: T?): Meta? = obj?.let { objectToMeta(it) }
|
|
||||||
|
|
||||||
public fun <T> MetaConverter<T>.valueToObject(value: Value): T? = metaToObject(Meta(value))
|
|
@ -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
|
||||||
@ -14,13 +13,13 @@ public fun MetaProvider.node(key: Name? = null): ReadOnlyProperty<Any?, Meta?> =
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use [converter] to read the Meta node
|
* Use [metaSpec] to read the Meta node
|
||||||
*/
|
*/
|
||||||
public fun <T> MetaProvider.convertable(
|
public fun <T> MetaProvider.spec(
|
||||||
converter: MetaConverter<T>,
|
metaSpec: MetaSpec<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 { metaSpec.read(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,30 +29,30 @@ public fun <T> MetaProvider.convertable(
|
|||||||
public inline fun <reified T> MetaProvider.serializable(
|
public inline fun <reified T> MetaProvider.serializable(
|
||||||
descriptor: MetaDescriptor? = null,
|
descriptor: MetaDescriptor? = null,
|
||||||
key: Name? = null,
|
key: Name? = null,
|
||||||
): ReadOnlyProperty<Any?, T?> = convertable(MetaConverter.serializable(descriptor), key)
|
): ReadOnlyProperty<Any?, T?> = spec(MetaConverter.serializable(descriptor), key)
|
||||||
|
|
||||||
@Deprecated("Use convertable", ReplaceWith("convertable(converter, key)"))
|
@Deprecated("Use convertable", ReplaceWith("convertable(converter, key)"))
|
||||||
public fun <T> MetaProvider.node(
|
public fun <T> MetaProvider.node(
|
||||||
key: Name? = null,
|
key: Name? = null,
|
||||||
converter: MetaConverter<T>,
|
converter: MetaSpec<T>,
|
||||||
): ReadOnlyProperty<Any?, T?> = convertable(converter, key)
|
): ReadOnlyProperty<Any?, T?> = spec(converter, key)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use [converter] to convert a list of same name siblings meta to object
|
* Use [converter] to convert a list of same name siblings meta to object
|
||||||
*/
|
*/
|
||||||
public fun <T> Meta.listOfConvertable(
|
public fun <T> Meta.listOfSpec(
|
||||||
converter: MetaConverter<T>,
|
converter: MetaSpec<T>,
|
||||||
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
|
||||||
public inline fun <reified T> Meta.listOfSerializable(
|
public inline fun <reified T> Meta.listOfSerializable(
|
||||||
descriptor: MetaDescriptor? = null,
|
descriptor: MetaDescriptor? = null,
|
||||||
key: Name? = null,
|
key: Name? = null,
|
||||||
): ReadOnlyProperty<Any?, List<T>> = listOfConvertable(MetaConverter.serializable(descriptor), key)
|
): ReadOnlyProperty<Any?, List<T>> = listOfSpec(MetaConverter.serializable(descriptor), key)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A property delegate that uses custom key
|
* A property delegate that uses custom key
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public fun <T : Any> MetaSpec<T>.readNullable(item: Meta?): T? = item?.let { read(it) }
|
||||||
|
public fun <T> MetaSpec<T>.readValue(value: Value): T? = read(Meta(value))
|
@ -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
|
@ -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) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package space.kscience.dataforge.meta
|
package space.kscience.dataforge.meta
|
||||||
|
|
||||||
import space.kscience.dataforge.misc.ThreadSafe
|
import space.kscience.dataforge.misc.ThreadSafe
|
||||||
import space.kscience.dataforge.names.*
|
import space.kscience.dataforge.names.Name
|
||||||
import kotlin.reflect.KProperty1
|
import space.kscience.dataforge.names.cutFirst
|
||||||
|
import space.kscience.dataforge.names.firstOrNull
|
||||||
|
import space.kscience.dataforge.names.isEmpty
|
||||||
|
|
||||||
|
|
||||||
internal data class MetaListener(
|
internal data class MetaListener(
|
||||||
@ -15,12 +17,15 @@ internal data class MetaListener(
|
|||||||
*/
|
*/
|
||||||
public interface ObservableMeta : Meta {
|
public interface ObservableMeta : Meta {
|
||||||
/**
|
/**
|
||||||
* Add change listener to this meta. Owner is declared to be able to remove listeners later. Listener without owner could not be removed
|
* Add change listener to this meta. The Owner is declared to be able to remove listeners later.
|
||||||
|
* Listeners without an owner could be only removed all together.
|
||||||
|
*
|
||||||
|
* `this` object in the listener represents the current state of this meta. The name points to a changed node
|
||||||
*/
|
*/
|
||||||
public fun onChange(owner: Any?, callback: Meta.(name: Name) -> Unit)
|
public fun onChange(owner: Any?, callback: Meta.(name: Name) -> Unit)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove all listeners belonging to given owner
|
* Remove all listeners belonging to the given [owner]. Passing null removes all listeners.
|
||||||
*/
|
*/
|
||||||
public fun removeListener(owner: Any?)
|
public fun removeListener(owner: Any?)
|
||||||
|
|
||||||
@ -67,24 +72,4 @@ internal abstract class AbstractObservableMeta : ObservableMeta {
|
|||||||
override fun toString(): String = Meta.toString(this)
|
override fun toString(): String = Meta.toString(this)
|
||||||
override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
|
override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
|
||||||
override fun hashCode(): Int = Meta.hashCode(this)
|
override fun hashCode(): Int = Meta.hashCode(this)
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use the value of the property in a [callBack].
|
|
||||||
* The callback is called once immediately after subscription to pass the initial value.
|
|
||||||
*
|
|
||||||
* Optional [owner] property is used for
|
|
||||||
*/
|
|
||||||
public fun <S : Scheme, T> S.useProperty(
|
|
||||||
property: KProperty1<S, T>,
|
|
||||||
owner: Any? = null,
|
|
||||||
callBack: S.(T) -> Unit,
|
|
||||||
) {
|
|
||||||
//Pass initial value.
|
|
||||||
callBack(property.get(this))
|
|
||||||
meta.onChange(owner) { name ->
|
|
||||||
if (name.startsWith(property.name.asName())) {
|
|
||||||
callBack(property.get(this@useProperty))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -7,36 +7,49 @@ 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
|
||||||
|
import kotlin.reflect.KProperty1
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Meta to be mutated by this schme
|
* Meta to be mutated by this scheme
|
||||||
*/
|
*/
|
||||||
private var targetMeta: MutableMeta = MutableMeta()
|
private var target: MutableMeta? = null
|
||||||
|
get() {
|
||||||
|
// automatic initialization of target if it is missing
|
||||||
|
if (field == null) {
|
||||||
|
field = MutableMeta()
|
||||||
|
}
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default values provided by this scheme
|
* Default values provided by this scheme
|
||||||
*/
|
*/
|
||||||
private var defaultMeta: Meta? = null
|
private var prototype: Meta? = null
|
||||||
|
|
||||||
final override val meta: ObservableMutableMeta = SchemeMeta(Name.EMPTY)
|
final override val meta: ObservableMutableMeta = SchemeMeta(Name.EMPTY)
|
||||||
|
|
||||||
final override var descriptor: MetaDescriptor? = null
|
final override var descriptor: MetaDescriptor? = null
|
||||||
internal set
|
private set
|
||||||
|
|
||||||
internal fun wrap(
|
/**
|
||||||
newMeta: MutableMeta,
|
* This method must be called before the scheme could be used
|
||||||
preserveDefault: Boolean = false,
|
*/
|
||||||
|
internal fun initialize(
|
||||||
|
target: MutableMeta,
|
||||||
|
prototype: Meta,
|
||||||
|
descriptor: MetaDescriptor?,
|
||||||
) {
|
) {
|
||||||
if (preserveDefault) {
|
this.target = target
|
||||||
defaultMeta = targetMeta.seal()
|
this.prototype = prototype
|
||||||
}
|
this.descriptor = descriptor
|
||||||
targetMeta = newMeta
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,11 +60,11 @@ public open class Scheme : Described, MetaRepr, MutableMetaProvider, Configurabl
|
|||||||
return descriptor?.validate(meta) ?: true
|
return descriptor?.validate(meta) ?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun get(name: Name): MutableMeta? = meta.get(name)
|
override fun get(name: Name): MutableMeta? = meta[name]
|
||||||
|
|
||||||
override fun set(name: Name, node: Meta?) {
|
override fun set(name: Name, node: Meta?) {
|
||||||
if (validate(name, meta)) {
|
if (validate(name, meta)) {
|
||||||
meta.set(name, node)
|
meta[name] = node
|
||||||
} else {
|
} else {
|
||||||
error("Validation failed for node $node at $name")
|
error("Validation failed for node $node at $name")
|
||||||
}
|
}
|
||||||
@ -68,14 +81,16 @@ public open class Scheme : Described, MetaRepr, MutableMetaProvider, Configurabl
|
|||||||
|
|
||||||
private val listeners: MutableList<MetaListener> = mutableListOf()
|
private val listeners: MutableList<MetaListener> = mutableListOf()
|
||||||
|
|
||||||
|
override fun toString(): String = meta.toString()
|
||||||
|
|
||||||
private inner class SchemeMeta(val pathName: Name) : ObservableMutableMeta {
|
private inner class SchemeMeta(val pathName: Name) : ObservableMutableMeta {
|
||||||
override var value: Value?
|
override var value: Value?
|
||||||
get() = targetMeta[pathName]?.value
|
get() = target[pathName]?.value
|
||||||
?: defaultMeta?.get(pathName)?.value
|
?: prototype?.get(pathName)?.value
|
||||||
?: descriptor?.get(pathName)?.defaultValue
|
?: descriptor?.get(pathName)?.defaultValue
|
||||||
set(value) {
|
set(value) {
|
||||||
val oldValue = targetMeta[pathName]?.value
|
val oldValue = target[pathName]?.value
|
||||||
targetMeta[pathName] = value
|
target!![pathName] = value
|
||||||
if (oldValue != value) {
|
if (oldValue != value) {
|
||||||
invalidate(Name.EMPTY)
|
invalidate(Name.EMPTY)
|
||||||
}
|
}
|
||||||
@ -83,8 +98,8 @@ public open class Scheme : Described, MetaRepr, MutableMetaProvider, Configurabl
|
|||||||
|
|
||||||
override val items: Map<NameToken, ObservableMutableMeta>
|
override val items: Map<NameToken, ObservableMutableMeta>
|
||||||
get() {
|
get() {
|
||||||
val targetKeys = targetMeta[pathName]?.items?.keys ?: emptySet()
|
val targetKeys = target[pathName]?.items?.keys ?: emptySet()
|
||||||
val defaultKeys = defaultMeta?.get(pathName)?.items?.keys ?: emptySet()
|
val defaultKeys = prototype?.get(pathName)?.items?.keys ?: emptySet()
|
||||||
return (targetKeys + defaultKeys).associateWith { SchemeMeta(pathName + it) }
|
return (targetKeys + defaultKeys).associateWith { SchemeMeta(pathName + it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +126,7 @@ public open class Scheme : Described, MetaRepr, MutableMetaProvider, Configurabl
|
|||||||
override fun hashCode(): Int = Meta.hashCode(this)
|
override fun hashCode(): Int = Meta.hashCode(this)
|
||||||
|
|
||||||
override fun set(name: Name, node: Meta?) {
|
override fun set(name: Name, node: Meta?) {
|
||||||
targetMeta.set(name, node)
|
target!![name] = node
|
||||||
invalidate(name)
|
invalidate(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +134,6 @@ public open class Scheme : Described, MetaRepr, MutableMetaProvider, Configurabl
|
|||||||
|
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
override fun attach(name: Name, node: ObservableMutableMeta) {
|
override fun attach(name: Name, node: ObservableMutableMeta) {
|
||||||
//TODO implement zero-copy attachment
|
|
||||||
set(name, node)
|
set(name, node)
|
||||||
node.onChange(this) { changeName ->
|
node.onChange(this) { changeName ->
|
||||||
set(name + changeName, this[changeName])
|
set(name + changeName, this[changeName])
|
||||||
@ -131,10 +145,11 @@ public open class Scheme : Described, MetaRepr, MutableMetaProvider, Configurabl
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Relocate scheme target onto given [MutableMeta]. Old provider does not get updates anymore.
|
* Relocate scheme target onto given [MutableMeta]. Old provider does not get updates anymore.
|
||||||
* Current state of the scheme used as a default.
|
* The Current state of the scheme that os used as a default.
|
||||||
*/
|
*/
|
||||||
|
@DFExperimental
|
||||||
public fun <T : Scheme> T.retarget(provider: MutableMeta): T = apply {
|
public fun <T : Scheme> T.retarget(provider: MutableMeta): T = apply {
|
||||||
wrap(provider, true)
|
initialize(provider, meta.seal(), descriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -153,24 +168,145 @@ 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 fun read(source: Meta): T = builder().also {
|
|
||||||
it.wrap(MutableMeta().withDefault(source))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun write(target: MutableMeta): T = empty().also {
|
|
||||||
it.wrap(target)
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO Generate descriptor from Scheme class
|
|
||||||
override val descriptor: MetaDescriptor? get() = null
|
override val descriptor: MetaDescriptor? get() = null
|
||||||
|
|
||||||
override fun empty(): T = builder().also {
|
override fun readOrNull(source: Meta): T = builder().also {
|
||||||
it.descriptor = descriptor
|
it.initialize(MutableMeta(), source, descriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("OVERRIDE_BY_INLINE")
|
public fun write(target: MutableMeta): T = empty().also {
|
||||||
final override inline operator fun invoke(action: T.() -> Unit): T = empty().apply(action)
|
it.initialize(target, Meta.EMPTY, descriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate an empty object
|
||||||
|
*/
|
||||||
|
public fun empty(): T = builder().also {
|
||||||
|
it.initialize(MutableMeta(), Meta.EMPTY, descriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the value of the property in a [callBack].
|
||||||
|
* The callback is called once immediately after subscription to pass the initial value.
|
||||||
|
*
|
||||||
|
* Optional [owner] property is used for
|
||||||
|
*/
|
||||||
|
public fun <S : Scheme, T> S.useProperty(
|
||||||
|
property: KProperty1<S, T>,
|
||||||
|
owner: Any? = null,
|
||||||
|
callBack: S.(T) -> Unit,
|
||||||
|
) {
|
||||||
|
//Pass initial value.
|
||||||
|
callBack(property.get(this))
|
||||||
|
meta.onChange(owner) { name ->
|
||||||
|
if (name.startsWith(property.name.asName())) {
|
||||||
|
callBack(property.get(this@useProperty))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -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
|
* 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
|
||||||
|
@ -44,38 +44,27 @@ public class MetaDescriptorBuilder @PublishedApi internal constructor() {
|
|||||||
attributes.apply(block)
|
attributes.apply(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun item(name: Name, block: MetaDescriptorBuilder.() -> Unit = {}): MetaDescriptorBuilder {
|
internal fun node(
|
||||||
return when (name.length) {
|
name: Name,
|
||||||
0 -> apply(block)
|
descriptorBuilder: MetaDescriptorBuilder,
|
||||||
|
): Unit {
|
||||||
|
when (name.length) {
|
||||||
|
0 -> error("Can't set descriptor to root")
|
||||||
1 -> {
|
1 -> {
|
||||||
val target = MetaDescriptorBuilder().apply(block)
|
children[name.first().body] = descriptorBuilder
|
||||||
children[name.first().body] = target
|
|
||||||
target
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> children.getOrPut(name.first().body) {
|
||||||
children.getOrPut(name.first().body) { MetaDescriptorBuilder() }.item(name.cutFirst(), block)
|
MetaDescriptorBuilder()
|
||||||
}
|
}.node(name.cutFirst(), descriptorBuilder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun node(
|
internal fun node(
|
||||||
name: Name,
|
name: Name,
|
||||||
descriptor: MetaDescriptor,
|
descriptorBuilder: MetaDescriptor,
|
||||||
block: MetaDescriptorBuilder.() -> Unit = {},
|
): Unit {
|
||||||
): MetaDescriptorBuilder = when (name.length) {
|
node(name, descriptorBuilder.toBuilder())
|
||||||
0 -> error("Can't set descriptor to root")
|
|
||||||
1 -> {
|
|
||||||
val item = descriptor.toBuilder().apply {
|
|
||||||
valueRestriction = ValueRestriction.ABSENT
|
|
||||||
}.apply(block)
|
|
||||||
children[name.first().body] = item
|
|
||||||
item
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> children.getOrPut(name.first().body) {
|
|
||||||
MetaDescriptorBuilder()
|
|
||||||
}.node(name.cutFirst(), descriptor, block)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public var allowedValues: List<Value>
|
public var allowedValues: List<Value>
|
||||||
@ -89,10 +78,21 @@ public class MetaDescriptorBuilder @PublishedApi internal constructor() {
|
|||||||
allowedValues = values.map { Value.of(it) }
|
allowedValues = values.map { Value.of(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun from(descriptor: MetaDescriptor) {
|
||||||
|
description = descriptor.description
|
||||||
|
children.putAll(descriptor.nodes.mapValues { it.value.toBuilder() })
|
||||||
|
multiple = descriptor.multiple
|
||||||
|
valueRestriction = descriptor.valueRestriction
|
||||||
|
valueTypes = descriptor.valueTypes
|
||||||
|
indexKey = descriptor.indexKey
|
||||||
|
default = descriptor.defaultValue
|
||||||
|
attributes.update(descriptor.attributes)
|
||||||
|
}
|
||||||
|
|
||||||
@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,
|
||||||
@ -102,12 +102,57 @@ public class MetaDescriptorBuilder @PublishedApi internal constructor() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun MetaDescriptorBuilder.item(name: String, block: MetaDescriptorBuilder.() -> Unit): MetaDescriptorBuilder =
|
//public fun MetaDescriptorBuilder.item(name: String, block: MetaDescriptorBuilder.() -> Unit): MetaDescriptorBuilder =
|
||||||
item(Name.parse(name), block)
|
// item(Name.parse(name), block)
|
||||||
|
|
||||||
public inline fun MetaDescriptor(block: MetaDescriptorBuilder.() -> Unit): MetaDescriptor =
|
public inline fun MetaDescriptor(block: MetaDescriptorBuilder.() -> Unit): MetaDescriptor =
|
||||||
MetaDescriptorBuilder().apply(block).build()
|
MetaDescriptorBuilder().apply(block).build()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and configure child node descriptor
|
||||||
|
*/
|
||||||
|
public fun MetaDescriptorBuilder.node(
|
||||||
|
name: Name,
|
||||||
|
block: MetaDescriptorBuilder.() -> Unit,
|
||||||
|
) {
|
||||||
|
node(
|
||||||
|
name,
|
||||||
|
MetaDescriptorBuilder().apply(block)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun MetaDescriptorBuilder.node(name: String, descriptor: MetaDescriptor) {
|
||||||
|
node(Name.parse(name), descriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun MetaDescriptorBuilder.node(name: String, block: MetaDescriptorBuilder.() -> Unit) {
|
||||||
|
node(Name.parse(name), block)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun MetaDescriptorBuilder.node(
|
||||||
|
key: String,
|
||||||
|
base: Described,
|
||||||
|
block: MetaDescriptorBuilder.() -> Unit = {},
|
||||||
|
) {
|
||||||
|
node(Name.parse(key), base.descriptor?.toBuilder()?.apply(block) ?: MetaDescriptorBuilder())
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun MetaDescriptorBuilder.required() {
|
||||||
|
valueRestriction = ValueRestriction.REQUIRED
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MetaDescriptor.toBuilder(): MetaDescriptorBuilder = MetaDescriptorBuilder().apply {
|
||||||
|
description = this@toBuilder.description
|
||||||
|
children = this@toBuilder.nodes.mapValuesTo(LinkedHashMap()) { it.value.toBuilder() }
|
||||||
|
multiple = this@toBuilder.multiple
|
||||||
|
valueRestriction = this@toBuilder.valueRestriction
|
||||||
|
valueTypes = this@toBuilder.valueTypes
|
||||||
|
indexKey = this@toBuilder.indexKey
|
||||||
|
default = defaultValue
|
||||||
|
attributes = this@toBuilder.attributes.toMutableMeta()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create and configure child value descriptor
|
* Create and configure child value descriptor
|
||||||
*/
|
*/
|
||||||
@ -116,7 +161,7 @@ public fun MetaDescriptorBuilder.value(
|
|||||||
type: ValueType,
|
type: ValueType,
|
||||||
vararg additionalTypes: ValueType,
|
vararg additionalTypes: ValueType,
|
||||||
block: MetaDescriptorBuilder.() -> Unit = {},
|
block: MetaDescriptorBuilder.() -> Unit = {},
|
||||||
): MetaDescriptorBuilder = item(name) {
|
): Unit = node(name) {
|
||||||
valueType(type, *additionalTypes)
|
valueType(type, *additionalTypes)
|
||||||
block()
|
block()
|
||||||
}
|
}
|
||||||
@ -126,41 +171,14 @@ public fun MetaDescriptorBuilder.value(
|
|||||||
type: ValueType,
|
type: ValueType,
|
||||||
vararg additionalTypes: ValueType,
|
vararg additionalTypes: ValueType,
|
||||||
block: MetaDescriptorBuilder.() -> Unit = {},
|
block: MetaDescriptorBuilder.() -> Unit = {},
|
||||||
): MetaDescriptorBuilder = value(Name.parse(name), type, additionalTypes = additionalTypes, block)
|
): Unit = value(Name.parse(name), type, additionalTypes = additionalTypes, block)
|
||||||
|
|
||||||
/**
|
|
||||||
* Create and configure child value descriptor
|
|
||||||
*/
|
|
||||||
public fun MetaDescriptorBuilder.node(
|
|
||||||
name: Name, block: MetaDescriptorBuilder.() -> Unit,
|
|
||||||
): MetaDescriptorBuilder = item(name) {
|
|
||||||
valueRestriction = ValueRestriction.ABSENT
|
|
||||||
block()
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun MetaDescriptorBuilder.node(name: String, block: MetaDescriptorBuilder.() -> Unit) {
|
|
||||||
node(Name.parse(name), block)
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun MetaDescriptorBuilder.node(
|
|
||||||
key: String,
|
|
||||||
described: Described,
|
|
||||||
block: MetaDescriptorBuilder.() -> Unit = {},
|
|
||||||
) {
|
|
||||||
described.descriptor?.let {
|
|
||||||
node(Name.parse(key), it, block)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun MetaDescriptorBuilder.required() {
|
|
||||||
valueRestriction = ValueRestriction.REQUIRED
|
|
||||||
}
|
|
||||||
|
|
||||||
public inline fun <reified E : Enum<E>> MetaDescriptorBuilder.enum(
|
public inline fun <reified E : Enum<E>> MetaDescriptorBuilder.enum(
|
||||||
key: Name,
|
key: Name,
|
||||||
default: E?,
|
default: E?,
|
||||||
crossinline modifier: MetaDescriptorBuilder.() -> Unit = {},
|
crossinline modifier: MetaDescriptorBuilder.() -> Unit = {},
|
||||||
): MetaDescriptorBuilder = value(key, ValueType.STRING) {
|
): Unit = value(key, ValueType.STRING) {
|
||||||
default?.let {
|
default?.let {
|
||||||
this.default = default.asValue()
|
this.default = default.asValue()
|
||||||
}
|
}
|
||||||
@ -168,17 +186,6 @@ public inline fun <reified E : Enum<E>> MetaDescriptorBuilder.enum(
|
|||||||
modifier()
|
modifier()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MetaDescriptor.toBuilder(): MetaDescriptorBuilder = MetaDescriptorBuilder().apply {
|
|
||||||
description = this@toBuilder.description
|
|
||||||
children = this@toBuilder.children.mapValuesTo(LinkedHashMap()) { it.value.toBuilder() }
|
|
||||||
multiple = this@toBuilder.multiple
|
|
||||||
valueRestriction = this@toBuilder.valueRestriction
|
|
||||||
valueTypes = this@toBuilder.valueTypes
|
|
||||||
indexKey = this@toBuilder.indexKey
|
|
||||||
default = defaultValue
|
|
||||||
attributes = this@toBuilder.attributes.toMutableMeta()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a deep copy of this descriptor applying given transformation [block]
|
* Make a deep copy of this descriptor applying given transformation [block]
|
||||||
*/
|
*/
|
||||||
|
@ -3,13 +3,15 @@ package space.kscience.dataforge.meta.descriptors
|
|||||||
import space.kscience.dataforge.meta.Scheme
|
import space.kscience.dataforge.meta.Scheme
|
||||||
import space.kscience.dataforge.meta.SchemeSpec
|
import space.kscience.dataforge.meta.SchemeSpec
|
||||||
import space.kscience.dataforge.meta.ValueType
|
import space.kscience.dataforge.meta.ValueType
|
||||||
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import kotlin.reflect.KProperty1
|
import kotlin.reflect.KProperty1
|
||||||
import kotlin.reflect.typeOf
|
import kotlin.reflect.typeOf
|
||||||
|
|
||||||
|
@DFExperimental
|
||||||
public inline fun <S : Scheme, reified T> MetaDescriptorBuilder.value(
|
public inline fun <S : Scheme, reified T> MetaDescriptorBuilder.value(
|
||||||
property: KProperty1<S, T>,
|
property: KProperty1<S, T>,
|
||||||
noinline block: MetaDescriptorBuilder.() -> Unit = {},
|
noinline block: MetaDescriptorBuilder.() -> Unit = {},
|
||||||
): MetaDescriptorBuilder = when (typeOf<T>()) {
|
): Unit = when (typeOf<T>()) {
|
||||||
typeOf<Number>(), typeOf<Int>(), typeOf<Double>(), typeOf<Short>(), typeOf<Long>(), typeOf<Float>() ->
|
typeOf<Number>(), typeOf<Int>(), typeOf<Double>(), typeOf<Short>(), typeOf<Long>(), typeOf<Float>() ->
|
||||||
value(property.name, ValueType.NUMBER) {
|
value(property.name, ValueType.NUMBER) {
|
||||||
block()
|
block()
|
||||||
@ -34,9 +36,10 @@ public inline fun <S : Scheme, reified T> MetaDescriptorBuilder.value(
|
|||||||
multiple = true
|
multiple = true
|
||||||
block()
|
block()
|
||||||
}
|
}
|
||||||
else -> item(property.name, block)
|
else -> node(property.name, block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DFExperimental
|
||||||
public inline fun <S : Scheme, reified T : Scheme> MetaDescriptorBuilder.scheme(
|
public inline fun <S : Scheme, reified T : Scheme> MetaDescriptorBuilder.scheme(
|
||||||
property: KProperty1<S, T>,
|
property: KProperty1<S, T>,
|
||||||
spec: SchemeSpec<T>,
|
spec: SchemeSpec<T>,
|
||||||
|
@ -2,7 +2,7 @@ package space.kscience.dataforge.meta
|
|||||||
|
|
||||||
import kotlinx.serialization.json.*
|
import kotlinx.serialization.json.*
|
||||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||||
import space.kscience.dataforge.meta.descriptors.item
|
import space.kscience.dataforge.meta.descriptors.node
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ class JsonMetaTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val descriptor = MetaDescriptor {
|
val descriptor = MetaDescriptor {
|
||||||
item("nodeArray") {
|
node("nodeArray") {
|
||||||
indexKey = "index"
|
indexKey = "index"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user