diff --git a/CHANGELOG.md b/CHANGELOG.md index ef953bb9..c91b1e81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,11 @@ ## Unreleased ### Added +- Relax requirements for `withDefault` and `view`. Now they could be used with `MutableMetaProvider` +- Add `MutableMetaProvider` to `MutableMeta` converter `asMutableMeta`. ### Changed +- Implementation of `Meta::withDefault` ### Deprecated 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 fd953085..bb9b90f6 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 @@ -249,11 +249,26 @@ public inline fun <reified E : Enum<E>> Meta?.enum(): E? = this?.value?.let { public val Meta?.stringList: List<String>? get() = this?.value?.list?.map { it.string } /** - * Create a provider that uses given provider for default values if those are not found in this provider + * Create a provider that uses a given provider for default values if those are not found in this provider */ -public fun Meta.withDefault(default: MetaProvider?): Meta = if (default == null) { +public fun MetaProvider.withDefault(default: MetaProvider?): Meta = if (default == null && this is Meta) { this } else { - //TODO optimize - toMutableMeta().withDefault(default) + object : Meta { + override val value: Value? + get() = this@withDefault.getValue(Name.EMPTY) ?: default?.getValue(Name.EMPTY) + + override val items: Map<NameToken, Meta> + get() = buildMap { + default?.get(Name.EMPTY)?.items?.let { putAll(it) } + this@withDefault.get(Name.EMPTY)?.items?.let { putAll(it) } + } + + 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) + + } } \ No newline at end of file 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 a11a5a53..f1084692 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 @@ -37,7 +37,7 @@ public interface MutableMeta : Meta, MutableMetaProvider { override val items: Map<NameToken, MutableMeta> /** - * Get or set value of this node + * Get or set the value of this node */ override var value: Value? @@ -437,7 +437,7 @@ public inline fun Meta.copy(modification: MutableMeta.() -> Unit = {}): Meta = M } private class MutableMetaWithDefault( - val source: MutableMeta, val default: MetaProvider, val rootName: Name, + val source: MutableMetaProvider, val default: MetaProvider, val rootName: Name, ) : MutableMeta { override val items: Map<NameToken, MutableMeta> get() { @@ -474,9 +474,20 @@ private class MutableMetaWithDefault( } /** - * Create a mutable item provider that uses given provider for default values if those are not found in this provider. + * Create a mutable item provider that uses a given provider for default values if those are not found in this provider. * Changes are propagated only to this provider. */ -public fun MutableMeta.withDefault(default: MetaProvider?): MutableMeta = if (default == null) { - this -} else MutableMetaWithDefault(this, default, Name.EMPTY) +public fun MutableMetaProvider.withDefault( + default: MetaProvider? +): MutableMeta = if (default == null) { + (this as? MutableMeta) ?: asMutableMeta() +} else { + MutableMetaWithDefault(this, default, Name.EMPTY) +} + +/** + * Use multiple default providers. Providers are queried in order of appearance. The first non-null value is returned + */ +public fun MutableMetaProvider.withDefaults(vararg defaults: MetaProvider): MutableMeta = withDefault { name -> + defaults.firstNotNullOfOrNull { it[name] } +} \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMetaView.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMetaView.kt index 2bc3f9aa..81a87ef9 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMetaView.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMetaView.kt @@ -9,7 +9,7 @@ import space.kscience.dataforge.names.plus * A [Meta] child proxy that creates required nodes on value write */ private class MutableMetaView( - val origin: MutableMeta, + val origin: MutableMetaProvider, val path: Name ) : MutableMeta { @@ -42,6 +42,11 @@ private class MutableMetaView( * The difference between this method and regular [getOrCreate] is that [getOrCreate] always creates and attaches node * even if it is empty. */ -public fun MutableMeta.view(name: Name): MutableMeta = MutableMetaView(this, name) +public fun MutableMetaProvider.view(name: Name): MutableMeta = MutableMetaView(this, name) -public fun MutableMeta.view(name: String): MutableMeta = view(name.parseAsName()) \ No newline at end of file +public fun MutableMetaProvider.view(name: String): MutableMeta = view(name.parseAsName()) + +/** + * Create a view of root node, thus effectively representing [MutableMetaProvider] as [MutableMeta] + */ +public fun MutableMetaProvider.asMutableMeta(): MutableMeta = view(Name.EMPTY) \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/valueExtensions.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/valueExtensions.kt index 0c87bcc2..e84d7da0 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/valueExtensions.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/valueExtensions.kt @@ -6,7 +6,7 @@ package space.kscience.dataforge.meta public fun Value.isNull(): Boolean = this == Null /** - * Check if value is list. + * Check if the value is a list. */ public fun Value.isList(): Boolean = this.type == ValueType.LIST