From 81375d4644ed94f716b7a51c31ef7f9f9713786a Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 25 Dec 2020 19:27:58 +0300 Subject: [PATCH] Separate ReadOnlySpecification. Cleanup Scheme inflation --- dataforge-meta/api/dataforge-meta.api | 39 +++++++------- .../hep/dataforge/meta/MutableItemProvider.kt | 4 +- .../kotlin/hep/dataforge/meta/Scheme.kt | 51 ++++++++----------- .../hep/dataforge/meta/Specification.kt | 27 ++++++---- .../hep/dataforge/meta/SpecificationTest.kt | 40 ++++++++++----- 5 files changed, 86 insertions(+), 75 deletions(-) diff --git a/dataforge-meta/api/dataforge-meta.api b/dataforge-meta/api/dataforge-meta.api index c1f68f89..0f94e3bd 100644 --- a/dataforge-meta/api/dataforge-meta.api +++ b/dataforge-meta/api/dataforge-meta.api @@ -431,16 +431,24 @@ public abstract interface class hep/dataforge/meta/ObservableItemProvider : hep/ public abstract fun removeListener (Ljava/lang/Object;)V } +public abstract interface class hep/dataforge/meta/ReadOnlySpecification { + public abstract fun empty ()Lhep/dataforge/meta/ItemProvider; + public abstract fun invoke (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/ItemProvider; + public abstract fun read (Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/ItemProvider; +} + +public final class hep/dataforge/meta/ReadOnlySpecification$DefaultImpls { + public static fun invoke (Lhep/dataforge/meta/ReadOnlySpecification;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/ItemProvider; +} + public class hep/dataforge/meta/Scheme : hep/dataforge/meta/MetaRepr, hep/dataforge/meta/MutableItemProvider, hep/dataforge/meta/descriptors/Described { public fun ()V - public fun (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/meta/descriptors/NodeDescriptor;)V - public synthetic fun (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/meta/descriptors/NodeDescriptor;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun getDefaultLayer ()Lhep/dataforge/meta/Meta; public synthetic fun getDescriptor ()Lhep/dataforge/meta/descriptors/ItemDescriptor; - public fun getDescriptor ()Lhep/dataforge/meta/descriptors/NodeDescriptor; + public final fun getDescriptor ()Lhep/dataforge/meta/descriptors/NodeDescriptor; public fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/TypedMetaItem; - public final fun getItems ()Lhep/dataforge/meta/MutableItemProvider; public final fun isEmpty ()Z + public final fun setDescriptor (Lhep/dataforge/meta/descriptors/NodeDescriptor;)V public fun setItem (Lhep/dataforge/names/Name;Lhep/dataforge/meta/TypedMetaItem;)V public fun toMeta ()Lhep/dataforge/meta/Laminate; public synthetic fun toMeta ()Lhep/dataforge/meta/Meta; @@ -448,22 +456,21 @@ public class hep/dataforge/meta/Scheme : hep/dataforge/meta/MetaRepr, hep/datafo } public final class hep/dataforge/meta/SchemeKt { - public static final fun asScheme (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/Scheme; + public static final fun inflate (Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/meta/descriptors/NodeDescriptor;)Lhep/dataforge/meta/Scheme; + public static synthetic fun inflate$default (Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/meta/descriptors/NodeDescriptor;ILjava/lang/Object;)Lhep/dataforge/meta/Scheme; public static final fun invoke (Lhep/dataforge/meta/Scheme;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/Scheme; - public static final fun toScheme (Lhep/dataforge/meta/Meta;Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MutableItemProvider; - public static synthetic fun toScheme$default (Lhep/dataforge/meta/Meta;Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lhep/dataforge/meta/MutableItemProvider; } public class hep/dataforge/meta/SchemeSpec : hep/dataforge/meta/Specification, hep/dataforge/meta/descriptors/Described { public fun (Lkotlin/jvm/functions/Function0;)V - public fun (Lkotlin/jvm/functions/Function3;)V - public synthetic fun empty ()Lhep/dataforge/meta/MutableItemProvider; + public synthetic fun empty ()Lhep/dataforge/meta/ItemProvider; public fun empty ()Lhep/dataforge/meta/Scheme; public synthetic fun getDescriptor ()Lhep/dataforge/meta/descriptors/ItemDescriptor; public fun getDescriptor ()Lhep/dataforge/meta/descriptors/NodeDescriptor; + public synthetic fun invoke (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/ItemProvider; public synthetic fun invoke (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MutableItemProvider; public final fun invoke (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/Scheme; - public synthetic fun read (Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; + public synthetic fun read (Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/ItemProvider; public fun read (Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/Scheme; public synthetic fun write (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; public fun write (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/Scheme; @@ -478,26 +485,20 @@ public final class hep/dataforge/meta/SealedMetaKt { public static final fun seal (Lhep/dataforge/meta/TypedMetaItem;)Lhep/dataforge/meta/TypedMetaItem; } -public abstract interface class hep/dataforge/meta/Specification { - public abstract fun empty ()Lhep/dataforge/meta/MutableItemProvider; - public abstract fun invoke (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MutableItemProvider; - public abstract fun read (Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; +public abstract interface class hep/dataforge/meta/Specification : hep/dataforge/meta/ReadOnlySpecification { public abstract fun write (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; } public final class hep/dataforge/meta/Specification$DefaultImpls { - public static fun empty (Lhep/dataforge/meta/Specification;)Lhep/dataforge/meta/MutableItemProvider; public static fun invoke (Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MutableItemProvider; public static synthetic fun write$default (Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;ILjava/lang/Object;)Lhep/dataforge/meta/MutableItemProvider; } public final class hep/dataforge/meta/SpecificationKt { - public static final fun spec (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/Scheme;Lhep/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; public static final fun spec (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/Specification;Lhep/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun spec$default (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/Scheme;Lhep/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; public static synthetic fun spec$default (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/Specification;Lhep/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static final fun update (Lhep/dataforge/meta/Configurable;Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/Configurable; - public static final fun update (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MutableItemProvider; + public static final fun update (Lhep/dataforge/meta/Configurable;Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)V + public static final fun update (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)V public static final fun withSpec (Lhep/dataforge/meta/TypedMetaItem;Lhep/dataforge/meta/Specification;)Lhep/dataforge/meta/MutableItemProvider; } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt index 3625162a..4216f7a1 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt @@ -116,8 +116,8 @@ public fun MutableItemProvider.editChild(name: Name, builder: MutableItemProvide * Create a mutable item provider that uses given provider for default values if those are not found in this provider. * Changes are propagated only to this provider. */ -public fun MutableItemProvider.withDefault(default: ItemProvider): MutableItemProvider = - if (default is Meta && default.isEmpty()) { +public fun MutableItemProvider.withDefault(default: ItemProvider?): MutableItemProvider = + if (default == null || (default is Meta && default.isEmpty())) { //Optimize for use with empty default this } else object : MutableItemProvider { diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt index 211b0f97..fcaf5f15 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt @@ -11,30 +11,24 @@ import hep.dataforge.names.asName */ public open class Scheme() : MutableItemProvider, Described, MetaRepr { - public var items: MutableItemProvider = MetaBuilder() - internal set(value) { - //Fix problem with `init` blocks in specifications - field = value.apply { - field.rootNode?.let { update(it) } - } - } + private var items: MutableItemProvider = MetaBuilder() - internal var default: ItemProvider? = null + private var default: ItemProvider? = null final override var descriptor: NodeDescriptor? = null - internal set - public constructor( + internal fun inflate( items: MutableItemProvider, default: ItemProvider? = null, descriptor: NodeDescriptor? = null, - ) : this(){ + ) { + //use properties in the init block as default + this.default = items.withDefault(default) + //reset values, defaults are already saved this.items = items - this.default = default this.descriptor = descriptor } - private fun getDefaultItem(name: Name): MetaItem? { return default?.get(name) ?: descriptor?.get(name)?.defaultItem() } @@ -87,6 +81,16 @@ public open class Scheme() : MutableItemProvider, Described, MetaRepr { public fun isEmpty(): Boolean = toMeta().isEmpty() } + +public fun > S.inflate( + items: MutableItemProvider, + default: ItemProvider? = null, + descriptor: NodeDescriptor? = null, +): T = empty().apply { + inflate(items, default, descriptor) +} + + /** * A shortcut to edit a [Scheme] object in-place */ @@ -95,31 +99,20 @@ public inline operator fun T.invoke(block: T.() -> Unit): T = apply /** * A specification for simplified generation of wrappers */ -public open class SchemeSpec( - private val builder: (target: MutableItemProvider, defaultProvider: ItemProvider, descriptor: NodeDescriptor?) -> T, +public open class SchemeSpec( + private val builder: () -> T, ) : Specification, Described { - public constructor(emptyBuilder: () -> T) : this({ target: MutableItemProvider, defaultProvider: ItemProvider, descriptor: NodeDescriptor? -> - emptyBuilder().apply { - this.items = target - this.default = defaultProvider - this.descriptor = descriptor - } - }) + override fun empty(): T = builder() - override fun read(items: ItemProvider): T = - builder(Config(), items, descriptor) + override fun read(items: ItemProvider): T = inflate(Config(), items, descriptor) override fun write(target: MutableItemProvider, defaultProvider: ItemProvider): T = - builder(target, defaultProvider, descriptor) + inflate(target, defaultProvider, descriptor) //TODO Generate descriptor from Scheme class override val descriptor: NodeDescriptor? get() = null @Suppress("OVERRIDE_BY_INLINE") final override inline operator fun invoke(action: T.() -> Unit): T = empty().apply(action) -} - -public fun Meta.asScheme(): Scheme = Scheme().apply { - items = this@asScheme.asConfig() } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt index 5f35f228..e6edd22e 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt @@ -5,26 +5,18 @@ import hep.dataforge.names.asName import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty -/** - * 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 { +public interface ReadOnlySpecification { + /** * Read generic read-only meta with this [Specification] producing instance of desired type. */ public fun read(items: ItemProvider): T - /** - * Wrap [MutableItemProvider], using it as inner storage (changes to [Specification] are reflected on [MutableItemProvider] - */ - public fun write(target: MutableItemProvider, defaultProvider: ItemProvider = ItemProvider.EMPTY): T /** * Generate an empty object */ - public fun empty(): T = read(Meta.EMPTY) + public fun empty(): T /** * A convenience method to use specifications in builders @@ -32,6 +24,19 @@ public interface Specification { 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: ReadOnlySpecification { + /** + * Wrap [MutableItemProvider], using it as inner storage (changes to [Specification] are reflected on [MutableItemProvider] + */ + public fun write(target: MutableItemProvider, defaultProvider: ItemProvider = ItemProvider.EMPTY): T +} + /** * Update a [MutableItemProvider] using given specification */ diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt index aa69a9e6..0f826c2c 100644 --- a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt @@ -4,22 +4,22 @@ import kotlin.test.Test import kotlin.test.assertEquals class SpecificationTest { - class TestStyled(target: MutableItemProvider, default: ItemProvider?) : Scheme(target, default) { + class TestScheme : Scheme() { var list by numberList(1, 2, 3) - companion object : Specification { - override fun read(items: ItemProvider): TestStyled = TestStyled(Config(), items) - - override fun write(target: MutableItemProvider, defaultProvider: ItemProvider): TestStyled = - TestStyled(target, defaultProvider) - } - } - - class TestScheme : Scheme() { var a by int() var b by string() - companion object : SchemeSpec(::TestScheme) + companion object : Specification { + override fun empty(): TestScheme = TestScheme() + + override fun read(items: ItemProvider): TestScheme = + inflate(Config(), items) + + override fun write(target: MutableItemProvider, defaultProvider: ItemProvider): TestScheme = + inflate(target, defaultProvider) + + } } // @Test @@ -49,7 +49,7 @@ class SpecificationTest { @Test fun testSpecific() { - val testObject = TestStyled { + val testObject = TestScheme { list = emptyList() } assertEquals(emptyList(), testObject.list) @@ -62,7 +62,19 @@ class SpecificationTest { val scheme = TestScheme.write(child) scheme.a = 22 scheme.b = "test" - assertEquals(22,config["child.a"].int) - assertEquals("test",config["child.b"].string) + assertEquals(22, config["child.a"].int) + assertEquals("test", config["child.b"].string) + } + + @Test + fun testChildUpdate() { + val config = Config() + val child = config.getChild("child") + val scheme = child.update(TestScheme) { + a = 22 + b = "test" + } + assertEquals(22, config["child.a"].int) + assertEquals("test", config["child.b"].string) } } \ No newline at end of file