diff --git a/dataforge-io/src/commonTest/kotlin/space/kscience/dataforge/io/MetaFormatTest.kt b/dataforge-io/src/commonTest/kotlin/space/kscience/dataforge/io/MetaFormatTest.kt index 143d202f..52154604 100644 --- a/dataforge-io/src/commonTest/kotlin/space/kscience/dataforge/io/MetaFormatTest.kt +++ b/dataforge-io/src/commonTest/kotlin/space/kscience/dataforge/io/MetaFormatTest.kt @@ -7,11 +7,11 @@ import kotlin.test.assertEquals fun Meta.toByteArray(format: MetaFormat = JsonMetaFormat) = ByteArray { - format.writeObject(this@ByteArray, this@toByteArray) + format.writeTo(this@ByteArray, this@toByteArray) } fun MetaFormat.fromByteArray(packet: ByteArray): Meta { - return packet.asBinary().read { readObject(this) } + return packet.asBinary().read { readFrom(this) } } class MetaFormatTest { diff --git a/dataforge-io/src/commonTest/kotlin/space/kscience/dataforge/io/ioTestUtils.kt b/dataforge-io/src/commonTest/kotlin/space/kscience/dataforge/io/ioTestUtils.kt index 4754f682..c07d1cca 100644 --- a/dataforge-io/src/commonTest/kotlin/space/kscience/dataforge/io/ioTestUtils.kt +++ b/dataforge-io/src/commonTest/kotlin/space/kscience/dataforge/io/ioTestUtils.kt @@ -4,8 +4,8 @@ import kotlinx.io.buffered fun IOFormat.writeToByteArray(obj: T): ByteArray = ByteArray { - writeObject(this, obj) + writeTo(this, obj) } fun IOFormat.readFromByteArray(array: ByteArray): T = ByteArraySource(array).buffered().use { - readObject(it) + readFrom(it) } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Meta.kt index 589f02d7..83e4ee0a 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Meta.kt @@ -50,7 +50,9 @@ public interface Meta : MetaRepr, MetaProvider { override fun toMeta(): Meta = this override fun toString(): String + override fun equals(other: Any?): Boolean + override fun hashCode(): Int public companion object { diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt index b8e565f7..c5984c34 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt @@ -11,7 +11,7 @@ import kotlin.js.JsName * Mark a meta builder */ @DslMarker -public annotation class MetaBuilder +public annotation class MetaBuilderMarker /** * A generic interface that gives access to getting and setting meta notes and values @@ -27,7 +27,7 @@ public interface MutableMetaProvider : MetaProvider, MutableValueProvider { * TODO documentation */ @Serializable(MutableMetaSerializer::class) -@MetaBuilder +@MetaBuilderMarker public interface MutableMeta : Meta, MutableMetaProvider { override val items: Map @@ -90,8 +90,8 @@ public interface MutableMeta : Meta, MutableMetaProvider { setMeta(this, repr.toMeta()) } - public infix fun Name.put(mutableMeta: MutableMeta.() -> Unit) { - setMeta(this, Meta(mutableMeta)) + public infix fun Name.put(builder: MutableMeta.() -> Unit) { + getOrCreate(this).apply(builder) } public infix fun String.put(meta: Meta) { @@ -131,7 +131,7 @@ public interface MutableMeta : Meta, MutableMetaProvider { } public infix fun String.put(builder: MutableMeta.() -> Unit) { - setMeta(Name.parse(this), MutableMeta(builder)) + getOrCreate(parseAsName()).apply(builder) } } @@ -381,16 +381,14 @@ public fun Meta.toMutableMeta(): ObservableMutableMeta = MutableMetaImpl(value, public fun Meta.asMutableMeta(): MutableMeta = (this as? MutableMeta) ?: toMutableMeta() -@Suppress("FunctionName") -@JsName("newMutableMeta") -public fun MutableMeta(): ObservableMutableMeta = MutableMetaImpl(null) +@JsName("newObservableMutableMeta") +public fun ObservableMutableMeta(): ObservableMutableMeta = MutableMetaImpl(null) /** * Build a [MutableMeta] using given transformation */ -@Suppress("FunctionName") -public inline fun MutableMeta(builder: MutableMeta.() -> Unit = {}): ObservableMutableMeta = - MutableMeta().apply(builder) +public inline fun ObservableMutableMeta(builder: MutableMeta.() -> Unit = {}): ObservableMutableMeta = + ObservableMutableMeta().apply(builder) /** diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMetaWrapper.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMetaWrapper.kt index d244e8d3..e231fbc5 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMetaWrapper.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMetaWrapper.kt @@ -51,6 +51,8 @@ private class ObservableMetaWrapper( override fun setMeta(name: Name, node: Meta?) { val oldMeta = get(name) + //don't forget to remove listener + oldMeta?.removeListener(this) root.setMeta(absoluteName + name, node) if (oldMeta != node) { invalidate(name) diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/SealedMeta.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/SealedMeta.kt index 6c8ab3e9..05626368 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/SealedMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/SealedMeta.kt @@ -1,16 +1,17 @@ package space.kscience.dataforge.meta import kotlinx.serialization.Serializable -import space.kscience.dataforge.names.NameToken +import space.kscience.dataforge.names.* +import kotlin.js.JsName /** * The meta implementation which is guaranteed to be immutable. * */ @Serializable -public class SealedMeta internal constructor( +public class SealedMeta( override val value: Value?, - override val items: Map + override val items: Map, ) : TypedMeta { override fun toString(): String = Meta.toString(this) override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta) @@ -26,7 +27,7 @@ public class SealedMeta internal constructor( } /** - * Generate sealed node from [this]. If it is already sealed return it as is. + * Generate sealed node from [this]. If it is already sealed, return it as is. */ public fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta( value, @@ -47,7 +48,79 @@ public fun Meta(value: String): SealedMeta = Meta(value.asValue()) @Suppress("FunctionName") public fun Meta(value: Boolean): SealedMeta = Meta(value.asValue()) -@Suppress("FunctionName") -public inline fun Meta(builder: MutableMeta.() -> Unit): SealedMeta = - MutableMeta(builder).seal() +/** + * A lightweight mutable meta used to create [SealedMeta] instances without bothering with + */ +@PublishedApi +internal class MetaBuilder( + override var value: Value? = null, + override val items: MutableMap = hashMapOf(), +) : MutableMeta { + + override fun getOrCreate(name: Name): MetaBuilder { + val existing = get(name) as? MetaBuilder + return if (existing == null) { + val newItem = MetaBuilder() + setMeta(name, newItem) + newItem + } else { + existing + } + } + + private fun wrap(meta: Meta): MetaBuilder = meta as? MetaBuilder ?: MetaBuilder( + meta.value, + meta.items.mapValuesTo(hashMapOf()) { wrap(it.value) } + ) + + + override fun setMeta(name: Name, node: Meta?) { + when (name.length) { + 0 -> error("Can't set a meta with empty name") + 1 -> { + val token = name.first() + //remove child and invalidate if argument is null + if (node == null) { + items.remove(token) + } else { + items[token] = wrap(node) + } + } + + else -> { + getOrCreate(name.first().asName()).setMeta(name.cutFirst(), node) + } + } + } + + 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) +} + +/** + * Create a read-only meta. + */ +public inline fun Meta(builder: MutableMeta.() -> Unit): Meta = + MetaBuilder().apply(builder).seal() + +/** + * Create an immutable meta. + */ +public inline fun SealedMeta(builder: MutableMeta.() -> Unit): SealedMeta = + MetaBuilder().apply(builder).seal() + +/** + * Create an empty meta mutable meta. + */ +@JsName("newMutableMeta") +public fun MutableMeta(): MutableMeta = MetaBuilder() + +/** + * Create a mutable meta with given builder. + */ +public inline fun MutableMeta(builder: MutableMeta.() -> Unit = {}): MutableMeta = + MutableMeta().apply(builder) diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/transformations/MetaTransformation.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/transformations/MetaTransformation.kt index b54f486a..be35106b 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/transformations/MetaTransformation.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/transformations/MetaTransformation.kt @@ -105,7 +105,7 @@ public value class MetaTransformation(private val transformations: Collection rule.selectItems(source).forEach { name -> rule.transformItem(name, source[name], this) diff --git a/dataforge-meta/src/jvmTest/kotlin/space/kscience/dataforge/meta/JvmMutableMetaTest.kt b/dataforge-meta/src/jvmTest/kotlin/space/kscience/dataforge/meta/JvmMutableMetaTest.kt deleted file mode 100644 index 075a9e45..00000000 --- a/dataforge-meta/src/jvmTest/kotlin/space/kscience/dataforge/meta/JvmMutableMetaTest.kt +++ /dev/null @@ -1,15 +0,0 @@ -package space.kscience.dataforge.meta - -import org.junit.jupiter.api.Test -import kotlin.test.assertFails - -class JvmMutableMetaTest { - @Test - fun recursiveMeta(){ - val meta = MutableMeta { - "a" put 2 - } - - assertFails { meta["child.a"] = meta } - } -} \ No newline at end of file