Compare commits
No commits in common. "7fa6617e7e85d341d7740cc252e46753cfd4c573" and "c62dc2c698ad802e38542c2490f9996d4944ae9f" have entirely different histories.
7fa6617e7e
...
c62dc2c698
@ -5,16 +5,12 @@
|
|||||||
### Added
|
### Added
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Simplify inheritance logic in `MutableTypedMeta`
|
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
- MetaProvider `spec` is replaced by `readable`. `listOfSpec` replaced with `listOfReadable`
|
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fixed NameToken parsing.
|
|
||||||
- Top level string list meta conversion.
|
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ plugins {
|
|||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
group = "space.kscience"
|
group = "space.kscience"
|
||||||
version = "0.9.1-dev-1"
|
version = "0.9.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
package space.kscience.dataforge.data
|
package space.kscience.dataforge.data
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.flow.take
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import space.kscience.dataforge.actions.Action
|
import space.kscience.dataforge.actions.Action
|
||||||
import space.kscience.dataforge.actions.invoke
|
import space.kscience.dataforge.actions.invoke
|
||||||
@ -13,28 +16,27 @@ import kotlin.time.Duration.Companion.milliseconds
|
|||||||
internal class ActionsTest {
|
internal class ActionsTest {
|
||||||
@Test
|
@Test
|
||||||
fun testStaticMapAction() = runTest(timeout = 500.milliseconds) {
|
fun testStaticMapAction() = runTest(timeout = 500.milliseconds) {
|
||||||
val plusOne = Action.mapping<Int, Int> {
|
|
||||||
result { it + 1 }
|
|
||||||
}
|
|
||||||
|
|
||||||
val data: DataTree<Int> = DataTree {
|
val data: DataTree<Int> = DataTree {
|
||||||
repeat(10) {
|
repeat(10) {
|
||||||
putValue(it.toString(), it)
|
putValue(it.toString(), it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val plusOne = Action.mapping<Int, Int> {
|
||||||
|
result { it + 1 }
|
||||||
|
}
|
||||||
val result = plusOne(data)
|
val result = plusOne(data)
|
||||||
assertEquals(2, result["1"]?.await())
|
assertEquals(2, result["1"]?.await())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testDynamicMapAction() = runTest(timeout = 500.milliseconds) {
|
fun testDynamicMapAction() = runTest(timeout = 500.milliseconds) {
|
||||||
|
val source: MutableDataTree<Int> = MutableDataTree()
|
||||||
|
|
||||||
val plusOne = Action.mapping<Int, Int> {
|
val plusOne = Action.mapping<Int, Int> {
|
||||||
result { it + 1 }
|
result { it + 1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
val source: MutableDataTree<Int> = MutableDataTree()
|
|
||||||
|
|
||||||
val result = plusOne(source)
|
val result = plusOne(source)
|
||||||
|
|
||||||
|
|
||||||
@ -42,7 +44,7 @@ internal class ActionsTest {
|
|||||||
source.updateValue(it.toString(), it)
|
source.updateValue(it.toString(), it)
|
||||||
}
|
}
|
||||||
|
|
||||||
// result.updates.take(10).onEach { println(it.name) }.collect()
|
result.updates.take(10).onEach { println(it.name) }.collect()
|
||||||
|
|
||||||
assertEquals(2, result["1"]?.await())
|
assertEquals(2, result["1"]?.await())
|
||||||
}
|
}
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id("space.kscience.gradle.mpp")
|
|
||||||
id("com.squareup.wire") version "4.9.9"
|
|
||||||
}
|
|
||||||
|
|
||||||
description = "ProtoBuf meta IO"
|
|
||||||
|
|
||||||
kscience {
|
|
||||||
jvm()
|
|
||||||
dependencies {
|
|
||||||
api(projects.dataforgeIo)
|
|
||||||
api("com.squareup.wire:wire-runtime:4.9.9")
|
|
||||||
}
|
|
||||||
useSerialization {
|
|
||||||
protobuf()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wire {
|
|
||||||
kotlin {
|
|
||||||
sourcePath {
|
|
||||||
srcDir("src/commonMain/proto")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
readme {
|
|
||||||
maturity = space.kscience.gradle.Maturity.PROTOTYPE
|
|
||||||
description = """
|
|
||||||
ProtoBuf Meta representation
|
|
||||||
""".trimIndent()
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
package space.kscience.dataforge.io.proto;
|
|
||||||
|
|
||||||
message ProtoMeta {
|
|
||||||
message ProtoValue {
|
|
||||||
oneof value {
|
|
||||||
string stringValue = 2;
|
|
||||||
bool booleanValue = 3;
|
|
||||||
double doubleValue = 4;
|
|
||||||
float floatValue = 5;
|
|
||||||
int32 int32Value = 6;
|
|
||||||
int64 int64Value = 7;
|
|
||||||
bytes bytesValue = 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
repeated ProtoValue value = 1;
|
|
||||||
|
|
||||||
map<string, ProtoMeta> items = 2;
|
|
||||||
}
|
|
@ -1,92 +0,0 @@
|
|||||||
package space.kscience.dataforge.io.proto
|
|
||||||
|
|
||||||
import kotlinx.io.Sink
|
|
||||||
import kotlinx.io.Source
|
|
||||||
import kotlinx.io.asInputStream
|
|
||||||
import kotlinx.io.asOutputStream
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import space.kscience.dataforge.io.MetaFormat
|
|
||||||
import space.kscience.dataforge.meta.*
|
|
||||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
|
||||||
import space.kscience.dataforge.names.NameToken
|
|
||||||
|
|
||||||
internal class ProtoMetaWrapper(private val proto: ProtoMeta) : Meta {
|
|
||||||
|
|
||||||
private fun ProtoMeta.ProtoValue.toValue(): Value = when {
|
|
||||||
stringValue != null -> stringValue.asValue()
|
|
||||||
booleanValue != null -> booleanValue.asValue()
|
|
||||||
doubleValue != null -> doubleValue.asValue()
|
|
||||||
floatValue != null -> floatValue.asValue()
|
|
||||||
int32Value != null -> int32Value.asValue()
|
|
||||||
int64Value != null -> int64Value.asValue()
|
|
||||||
bytesValue != null -> bytesValue.toByteArray().asValue()
|
|
||||||
else -> Null
|
|
||||||
}
|
|
||||||
|
|
||||||
override val value: Value?
|
|
||||||
get() = when (proto.value_.size) {
|
|
||||||
0 -> null
|
|
||||||
1 -> proto.value_[0].toValue()
|
|
||||||
else -> proto.value_.map { it.toValue() }.asValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override val items: Map<NameToken, Meta>
|
|
||||||
get() = proto.items.entries.associate { NameToken.parse(it.key) to ProtoMetaWrapper(it.value) }
|
|
||||||
|
|
||||||
override fun toString(): String = Meta.toString(this)
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
|
|
||||||
|
|
||||||
override fun hashCode(): Int = Meta.hashCode(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun Meta.toProto(): ProtoMeta {
|
|
||||||
|
|
||||||
|
|
||||||
fun MutableList<ProtoMeta.ProtoValue>.appendProtoValues(value: Value): Unit {
|
|
||||||
when (value.type) {
|
|
||||||
ValueType.NULL -> {
|
|
||||||
//do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
ValueType.NUMBER -> when (value.value) {
|
|
||||||
is Int, is Short, is Byte -> add(ProtoMeta.ProtoValue(int32Value = value.int))
|
|
||||||
is Long -> add(ProtoMeta.ProtoValue(int64Value = value.long))
|
|
||||||
is Float -> add(ProtoMeta.ProtoValue(floatValue = value.float))
|
|
||||||
else -> {
|
|
||||||
LoggerFactory.getLogger(ProtoMeta::class.java)
|
|
||||||
.warn("Unknown number type ${value.value} encoded as Double")
|
|
||||||
add(ProtoMeta.ProtoValue(doubleValue = value.double))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ValueType.STRING -> add(ProtoMeta.ProtoValue(stringValue = value.string))
|
|
||||||
ValueType.BOOLEAN -> add(ProtoMeta.ProtoValue(booleanValue = value.boolean))
|
|
||||||
ValueType.LIST -> {
|
|
||||||
value.list.forEach {
|
|
||||||
if (it.type == ValueType.LIST) {
|
|
||||||
error("Nested lists are not supported")
|
|
||||||
} else {
|
|
||||||
appendProtoValues(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ProtoMeta(
|
|
||||||
value_ = buildList { value?.let { appendProtoValues(it) } },
|
|
||||||
items.entries.associate { it.key.toString() to it.value.toProto() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public object ProtoMetaFormat : MetaFormat {
|
|
||||||
override fun writeMeta(sink: Sink, meta: Meta, descriptor: MetaDescriptor?) {
|
|
||||||
ProtoMeta.ADAPTER.encode(sink.asOutputStream(), meta.toProto())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun readMeta(source: Source, descriptor: MetaDescriptor?): Meta =
|
|
||||||
ProtoMetaWrapper(ProtoMeta.ADAPTER.decode(source.asInputStream()))
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package space.kscience.dataforge.io.proto
|
|
||||||
|
|
||||||
import space.kscience.dataforge.meta.Meta
|
|
||||||
import space.kscience.dataforge.meta.get
|
|
||||||
import kotlin.test.Test
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
|
|
||||||
class ProtoBufTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testProtoBufMetaFormat(){
|
|
||||||
val meta = Meta {
|
|
||||||
"a" put 22
|
|
||||||
"node" put {
|
|
||||||
"b" put "DDD"
|
|
||||||
"c" put 11.1
|
|
||||||
"d" put {
|
|
||||||
"d1" put {
|
|
||||||
"d11" put "aaa"
|
|
||||||
"d12" put "bbb"
|
|
||||||
}
|
|
||||||
"d2" put 2
|
|
||||||
}
|
|
||||||
"array" put doubleArrayOf(1.0, 2.0, 3.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val buffer = kotlinx.io.Buffer()
|
|
||||||
ProtoMetaFormat.writeTo(buffer,meta)
|
|
||||||
val result = ProtoMetaFormat.readFrom(buffer)
|
|
||||||
|
|
||||||
println(result["a"]?.value)
|
|
||||||
|
|
||||||
meta.items.keys.forEach {
|
|
||||||
assertEquals(meta[it],result[it],"${meta[it]} != ${result[it]}")
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(meta, result)
|
|
||||||
}
|
|
||||||
}
|
|
@ -117,11 +117,8 @@ private fun MutableMap<NameToken, SealedMeta>.addJsonElement(
|
|||||||
} else {
|
} else {
|
||||||
val indexKey = descriptor?.indexKey ?: Meta.INDEX_KEY
|
val indexKey = descriptor?.indexKey ?: Meta.INDEX_KEY
|
||||||
element.forEachIndexed { serial, childElement ->
|
element.forEachIndexed { serial, childElement ->
|
||||||
|
val index = (childElement as? JsonObject)?.get(indexKey)?.jsonPrimitive?.content
|
||||||
val index = (childElement as? JsonObject)
|
|
||||||
?.get(indexKey)?.jsonPrimitive?.content
|
|
||||||
?: serial.toString()
|
?: serial.toString()
|
||||||
|
|
||||||
val child: SealedMeta = when (childElement) {
|
val child: SealedMeta = when (childElement) {
|
||||||
is JsonObject -> childElement.toMeta(descriptor)
|
is JsonObject -> childElement.toMeta(descriptor)
|
||||||
is JsonArray -> {
|
is JsonArray -> {
|
||||||
@ -163,15 +160,11 @@ public fun JsonObject.toMeta(descriptor: MetaDescriptor? = null): SealedMeta {
|
|||||||
public fun JsonElement.toMeta(descriptor: MetaDescriptor? = null): SealedMeta = when (this) {
|
public fun JsonElement.toMeta(descriptor: MetaDescriptor? = null): SealedMeta = when (this) {
|
||||||
is JsonPrimitive -> Meta(toValue(descriptor))
|
is JsonPrimitive -> Meta(toValue(descriptor))
|
||||||
is JsonObject -> toMeta(descriptor)
|
is JsonObject -> toMeta(descriptor)
|
||||||
is JsonArray -> if (any { it is JsonObject }) {
|
is JsonArray -> SealedMeta(null,
|
||||||
SealedMeta(null,
|
|
||||||
linkedMapOf<NameToken, SealedMeta>().apply {
|
linkedMapOf<NameToken, SealedMeta>().apply {
|
||||||
addJsonElement(Meta.JSON_ARRAY_KEY, this@toMeta, null)
|
addJsonElement(Meta.JSON_ARRAY_KEY, this@toMeta, null)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
} else{
|
|
||||||
Meta(map { it.toValueOrNull(descriptor) ?: kotlin.error("Unreachable: should not contain objects") }.asValue())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -116,12 +116,6 @@ public interface MetaConverter<T> : MetaReader<T> {
|
|||||||
override fun convert(obj: E): Meta = Meta(obj.asValue())
|
override fun convert(obj: E): Meta = Meta(obj.asValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
public val stringList: MetaConverter<List<String>> = object : MetaConverter<List<String>> {
|
|
||||||
override fun convert(obj: List<String>): Meta = Meta(obj.map { it.asValue() }.asValue())
|
|
||||||
|
|
||||||
override fun readOrNull(source: Meta): List<String>? = source.stringList
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun <T> valueList(
|
public fun <T> valueList(
|
||||||
writer: (T) -> Value = { Value.of(it) },
|
writer: (T) -> Value = { Value.of(it) },
|
||||||
reader: (Value) -> T,
|
reader: (Value) -> T,
|
||||||
|
@ -24,45 +24,20 @@ public fun MetaProvider.node(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use [reader] to read the Meta node
|
* Use [metaReader] to read the Meta node
|
||||||
*/
|
*/
|
||||||
public fun <T> MetaProvider.readable(
|
public fun <T> MetaProvider.spec(
|
||||||
reader: MetaReader<T>,
|
metaReader: MetaReader<T>,
|
||||||
key: Name? = null,
|
key: Name? = null,
|
||||||
): MetaDelegate<T?> = object : MetaDelegate<T?> {
|
): MetaDelegate<T?> = object : MetaDelegate<T?> {
|
||||||
override val descriptor: MetaDescriptor? get() = reader.descriptor
|
override val descriptor: MetaDescriptor? get() = metaReader.descriptor
|
||||||
|
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
|
override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
|
||||||
return get(key ?: property.name.asName())?.let { reader.read(it) }
|
return get(key ?: property.name.asName())?.let { metaReader.read(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Use [reader] to read the Meta node or return [default] if node does not exist
|
|
||||||
*/
|
|
||||||
public fun <T> MetaProvider.readable(
|
|
||||||
reader: MetaReader<T>,
|
|
||||||
default: T,
|
|
||||||
key: Name? = null,
|
|
||||||
): MetaDelegate<T> = object : MetaDelegate<T> {
|
|
||||||
override val descriptor: MetaDescriptor? get() = reader.descriptor
|
|
||||||
|
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
|
||||||
return get(key ?: property.name.asName())?.let { reader.read(it) } ?: default
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use [reader] to read the Meta node
|
|
||||||
*/
|
|
||||||
@Deprecated("Replace with reading", ReplaceWith("reading(metaReader, key)"))
|
|
||||||
public fun <T> MetaProvider.spec(
|
|
||||||
reader: MetaReader<T>,
|
|
||||||
key: Name? = null,
|
|
||||||
): MetaDelegate<T?> = readable(reader, key)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use object serializer to transform it to Meta and back
|
* Use object serializer to transform it to Meta and back
|
||||||
*/
|
*/
|
||||||
@ -70,51 +45,34 @@ public fun <T> MetaProvider.spec(
|
|||||||
public inline fun <reified T> MetaProvider.serializable(
|
public inline fun <reified T> MetaProvider.serializable(
|
||||||
key: Name? = null,
|
key: Name? = null,
|
||||||
descriptor: MetaDescriptor? = null,
|
descriptor: MetaDescriptor? = null,
|
||||||
): MetaDelegate<T?> = readable(MetaConverter.serializable(descriptor), key)
|
): MetaDelegate<T?> = spec(MetaConverter.serializable(descriptor), key)
|
||||||
|
|
||||||
@DFExperimental
|
|
||||||
public inline fun <reified T> MetaProvider.serializable(
|
|
||||||
key: Name? = null,
|
|
||||||
default: T,
|
|
||||||
descriptor: MetaDescriptor? = null,
|
|
||||||
): MetaDelegate<T> = readable(MetaConverter.serializable(descriptor), default, 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: MetaReader<T>,
|
converter: MetaReader<T>,
|
||||||
): ReadOnlyProperty<Any?, T?> = readable(converter, key)
|
): ReadOnlyProperty<Any?, T?> = spec(converter, key)
|
||||||
|
|
||||||
/**
|
|
||||||
* Use [reader] to convert a list of same name siblings meta to object
|
|
||||||
*/
|
|
||||||
public fun <T> Meta.listOfReadable(
|
|
||||||
reader: MetaReader<T>,
|
|
||||||
key: Name? = null,
|
|
||||||
): MetaDelegate<List<T>> = object : MetaDelegate<List<T>> {
|
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): List<T> {
|
|
||||||
val name = key ?: property.name.asName()
|
|
||||||
return getIndexed(name).values.map { reader.read(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override val descriptor: MetaDescriptor? = reader.descriptor?.copy(multiple = true)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
*/
|
*/
|
||||||
@Deprecated("Replace with readingList", ReplaceWith("readingList(converter, key)"))
|
|
||||||
public fun <T> Meta.listOfSpec(
|
public fun <T> Meta.listOfSpec(
|
||||||
converter: MetaReader<T>,
|
converter: MetaReader<T>,
|
||||||
key: Name? = null,
|
key: Name? = null,
|
||||||
): MetaDelegate<List<T>> = listOfReadable(converter, key)
|
): MetaDelegate<List<T>> = object : MetaDelegate<List<T>> {
|
||||||
|
override fun getValue(thisRef: Any?, property: KProperty<*>): List<T> {
|
||||||
|
val name = key ?: property.name.asName()
|
||||||
|
return getIndexed(name).values.map { converter.read(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override val descriptor: MetaDescriptor? = converter.descriptor?.copy(multiple = true)
|
||||||
|
}
|
||||||
|
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
public inline fun <reified T> Meta.listOfSerializable(
|
public inline fun <reified T> Meta.listOfSerializable(
|
||||||
key: Name? = null,
|
key: Name? = null,
|
||||||
descriptor: MetaDescriptor? = null,
|
descriptor: MetaDescriptor? = null,
|
||||||
): MetaDelegate<List<T>> = listOfReadable(MetaConverter.serializable(descriptor), key)
|
): MetaDelegate<List<T>> = listOfSpec(MetaConverter.serializable(descriptor), key)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A property delegate that uses custom key
|
* A property delegate that uses custom key
|
||||||
|
@ -159,17 +159,7 @@ public interface MutableTypedMeta<M : MutableTypedMeta<M>> : TypedMeta<M>, Mutab
|
|||||||
*/
|
*/
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
public fun attach(name: Name, node: M)
|
public fun attach(name: Name, node: M)
|
||||||
|
override fun get(name: Name): M?
|
||||||
override fun get(name: Name): M? {
|
|
||||||
tailrec fun M.find(name: Name): M? = if (name.isEmpty()) {
|
|
||||||
self
|
|
||||||
} else {
|
|
||||||
items[name.firstOrNull()!!]?.find(name.cutFirst())
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.find(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getOrCreate(name: Name): M
|
override fun getOrCreate(name: Name): M
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,25 +54,9 @@ public fun <T> MutableMetaProvider.convertable(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun <T> MutableMetaProvider.convertable(
|
@Deprecated("Use convertable", ReplaceWith("convertable(converter, key)"))
|
||||||
converter: MetaConverter<T>,
|
public fun <T> MutableMetaProvider.node(key: Name? = null, converter: MetaConverter<T>): MutableMetaDelegate<T?> =
|
||||||
default: T,
|
convertable(converter, key)
|
||||||
key: Name? = null,
|
|
||||||
): MutableMetaDelegate<T> = object : MutableMetaDelegate<T> {
|
|
||||||
|
|
||||||
override val descriptor: MetaDescriptor? get() = converter.descriptor
|
|
||||||
|
|
||||||
|
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
|
||||||
val name = key ?: property.name.asName()
|
|
||||||
return get(name)?.let { converter.read(it) } ?: default
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
|
||||||
val name = key ?: property.name.asName()
|
|
||||||
set(name, value?.let { converter.convert(it) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use object serializer to transform it to Meta and back.
|
* Use object serializer to transform it to Meta and back.
|
||||||
@ -82,14 +66,7 @@ public fun <T> MutableMetaProvider.convertable(
|
|||||||
public inline fun <reified T> MutableMetaProvider.serializable(
|
public inline fun <reified T> MutableMetaProvider.serializable(
|
||||||
descriptor: MetaDescriptor? = null,
|
descriptor: MetaDescriptor? = null,
|
||||||
key: Name? = null,
|
key: Name? = null,
|
||||||
): MutableMetaDelegate<T?> = convertable<T>(MetaConverter.serializable(descriptor), key)
|
): MutableMetaDelegate<T?> = convertable(MetaConverter.serializable(descriptor), key)
|
||||||
|
|
||||||
@DFExperimental
|
|
||||||
public inline fun <reified T> MutableMetaProvider.serializable(
|
|
||||||
descriptor: MetaDescriptor? = null,
|
|
||||||
default: T,
|
|
||||||
key: Name? = null,
|
|
||||||
): MutableMetaDelegate<T> = convertable(MetaConverter.serializable(descriptor), default, key)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use [converter] to convert a list of same name siblings meta to object and back.
|
* Use [converter] to convert a list of same name siblings meta to object and back.
|
||||||
|
@ -2,6 +2,9 @@ package space.kscience.dataforge.meta
|
|||||||
|
|
||||||
import space.kscience.dataforge.misc.ThreadSafe
|
import space.kscience.dataforge.misc.ThreadSafe
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
|
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(
|
||||||
@ -36,7 +39,20 @@ public interface ObservableMeta : Meta {
|
|||||||
* A [Meta] which is both observable and mutable
|
* A [Meta] which is both observable and mutable
|
||||||
*/
|
*/
|
||||||
public interface ObservableMutableMeta : ObservableMeta, MutableMeta, MutableTypedMeta<ObservableMutableMeta> {
|
public interface ObservableMutableMeta : ObservableMeta, MutableMeta, MutableTypedMeta<ObservableMutableMeta> {
|
||||||
|
|
||||||
override val self: ObservableMutableMeta get() = this
|
override val self: ObservableMutableMeta get() = this
|
||||||
|
|
||||||
|
override fun getOrCreate(name: Name): ObservableMutableMeta
|
||||||
|
|
||||||
|
override fun get(name: Name): ObservableMutableMeta? {
|
||||||
|
tailrec fun ObservableMutableMeta.find(name: Name): ObservableMutableMeta? = if (name.isEmpty()) {
|
||||||
|
this
|
||||||
|
} else {
|
||||||
|
items[name.firstOrNull()!!]?.find(name.cutFirst())
|
||||||
|
}
|
||||||
|
|
||||||
|
return find(name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal abstract class AbstractObservableMeta : ObservableMeta {
|
internal abstract class AbstractObservableMeta : ObservableMeta {
|
||||||
|
@ -221,14 +221,13 @@ public fun <T : Scheme> Configurable.updateWith(
|
|||||||
/**
|
/**
|
||||||
* A delegate that uses a [MetaReader] to wrap a child of this provider
|
* A delegate that uses a [MetaReader] to wrap a child of this provider
|
||||||
*/
|
*/
|
||||||
public fun <T : Scheme> MutableMetaProvider.scheme(
|
public fun <T : Scheme> MutableMeta.scheme(
|
||||||
spec: SchemeSpec<T>,
|
spec: SchemeSpec<T>,
|
||||||
key: Name? = null,
|
key: Name? = null,
|
||||||
): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> {
|
): 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()
|
||||||
val node = get(name)?: MutableMeta().also { set(name,it) }
|
return spec.write(getOrCreate(name))
|
||||||
return spec.write(node)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||||
|
@ -67,29 +67,10 @@ public class NameToken(public val body: String, public val index: String? = null
|
|||||||
* Parse name token from a string
|
* Parse name token from a string
|
||||||
*/
|
*/
|
||||||
public fun parse(string: String): NameToken {
|
public fun parse(string: String): NameToken {
|
||||||
var indexStart = -1
|
val body = string.substringBefore('[')
|
||||||
var indexEnd = -1
|
val index = string.substringAfter('[', "")
|
||||||
string.forEachIndexed { index, c ->
|
if (index.isNotEmpty() && !index.endsWith(']')) error("NameToken with index must end with ']'")
|
||||||
when (c) {
|
return NameToken(body, index.removeSuffix("]"))
|
||||||
'[' -> when {
|
|
||||||
indexStart >= 0 -> error("Second opening bracket not allowed in NameToken: $string")
|
|
||||||
else -> indexStart = index
|
|
||||||
}
|
|
||||||
|
|
||||||
']' -> when {
|
|
||||||
indexStart < 0 -> error("Closing index bracket could not be used before opening bracket in NameToken: $string")
|
|
||||||
indexEnd >= 0 -> error("Second closing bracket not allowed in NameToken: $string")
|
|
||||||
else -> indexEnd = index
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> if(indexEnd>=0) error("Symbols not allowed after index in NameToken: $string")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(indexStart>=0 && indexEnd<0) error("Opening bracket without closing bracket not allowed in NameToken: $string")
|
|
||||||
return NameToken(
|
|
||||||
if(indexStart>=0) string.substring(0, indexStart) else string,
|
|
||||||
if(indexStart>=0) string.substring(indexStart + 1, indexEnd) else null
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
package space.kscience.dataforge.meta
|
|
||||||
|
|
||||||
import kotlin.test.Test
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
|
|
||||||
class ConvertersTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun stringListConversion() {
|
|
||||||
val list = listOf("A", "B", "C")
|
|
||||||
val meta = MetaConverter.stringList.convert(list)
|
|
||||||
val json = meta.toJson()
|
|
||||||
val reconstructedMeta = json.toMeta()
|
|
||||||
val reconstructed = MetaConverter.stringList.read(reconstructedMeta)
|
|
||||||
assertEquals(list,reconstructed)
|
|
||||||
}
|
|
||||||
}
|
|
@ -56,22 +56,10 @@ class NameTest {
|
|||||||
|
|
||||||
val token2 = NameToken.parse("token-body")
|
val token2 = NameToken.parse("token-body")
|
||||||
assertEquals("token-body", token2.body)
|
assertEquals("token-body", token2.body)
|
||||||
assertEquals(null, token2.index)
|
assertEquals("", token2.index)
|
||||||
|
|
||||||
// val token3 = NameToken.parse("[token-index]")
|
|
||||||
// assertEquals("", token3.body)
|
|
||||||
// assertEquals("token-index", token3.index)
|
|
||||||
|
|
||||||
assertFails{
|
|
||||||
NameToken.parse("[token-index]")
|
|
||||||
}
|
|
||||||
|
|
||||||
assertFails {
|
assertFails {
|
||||||
NameToken.parse("token[22")
|
NameToken.parse("token[22")
|
||||||
}
|
}
|
||||||
|
|
||||||
assertFails {
|
|
||||||
NameToken.parse("token[22]ddd")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -43,7 +43,6 @@ include(
|
|||||||
":dataforge-meta",
|
":dataforge-meta",
|
||||||
":dataforge-io",
|
":dataforge-io",
|
||||||
":dataforge-io:dataforge-io-yaml",
|
":dataforge-io:dataforge-io-yaml",
|
||||||
":dataforge-io:dataforge-io-proto",
|
|
||||||
":dataforge-context",
|
":dataforge-context",
|
||||||
":dataforge-data",
|
":dataforge-data",
|
||||||
":dataforge-workspace",
|
":dataforge-workspace",
|
||||||
|
Loading…
Reference in New Issue
Block a user