From 1926d157ee1e41e2ad892cf8c64de90d102f4103 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 19 Sep 2018 10:15:25 +0300 Subject: [PATCH 01/37] serialization for meta (not fully functional yet) --- build.gradle | 31 +++--- dataforge-meta-common/build.gradle | 17 ++++ .../main/kotlin/hep/dataforge/meta/Config.kt | 0 .../kotlin/hep/dataforge/meta/Delegates.kt | 0 .../main/kotlin/hep/dataforge/meta/Meta.kt | 2 + .../kotlin/hep/dataforge/meta/MetaBuilder.kt | 0 .../kotlin/hep/dataforge/meta/MetaUtils.kt | 0 .../hep/dataforge/meta/MutableMetaNode.kt | 0 .../kotlin/hep/dataforge/meta/Serializers.kt | 95 +++++++++++++++++++ .../hep/dataforge/meta/Specification.kt | 0 .../kotlin/hep/dataforge/meta/Styleable.kt | 0 .../main/kotlin/hep/dataforge/meta/Value.kt | 3 + .../main/kotlin/hep/dataforge/names/Name.kt | 0 .../hep/dataforge/meta/MetaBuilderTest.kt | 0 .../hep/dataforge/meta/MetaDelegateTest.kt | 3 +- .../hep/dataforge/meta/SerializationTest.kt | 22 +++++ .../kotlin/hep/dataforge/names/NameTest.kt | 0 dataforge-meta-js/build.gradle | 13 +-- dataforge-meta-jvm/build.gradle | 16 +--- settings.gradle | 4 +- 20 files changed, 169 insertions(+), 37 deletions(-) create mode 100644 dataforge-meta-common/build.gradle rename {src => dataforge-meta-common/src}/main/kotlin/hep/dataforge/meta/Config.kt (100%) rename {src => dataforge-meta-common/src}/main/kotlin/hep/dataforge/meta/Delegates.kt (100%) rename {src => dataforge-meta-common/src}/main/kotlin/hep/dataforge/meta/Meta.kt (98%) rename {src => dataforge-meta-common/src}/main/kotlin/hep/dataforge/meta/MetaBuilder.kt (100%) rename {src => dataforge-meta-common/src}/main/kotlin/hep/dataforge/meta/MetaUtils.kt (100%) rename {src => dataforge-meta-common/src}/main/kotlin/hep/dataforge/meta/MutableMetaNode.kt (100%) create mode 100644 dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Serializers.kt rename {src => dataforge-meta-common/src}/main/kotlin/hep/dataforge/meta/Specification.kt (100%) rename {src => dataforge-meta-common/src}/main/kotlin/hep/dataforge/meta/Styleable.kt (100%) rename {src => dataforge-meta-common/src}/main/kotlin/hep/dataforge/meta/Value.kt (97%) rename {src => dataforge-meta-common/src}/main/kotlin/hep/dataforge/names/Name.kt (100%) rename {src => dataforge-meta-common/src}/test/kotlin/hep/dataforge/meta/MetaBuilderTest.kt (100%) rename {src => dataforge-meta-common/src}/test/kotlin/hep/dataforge/meta/MetaDelegateTest.kt (85%) create mode 100644 dataforge-meta-common/src/test/kotlin/hep/dataforge/meta/SerializationTest.kt rename {src => dataforge-meta-common/src}/test/kotlin/hep/dataforge/names/NameTest.kt (100%) diff --git a/build.gradle b/build.gradle index 8f26a92f..cd59ea87 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,15 @@ -plugins { - id 'kotlin-platform-common' version '1.2.70' +buildscript { + ext.kotlin_version = '1.2.70' + ext.serialization_version = '0.6.2' + repositories { + jcenter() + maven { url "https://kotlin.bintray.com/kotlinx" } + } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlinx:kotlinx-gradle-serialization-plugin:$serialization_version" + } } description = "The basic interfaces for DataForge meta-data" @@ -7,17 +17,10 @@ description = "The basic interfaces for DataForge meta-data" group 'hep.dataforge' version '0.1.1-SNAPSHOT' -repositories { - mavenCentral() -} - -dependencies { - compile "org.jetbrains.kotlin:kotlin-stdlib-common" - testCompile "org.jetbrains.kotlin:kotlin-test-annotations-common" - testCompile "org.jetbrains.kotlin:kotlin-test-common" -} -kotlin { - experimental { - coroutines "enable" +subprojects{ + repositories { + jcenter() + maven { url "https://kotlin.bintray.com/kotlinx" } + //maven { url 'https://jitpack.io' } } } diff --git a/dataforge-meta-common/build.gradle b/dataforge-meta-common/build.gradle new file mode 100644 index 00000000..6292cd0f --- /dev/null +++ b/dataforge-meta-common/build.gradle @@ -0,0 +1,17 @@ +plugins { + id 'kotlin-platform-common' + id 'kotlinx-serialization' +} + +dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version" + compile "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version" + testCompile "org.jetbrains.kotlin:kotlin-test-annotations-common" + testCompile "org.jetbrains.kotlin:kotlin-test-common" +} + +kotlin { + experimental { + coroutines "enable" + } +} \ No newline at end of file diff --git a/src/main/kotlin/hep/dataforge/meta/Config.kt b/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Config.kt similarity index 100% rename from src/main/kotlin/hep/dataforge/meta/Config.kt rename to dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Config.kt diff --git a/src/main/kotlin/hep/dataforge/meta/Delegates.kt b/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Delegates.kt similarity index 100% rename from src/main/kotlin/hep/dataforge/meta/Delegates.kt rename to dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Delegates.kt diff --git a/src/main/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Meta.kt similarity index 98% rename from src/main/kotlin/hep/dataforge/meta/Meta.kt rename to dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Meta.kt index 4d9ded52..b67fc68b 100644 --- a/src/main/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Meta.kt @@ -2,6 +2,7 @@ package hep.dataforge.meta import hep.dataforge.names.Name import hep.dataforge.names.toName +import kotlinx.serialization.Serializable /** * A member of the meta tree. Could be represented as one of following: @@ -30,6 +31,7 @@ operator fun List.get(query: String): M? { * * [MetaItem.SingleNodeItem] single node * * [MetaItem.MultiNodeItem] multi-value node */ +@Serializable interface Meta { val items: Map> } diff --git a/src/main/kotlin/hep/dataforge/meta/MetaBuilder.kt b/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/MetaBuilder.kt similarity index 100% rename from src/main/kotlin/hep/dataforge/meta/MetaBuilder.kt rename to dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/MetaBuilder.kt diff --git a/src/main/kotlin/hep/dataforge/meta/MetaUtils.kt b/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/MetaUtils.kt similarity index 100% rename from src/main/kotlin/hep/dataforge/meta/MetaUtils.kt rename to dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/MetaUtils.kt diff --git a/src/main/kotlin/hep/dataforge/meta/MutableMetaNode.kt b/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/MutableMetaNode.kt similarity index 100% rename from src/main/kotlin/hep/dataforge/meta/MutableMetaNode.kt rename to dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/MutableMetaNode.kt diff --git a/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Serializers.kt b/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Serializers.kt new file mode 100644 index 00000000..828056d7 --- /dev/null +++ b/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Serializers.kt @@ -0,0 +1,95 @@ +package hep.dataforge.meta + +import kotlinx.serialization.* +import kotlinx.serialization.internal.SerialClassDescImpl + +@Serializer(forClass = Value::class) +object ValueSerializer : KSerializer { + override val serialClassDesc: KSerialClassDesc = SerialClassDescImpl("Value") + + override fun load(input: KInput): Value { + val key = input.readByteValue() + return when (key.toChar()) { + 'S' -> StringValue(input.readStringValue()) + 'd' -> NumberValue(input.readDoubleValue()) + 'f' -> NumberValue(input.readFloatValue()) + 'i' -> NumberValue(input.readIntValue()) + 's' -> NumberValue(input.readShortValue()) + 'l' -> NumberValue(input.readLongValue()) + 'b' -> NumberValue(input.readByteValue()) + '+' -> True + '-' -> False + 'N' -> Null + 'L' -> { + val size = input.readIntValue() + val list = (0 until size).map { load(input) } + ListValue(list) + } + else -> error("Unknown value deserialization ket '$key'") + } + } + + override fun save(output: KOutput, obj: Value) { + when (obj.type) { + ValueType.NUMBER -> { + val number = obj.number + when (number) { + is Float -> { + output.writeByteValue('f'.toByte()) + output.writeFloatValue(number) + } + is Short -> { + output.writeByteValue('s'.toByte()) + output.writeShortValue(number) + } + is Int -> { + output.writeByteValue('i'.toByte()) + output.writeIntValue(number) + } + is Long -> { + output.writeByteValue('l'.toByte()) + output.writeLongValue(number) + } + is Byte -> { + output.writeByteValue('b'.toByte()) + output.writeByteValue(number) + } + is Double -> { + output.writeByteValue('d'.toByte()) + output.writeDoubleValue(number) + } + else -> { + //TODO add warning + output.writeByteValue('d'.toByte()) + output.writeDoubleValue(number.toDouble()) + } + } + } + ValueType.STRING -> { + output.writeByteValue('S'.toByte()) + output.writeStringValue(obj.string) + } + ValueType.BOOLEAN -> if (obj.boolean) { + output.writeByteValue('+'.toByte()) + } else { + output.writeByteValue('-'.toByte()) + } + ValueType.NULL -> output.writeByteValue('N'.toByte()) + } + } +} + + +//@Serializer(forClass = Meta::class) +//object MetaSerializer: KSerializer{ +// override val serialClassDesc: KSerialClassDesc = SerialClassDescImpl("Meta") +// +// override fun load(input: KInput): Meta { +// +// } +// +// override fun save(output: KOutput, obj: Meta) { +// NamedValueOutput() +// } +// +//} \ No newline at end of file diff --git a/src/main/kotlin/hep/dataforge/meta/Specification.kt b/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Specification.kt similarity index 100% rename from src/main/kotlin/hep/dataforge/meta/Specification.kt rename to dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Specification.kt diff --git a/src/main/kotlin/hep/dataforge/meta/Styleable.kt b/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Styleable.kt similarity index 100% rename from src/main/kotlin/hep/dataforge/meta/Styleable.kt rename to dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Styleable.kt diff --git a/src/main/kotlin/hep/dataforge/meta/Value.kt b/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Value.kt similarity index 97% rename from src/main/kotlin/hep/dataforge/meta/Value.kt rename to dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Value.kt index 4bd6d55e..fb7f62e5 100644 --- a/src/main/kotlin/hep/dataforge/meta/Value.kt +++ b/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Value.kt @@ -1,5 +1,7 @@ package hep.dataforge.meta +import kotlinx.serialization.Serializable + /** * The list of supported Value types. @@ -16,6 +18,7 @@ enum class ValueType { * * Value can represent a list of value objects. */ +@Serializable(with = ValueSerializer::class) interface Value { /** * Get raw value of this value diff --git a/src/main/kotlin/hep/dataforge/names/Name.kt b/dataforge-meta-common/src/main/kotlin/hep/dataforge/names/Name.kt similarity index 100% rename from src/main/kotlin/hep/dataforge/names/Name.kt rename to dataforge-meta-common/src/main/kotlin/hep/dataforge/names/Name.kt diff --git a/src/test/kotlin/hep/dataforge/meta/MetaBuilderTest.kt b/dataforge-meta-common/src/test/kotlin/hep/dataforge/meta/MetaBuilderTest.kt similarity index 100% rename from src/test/kotlin/hep/dataforge/meta/MetaBuilderTest.kt rename to dataforge-meta-common/src/test/kotlin/hep/dataforge/meta/MetaBuilderTest.kt diff --git a/src/test/kotlin/hep/dataforge/meta/MetaDelegateTest.kt b/dataforge-meta-common/src/test/kotlin/hep/dataforge/meta/MetaDelegateTest.kt similarity index 85% rename from src/test/kotlin/hep/dataforge/meta/MetaDelegateTest.kt rename to dataforge-meta-common/src/test/kotlin/hep/dataforge/meta/MetaDelegateTest.kt index 30c30ed3..f6929e65 100644 --- a/src/test/kotlin/hep/dataforge/meta/MetaDelegateTest.kt +++ b/dataforge-meta-common/src/test/kotlin/hep/dataforge/meta/MetaDelegateTest.kt @@ -12,7 +12,8 @@ class MetaDelegateTest { @Test fun delegateTest() { - val testObject = object : SimpleConfigurable(Config()) { + val testObject = object : Specification { + override val config: Config = Config() var myValue by string() var safeValue by number(2.2) var enumValue by enum(TestEnum.YES) diff --git a/dataforge-meta-common/src/test/kotlin/hep/dataforge/meta/SerializationTest.kt b/dataforge-meta-common/src/test/kotlin/hep/dataforge/meta/SerializationTest.kt new file mode 100644 index 00000000..f4e87580 --- /dev/null +++ b/dataforge-meta-common/src/test/kotlin/hep/dataforge/meta/SerializationTest.kt @@ -0,0 +1,22 @@ +package scientifik.kplot.remote + +import hep.dataforge.meta.buildMeta +import hep.dataforge.meta.get +import hep.dataforge.meta.value +import kotlinx.serialization.json.JSON +import kotlin.test.Test + +class SerializationTest{ + @Test + fun testMetaSerialization(){ + val meta = buildMeta { + "a" to 2 + "b" to { + "c" to "ddd" + "d" to 2.2 + } + } + val json = JSON.stringify(meta["a"]?.value!!) + println(json) + } +} \ No newline at end of file diff --git a/src/test/kotlin/hep/dataforge/names/NameTest.kt b/dataforge-meta-common/src/test/kotlin/hep/dataforge/names/NameTest.kt similarity index 100% rename from src/test/kotlin/hep/dataforge/names/NameTest.kt rename to dataforge-meta-common/src/test/kotlin/hep/dataforge/names/NameTest.kt diff --git a/dataforge-meta-js/build.gradle b/dataforge-meta-js/build.gradle index 59da54d1..72701abb 100644 --- a/dataforge-meta-js/build.gradle +++ b/dataforge-meta-js/build.gradle @@ -1,17 +1,12 @@ plugins { id 'kotlin-platform-js' -} - -group 'hep.dataforge' -version '0.1.0-SNAPSHOT' - -repositories { - mavenCentral() + //id 'kotlinx-serialization' } dependencies { - expectedBy rootProject + expectedBy project(":dataforge-meta-common") - compile "org.jetbrains.kotlin:kotlin-stdlib-js" + compile "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version" + compile "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version" testCompile "org.jetbrains.kotlin:kotlin-test-js" } diff --git a/dataforge-meta-jvm/build.gradle b/dataforge-meta-jvm/build.gradle index 99ac4a6d..872ba285 100644 --- a/dataforge-meta-jvm/build.gradle +++ b/dataforge-meta-jvm/build.gradle @@ -1,21 +1,15 @@ plugins { id 'kotlin-platform-jvm' -} - -group 'hep.dataforge' -version '0.1.0-SNAPSHOT' - -repositories { - mavenCentral() + id 'kotlinx-serialization' } dependencies { - expectedBy rootProject + expectedBy project(":dataforge-meta-common") - compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8" - testCompile "junit:junit:4.12" + compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + compile "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version" testCompile "org.jetbrains.kotlin:kotlin-test" - testCompile "org.jetbrains.kotlin:kotlin-test-junit" + testCompile "org.jetbrains.kotlin:kotlin-test-junit5" } compileKotlin { diff --git a/settings.gradle b/settings.gradle index 8b0f8f86..655d929b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -4,18 +4,18 @@ pluginManagement { if (requested.id.id == "kotlin-platform-common") { useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") } - if (requested.id.id == "kotlin-platform-jvm") { useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") } - if (requested.id.id == "kotlin-platform-js") { useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") } } } } + rootProject.name = 'dataforge-meta' +include ":dataforge-meta-common" include ":dataforge-meta-jvm" include ":dataforge-meta-js" \ No newline at end of file From a5a4b67c97095558c76d950859c9e2aa2d802bf9 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 23 Sep 2018 21:09:35 +0300 Subject: [PATCH 02/37] custom JSON serialization (no universal serialization yet) --- .gitignore | 2 +- build.gradle | 24 +- dataforge-meta-common/build.gradle | 9 +- .../main/kotlin/hep/dataforge/meta/Meta.kt | 116 ++++++-- .../kotlin/hep/dataforge/meta/MetaBuilder.kt | 11 + .../kotlin/hep/dataforge/meta/MetaUtils.kt | 40 --- .../kotlin/hep/dataforge/meta/Serializers.kt | 263 +++++++++++++----- .../main/kotlin/hep/dataforge/meta/Value.kt | 69 +++-- .../main/kotlin/hep/dataforge/names/Name.kt | 3 +- .../hep/dataforge/meta/SerializationTest.kt | 46 ++- dataforge-meta-js/build.gradle | 3 +- dataforge-meta-jvm/build.gradle | 6 +- 12 files changed, 411 insertions(+), 181 deletions(-) delete mode 100644 dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/MetaUtils.kt diff --git a/.gitignore b/.gitignore index d482c058..d35e96f9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ *.iws out/ .gradle -/build/ +**/build/ !gradle-wrapper.jar diff --git a/build.gradle b/build.gradle index cd59ea87..ae66ee48 100644 --- a/build.gradle +++ b/build.gradle @@ -1,26 +1,40 @@ buildscript { - ext.kotlin_version = '1.2.70' - ext.serialization_version = '0.6.2' + ext.kotlin_version = '1.3.0-rc-57' + ext.serialization_version = '0.8.0-rc13' repositories { jcenter() - maven { url "https://kotlin.bintray.com/kotlinx" } + maven { + url = "http://dl.bintray.com/kotlin/kotlin-eap" + } } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlinx:kotlinx-gradle-serialization-plugin:$serialization_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" } } +//plugins { +// id 'kotlin-platform-common' version "$kotlin_version" apply false +// id 'kotlin-platform-jvm' version "$kotlin_version" apply false +// id 'kotlin-platform-js' version "$kotlin_version" apply false +// id 'kotlinx-serialization' version "$kotlin_version" apply false +//} + description = "The basic interfaces for DataForge meta-data" group 'hep.dataforge' version '0.1.1-SNAPSHOT' -subprojects{ +allprojects { repositories { jcenter() + maven { url = "http://dl.bintray.com/kotlin/kotlin-eap" } maven { url "https://kotlin.bintray.com/kotlinx" } //maven { url 'https://jitpack.io' } } } + +subprojects { + apply plugin: 'kotlinx-serialization' +} diff --git a/dataforge-meta-common/build.gradle b/dataforge-meta-common/build.gradle index 6292cd0f..a396466f 100644 --- a/dataforge-meta-common/build.gradle +++ b/dataforge-meta-common/build.gradle @@ -1,6 +1,5 @@ -plugins { +plugins{ id 'kotlin-platform-common' - id 'kotlinx-serialization' } dependencies { @@ -8,10 +7,4 @@ dependencies { compile "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version" testCompile "org.jetbrains.kotlin:kotlin-test-annotations-common" testCompile "org.jetbrains.kotlin:kotlin-test-common" -} - -kotlin { - experimental { - coroutines "enable" - } } \ No newline at end of file diff --git a/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Meta.kt index b67fc68b..b84a84e7 100644 --- a/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Meta.kt @@ -2,7 +2,6 @@ package hep.dataforge.meta import hep.dataforge.names.Name import hep.dataforge.names.toName -import kotlinx.serialization.Serializable /** * A member of the meta tree. Could be represented as one of following: @@ -11,9 +10,34 @@ import kotlinx.serialization.Serializable * * a list of nodes */ sealed class MetaItem { - class ValueItem(val value: Value) : MetaItem() - class SingleNodeItem(val node: M) : MetaItem() - class MultiNodeItem(val nodes: List) : MetaItem() + class ValueItem(val value: Value) : MetaItem(){ + override fun equals(other: Any?): Boolean { + return this.value == (other as? ValueItem<*>)?.value + } + + override fun hashCode(): Int { + return value.hashCode() + } + } + class SingleNodeItem(val node: M) : MetaItem(){ + override fun equals(other: Any?): Boolean { + return this.node == (other as? SingleNodeItem<*>)?.node + } + + override fun hashCode(): Int { + return node.hashCode() + } + } + + class MultiNodeItem(val nodes: List) : MetaItem(){ + override fun equals(other: Any?): Boolean { + return this.nodes == (other as? MultiNodeItem<*>)?.nodes + } + + override fun hashCode(): Int { + return nodes.hashCode() + } + } } operator fun List.get(query: String): M? { @@ -31,7 +55,6 @@ operator fun List.get(query: String): M? { * * [MetaItem.SingleNodeItem] single node * * [MetaItem.MultiNodeItem] multi-value node */ -@Serializable interface Meta { val items: Map> } @@ -62,6 +85,22 @@ abstract class MetaNode> : Meta { } operator fun get(key: String): MetaItem? = get(key.toName()) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Meta) return false + + return this.items == other.items + } + + override fun hashCode(): Int { + return items.hashCode() + } + + override fun toString(): String { + return toJSON().toString() + } + } /** @@ -69,17 +108,23 @@ abstract class MetaNode> : Meta { * * If the argument is possibly mutable node, it is copied on creation */ -class SealedMeta(meta: Meta) : MetaNode() { - override val items: Map> = if (meta is SealedMeta) { - meta.items - } else { - meta.items.mapValues { entry -> - val item = entry.value - when (item) { - is MetaItem.ValueItem -> MetaItem.ValueItem(item.value) - is MetaItem.SingleNodeItem -> MetaItem.SingleNodeItem(SealedMeta(item.node)) - is MetaItem.MultiNodeItem -> MetaItem.MultiNodeItem(item.nodes.map { SealedMeta(it) }) +class SealedMeta internal constructor(override val items: Map>) : MetaNode() { + + companion object { + fun seal(meta: Meta): SealedMeta { + val items = if (meta is SealedMeta) { + meta.items + } else { + meta.items.mapValues { entry -> + val item = entry.value + when (item) { + is MetaItem.ValueItem -> MetaItem.ValueItem(item.value) + is MetaItem.SingleNodeItem -> MetaItem.SingleNodeItem(seal(item.node)) + is MetaItem.MultiNodeItem -> MetaItem.MultiNodeItem(item.nodes.map { seal(it) }) + } + } } + return SealedMeta(items) } } } @@ -87,12 +132,51 @@ class SealedMeta(meta: Meta) : MetaNode() { /** * Generate sealed node from [this]. If it is already sealed return it as is */ -fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta(this) +fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta.seal(this) object EmptyMeta : Meta { override val items: Map> = emptyMap() } +/** + * Unsafe methods to access values and nodes directly from [MetaItem] + */ + +val MetaItem<*>.value + get() = (this as? MetaItem.ValueItem)?.value ?: error("Trying to interpret node meta item as value item") +val MetaItem<*>.string get() = value.string +val MetaItem<*>.boolean get() = value.boolean +val MetaItem<*>.number get() = value.number +val MetaItem<*>.double get() = number.toDouble() +val MetaItem<*>.int get() = number.toInt() +val MetaItem<*>.long get() = number.toLong() + +val MetaItem.node: M + get() = when (this) { + is MetaItem.ValueItem -> error("Trying to interpret value meta item as node item") + is MetaItem.SingleNodeItem -> node + is MetaItem.MultiNodeItem -> nodes.first() + } + +/** + * Utility method to access item content as list of nodes. + * Returns empty list if it is value item. + */ +val MetaItem.nodes: List + get() = when (this) { + is MetaItem.ValueItem -> emptyList()//error("Trying to interpret value meta item as node item") + is MetaItem.SingleNodeItem -> listOf(node) + is MetaItem.MultiNodeItem -> nodes + } + +fun MetaItem.indexOf(meta: M): Int { + return when (this) { + is MetaItem.ValueItem -> -1 + is MetaItem.SingleNodeItem -> if (node == meta) 0 else -1 + is MetaItem.MultiNodeItem -> nodes.indexOf(meta) + } +} + /** * Generic meta-holder object */ diff --git a/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/MetaBuilder.kt b/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/MetaBuilder.kt index 7182c82e..c7f6724d 100644 --- a/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/MetaBuilder.kt +++ b/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/MetaBuilder.kt @@ -10,9 +10,20 @@ class MetaBuilder : MutableMetaNode() { override fun empty(): MetaBuilder = MetaBuilder() infix fun String.to(value: Any) { + if (value is Meta) { + this@MetaBuilder[this] = value + } this@MetaBuilder[this] = Value.of(value) } + infix fun String.to(meta: Meta) { + this@MetaBuilder[this] = meta + } + + infix fun String.to(value: Iterable) { + this@MetaBuilder[this] = value.toList() + } + infix fun String.to(metaBuilder: MetaBuilder.() -> Unit) { this@MetaBuilder[this] = MetaBuilder().apply(metaBuilder) } diff --git a/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/MetaUtils.kt b/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/MetaUtils.kt deleted file mode 100644 index ef8b8880..00000000 --- a/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/MetaUtils.kt +++ /dev/null @@ -1,40 +0,0 @@ -package hep.dataforge.meta - -/** - * Unsafe methods to access values and nodes directly from [MetaItem] - */ - -val MetaItem<*>.value - get() = (this as? MetaItem.ValueItem)?.value ?: error("Trying to interpret node meta item as value item") -val MetaItem<*>.string get() = value.string -val MetaItem<*>.boolean get() = value.boolean -val MetaItem<*>.number get() = value.number -val MetaItem<*>.double get() = number.toDouble() -val MetaItem<*>.int get() = number.toInt() -val MetaItem<*>.long get() = number.toLong() - -val MetaItem.node: M - get() = when (this) { - is MetaItem.ValueItem -> error("Trying to interpret value meta item as node item") - is MetaItem.SingleNodeItem -> node - is MetaItem.MultiNodeItem -> nodes.first() - } - -/** - * Utility method to access item content as list of nodes. - * Returns empty list if it is value item. - */ -val MetaItem.nodes: List - get() = when (this) { - is MetaItem.ValueItem -> emptyList()//error("Trying to interpret value meta item as node item") - is MetaItem.SingleNodeItem -> listOf(node) - is MetaItem.MultiNodeItem -> nodes - } - -fun MetaItem.indexOf(meta: M): Int { - return when (this) { - is MetaItem.ValueItem -> -1 - is MetaItem.SingleNodeItem -> if (node == meta) 0 else -1 - is MetaItem.MultiNodeItem -> nodes.indexOf(meta) - } -} \ No newline at end of file diff --git a/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Serializers.kt b/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Serializers.kt index 828056d7..1fd0a316 100644 --- a/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Serializers.kt +++ b/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Serializers.kt @@ -1,95 +1,206 @@ package hep.dataforge.meta -import kotlinx.serialization.* -import kotlinx.serialization.internal.SerialClassDescImpl +import kotlinx.serialization.json.* -@Serializer(forClass = Value::class) -object ValueSerializer : KSerializer { - override val serialClassDesc: KSerialClassDesc = SerialClassDescImpl("Value") - override fun load(input: KInput): Value { - val key = input.readByteValue() - return when (key.toChar()) { - 'S' -> StringValue(input.readStringValue()) - 'd' -> NumberValue(input.readDoubleValue()) - 'f' -> NumberValue(input.readFloatValue()) - 'i' -> NumberValue(input.readIntValue()) - 's' -> NumberValue(input.readShortValue()) - 'l' -> NumberValue(input.readLongValue()) - 'b' -> NumberValue(input.readByteValue()) - '+' -> True - '-' -> False - 'N' -> Null - 'L' -> { - val size = input.readIntValue() - val list = (0 until size).map { load(input) } - ListValue(list) - } - else -> error("Unknown value deserialization ket '$key'") +/*Universal serialization*/ + +//sealed class MetaItemProxy { +// +// @Serializable +// class NumberValueProxy(val number: Number) : MetaItemProxy() +// +// @Serializable +// class StringValueProxy(val string: String) : MetaItemProxy() +// +// @Serializable +// class BooleanValueProxy(val boolean: Boolean) : MetaItemProxy() +// +// @Serializable +// object NullValueProxy : MetaItemProxy() +// +// @Serializable +// class MetaProxy(@Serializable val map: Map) : MetaItemProxy() +// +// @Serializable +// class MetaListProxy(@Serializable val nodes: List) : MetaItemProxy() +//} +// +// +//fun Meta.toMap(): Map { +// return this.items.mapValues { (_, value) -> +// when (value) { +// is MetaItem.ValueItem -> when (value.value.type) { +// ValueType.NUMBER -> MetaItemProxy.NumberValueProxy(value.value.number) +// ValueType.STRING -> MetaItemProxy.StringValueProxy(value.value.string) +// ValueType.BOOLEAN -> MetaItemProxy.BooleanValueProxy(value.value.boolean) +// ValueType.NULL -> MetaItemProxy.NullValueProxy +// } +// is MetaItem.SingleNodeItem -> MetaItemProxy.MetaProxy(value.node.toMap()) +// is MetaItem.MultiNodeItem -> MetaItemProxy.MetaListProxy(value.nodes.map { MetaItemProxy.MetaProxy(it.toMap()) }) +// } +// } +//} + + +/*Direct JSON serialization*/ + +fun Value.toJson(): JsonElement = if (isList()) { + JsonArray(list.map { it.toJson() }) +} else { + when (type) { + ValueType.NUMBER -> JsonPrimitive(number) + ValueType.STRING -> JsonPrimitive(string) + ValueType.BOOLEAN -> JsonPrimitive(boolean) + ValueType.NULL -> JsonNull + } +} + +fun Meta.toJSON(): JsonObject { + val map = this.items.mapValues { (_, value) -> + when (value) { + is MetaItem.ValueItem -> value.value.toJson() + is MetaItem.SingleNodeItem -> value.node.toJSON() + is MetaItem.MultiNodeItem -> JsonArray(value.nodes.map { it.toJSON() }) } } + return JsonObject(map) +} - override fun save(output: KOutput, obj: Value) { - when (obj.type) { - ValueType.NUMBER -> { - val number = obj.number - when (number) { - is Float -> { - output.writeByteValue('f'.toByte()) - output.writeFloatValue(number) - } - is Short -> { - output.writeByteValue('s'.toByte()) - output.writeShortValue(number) - } - is Int -> { - output.writeByteValue('i'.toByte()) - output.writeIntValue(number) - } - is Long -> { - output.writeByteValue('l'.toByte()) - output.writeLongValue(number) - } - is Byte -> { - output.writeByteValue('b'.toByte()) - output.writeByteValue(number) - } - is Double -> { - output.writeByteValue('d'.toByte()) - output.writeDoubleValue(number) - } - else -> { - //TODO add warning - output.writeByteValue('d'.toByte()) - output.writeDoubleValue(number.toDouble()) - } +fun JsonPrimitive.toValue(): Value { + return when (this) { + is JsonLiteral -> LazyParsedValue(content) + is JsonNull -> Null + } +} + +fun JsonObject.toMeta(): Meta { + return buildMeta { + this@toMeta.forEach { (key, value) -> + when (value) { + is JsonPrimitive -> set(key, value.toValue()) + is JsonObject -> set(key, value.toMeta()) + is JsonArray -> if (value.all { it is JsonPrimitive }) { + set(key, ListValue(value.map { (it as JsonPrimitive).toValue() })) + } else { + set( + key, + value.map { + if (it is JsonObject) { + it.toMeta() + } else { + buildMeta { "@value" to it.primitive.toValue() } + } + } + ) } } - ValueType.STRING -> { - output.writeByteValue('S'.toByte()) - output.writeStringValue(obj.string) - } - ValueType.BOOLEAN -> if (obj.boolean) { - output.writeByteValue('+'.toByte()) - } else { - output.writeByteValue('-'.toByte()) - } - ValueType.NULL -> output.writeByteValue('N'.toByte()) } } } +/*Direct CBOR serialization*/ -//@Serializer(forClass = Meta::class) -//object MetaSerializer: KSerializer{ -// override val serialClassDesc: KSerialClassDesc = SerialClassDescImpl("Meta") -// -// override fun load(input: KInput): Meta { -// +//fun Meta.toBinary(out: OutputStream) { +// fun CBOR.CBOREncoder.encodeChar(char: Char) { +// encodeNumber(char.toByte().toLong()) // } // -// override fun save(output: KOutput, obj: Meta) { -// NamedValueOutput() +// fun CBOR.CBOREncoder.encodeValue(value: Value) { +// if (value.isList()) { +// encodeChar('L') +// startArray() +// value.list.forEach { +// encodeValue(it) +// } +// end() +// } else when (value.type) { +// ValueType.NUMBER -> when (value.value) { +// is Int, is Short, is Long -> { +// encodeChar('i') +// encodeNumber(value.number.toLong()) +// } +// is Float -> { +// encodeChar('f') +// encodeFloat(value.number.toFloat()) +// } +// else -> { +// encodeChar('d') +// encodeDouble(value.number.toDouble()) +// } +// } +// ValueType.STRING -> { +// encodeChar('S') +// encodeString(value.string) +// } +// ValueType.BOOLEAN -> { +// if (value.boolean) { +// encodeChar('+') +// } else { +// encodeChar('-') +// } +// } +// ValueType.NULL -> { +// encodeChar('N') +// } +// } +// } +// +// fun CBOR.CBOREncoder.encodeMeta(meta: Meta) { +// meta.items.forEach { (key, item) -> +// this.startMap() +// encodeString(key) +// when (item) { +// is MetaItem.ValueItem -> { +// encodeChar('V') +// encodeValue(item.value) +// } +// is MetaItem.SingleNodeItem -> { +// startArray() +// encodeMeta(item.node) +// } +// is MetaItem.MultiNodeItem -> { +// startArray() +// item.nodes.forEach { +// encodeMeta(it) +// } +// end() +// } +// } +// } +// } +// +// +// CBOR.CBOREncoder(out).apply { +// encodeMeta(this@toBinary) +// } +//} +// +//fun InputStream.readBinaryMeta(): Meta { +// fun CBOR.CBORDecoder.nextChar(): Char = nextNumber().toByte().toChar() +// +// fun CBOR.CBORDecoder.nextValue(): Value { +// val key = nextChar() +// return when(key){ +// 'L' -> { +// val size = startArray() +// val res = (0 until size).map { nextValue() } +// end() +// ListValue(res) +// } +// 'S' -> StringValue(nextString()) +// 'N' -> Null +// '+' -> True +// '-' -> False +// 'i' -> NumberValue(nextNumber()) +// 'f' -> NumberValue(nextFloat()) +// 'd' -> NumberValue(nextDouble()) +// else -> error("Unknown binary key: $key") +// } +// } +// +// fun CBOR.CBORDecoder.nextMeta(): Meta{ +// // } // //} \ No newline at end of file diff --git a/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Value.kt b/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Value.kt index fb7f62e5..53c5a8f3 100644 --- a/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Value.kt +++ b/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Value.kt @@ -1,7 +1,5 @@ package hep.dataforge.meta -import kotlinx.serialization.Serializable - /** * The list of supported Value types. @@ -18,7 +16,6 @@ enum class ValueType { * * Value can represent a list of value objects. */ -@Serializable(with = ValueSerializer::class) interface Value { /** * Get raw value of this value @@ -69,10 +66,10 @@ interface Value { * A singleton null value */ object Null : Value { - override val value: Any? = null - override val type: ValueType = ValueType.NULL - override val number: Number = Double.NaN - override val string: String = "@null" + override val value: Any? get() = null + override val type: ValueType get() = ValueType.NULL + override val number: Number get() = Double.NaN + override val string: String get() = "@null" } /** @@ -85,40 +82,60 @@ fun Value.isNull(): Boolean = this == Null * Singleton true value */ object True : Value { - override val value: Any? = true - override val type: ValueType = ValueType.BOOLEAN - override val number: Number = 1.0 - override val string: String = "+" + override val value: Any? get() = true + override val type: ValueType get() = ValueType.BOOLEAN + override val number: Number get() = 1.0 + override val string: String get() = "+" } /** * Singleton false value */ object False : Value { - override val value: Any? = false - override val type: ValueType = ValueType.BOOLEAN - override val number: Number = -1.0 - override val string: String = "-" + override val value: Any? get() = false + override val type: ValueType get() = ValueType.BOOLEAN + override val number: Number get() = -1.0 + override val string: String get() = "-" } val Value.boolean get() = this == True || this.list.firstOrNull() == True || (type == ValueType.STRING && string.toBoolean()) class NumberValue(override val number: Number) : Value { override val value: Any? get() = number - override val type: ValueType = ValueType.NUMBER + override val type: ValueType get() = ValueType.NUMBER override val string: String get() = number.toString() + + override fun equals(other: Any?): Boolean { + return this.number == (other as? Value)?.number + } + + override fun hashCode(): Int = number.hashCode() + + } class StringValue(override val string: String) : Value { override val value: Any? get() = string - override val type: ValueType = ValueType.STRING + override val type: ValueType get() = ValueType.STRING override val number: Number get() = string.toDouble() + + override fun equals(other: Any?): Boolean { + return this.string == (other as? Value)?.string + } + + override fun hashCode(): Int = string.hashCode() } class EnumValue>(override val value: E) : Value { - override val type: ValueType = ValueType.STRING - override val number: Number = value.ordinal - override val string: String = value.name + override val type: ValueType get() = ValueType.STRING + override val number: Number get() = value.ordinal + override val string: String get() = value.name + + override fun equals(other: Any?): Boolean { + return string == (other as? Value)?.string + } + + override fun hashCode(): Int = value.hashCode() } class ListValue(override val list: List) : Value { @@ -148,6 +165,7 @@ fun String.asValue(): Value = StringValue(this) fun Collection.asValue(): Value = ListValue(this.toList()) + /** * Create Value from String using closest match conversion */ @@ -181,4 +199,15 @@ fun String.parseValue(): Value { //Give up and return a StringValue return StringValue(this) +} + +class LazyParsedValue(override val string: String): Value{ + private val parsedValue by lazy { string.parseValue() } + + override val value: Any? + get() = parsedValue.value + override val type: ValueType + get() = parsedValue.type + override val number: Number + get() = parsedValue.number } \ No newline at end of file diff --git a/dataforge-meta-common/src/main/kotlin/hep/dataforge/names/Name.kt b/dataforge-meta-common/src/main/kotlin/hep/dataforge/names/Name.kt index 6ac98d8d..71c61379 100644 --- a/dataforge-meta-common/src/main/kotlin/hep/dataforge/names/Name.kt +++ b/dataforge-meta-common/src/main/kotlin/hep/dataforge/names/Name.kt @@ -1,6 +1,5 @@ package hep.dataforge.names -import kotlin.coroutines.experimental.buildSequence /** * The general interface for working with names. @@ -75,7 +74,7 @@ data class NameToken internal constructor(val body: String, val query: String) { } fun String.toName(): Name { - val tokens = buildSequence { + val tokens = sequence { var bodyBuilder = StringBuilder() var queryBuilder = StringBuilder() var bracketCount: Int = 0 diff --git a/dataforge-meta-common/src/test/kotlin/hep/dataforge/meta/SerializationTest.kt b/dataforge-meta-common/src/test/kotlin/hep/dataforge/meta/SerializationTest.kt index f4e87580..b1e28ab7 100644 --- a/dataforge-meta-common/src/test/kotlin/hep/dataforge/meta/SerializationTest.kt +++ b/dataforge-meta-common/src/test/kotlin/hep/dataforge/meta/SerializationTest.kt @@ -1,14 +1,12 @@ -package scientifik.kplot.remote +package hep.dataforge.meta -import hep.dataforge.meta.buildMeta -import hep.dataforge.meta.get -import hep.dataforge.meta.value -import kotlinx.serialization.json.JSON import kotlin.test.Test +import kotlin.test.assertEquals + +class SerializationTest { -class SerializationTest{ @Test - fun testMetaSerialization(){ + fun testJSONSerialization() { val meta = buildMeta { "a" to 2 "b" to { @@ -16,7 +14,39 @@ class SerializationTest{ "d" to 2.2 } } - val json = JSON.stringify(meta["a"]?.value!!) + val json = meta.toJSON() println(json) + val result = json.toMeta() + assertEquals(meta, result) } + +// @Test +// fun testIndirectSerialization() { +// val meta = buildMeta { +// "a" to 2 +// "b" to { +// "c" to "ddd" +// "d" to 2.2 +// } +// } +// val json = JSON.stringify(meta.toMap()) +// println(json) +//// val result = json.toMeta() +//// assertEquals(meta, result) +// } + +// @Test +// fun testWeirdSerialization() { +// val meta = buildMeta { +// "a" to 2 +// "b" to { +// "c" to "ddd" +// "d" to 2.2 +// } +// } +// val json = JSON.stringify(meta.toJSON()) +// println(json) +// val result: JsonObject = JSON.parse(json) +// assertEquals(meta, result.toMeta()) +// } } \ No newline at end of file diff --git a/dataforge-meta-js/build.gradle b/dataforge-meta-js/build.gradle index 72701abb..f884412d 100644 --- a/dataforge-meta-js/build.gradle +++ b/dataforge-meta-js/build.gradle @@ -1,6 +1,5 @@ -plugins { +plugins{ id 'kotlin-platform-js' - //id 'kotlinx-serialization' } dependencies { diff --git a/dataforge-meta-jvm/build.gradle b/dataforge-meta-jvm/build.gradle index 872ba285..f371911a 100644 --- a/dataforge-meta-jvm/build.gradle +++ b/dataforge-meta-jvm/build.gradle @@ -1,15 +1,15 @@ -plugins { +plugins{ id 'kotlin-platform-jvm' - id 'kotlinx-serialization' } dependencies { expectedBy project(":dataforge-meta-common") compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" compile "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version" testCompile "org.jetbrains.kotlin:kotlin-test" - testCompile "org.jetbrains.kotlin:kotlin-test-junit5" + testCompile "org.jetbrains.kotlin:kotlin-test-junit" } compileKotlin { From ad059dea33399762b16d0bacde3ea12ee798b2b0 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 25 Sep 2018 12:06:35 +0300 Subject: [PATCH 03/37] Splitting meta and mata-io. Project rename --- build.gradle | 5 +- dataforge-envelope/build.gradle | 13 ++++ .../hep/dataforge/envelopes/Envelope.kt | 9 +++ .../build.gradle | 2 +- dataforge-meta/dataforge-meta-io/build.gradle | 19 +++++ .../hep/dataforge/meta/io/MetaFormat.kt | 26 +++++++ .../hep/dataforge/meta/io}/Serializers.kt | 77 ++++++++++--------- .../dataforge-meta-js}/build.gradle | 3 +- .../dataforge-meta-jvm}/build.gradle | 4 +- .../main/kotlin/hep/dataforge/meta/Config.kt | 0 .../kotlin/hep/dataforge/meta/Delegates.kt | 0 .../main/kotlin/hep/dataforge/meta/Meta.kt | 5 -- .../kotlin/hep/dataforge/meta/MetaBuilder.kt | 0 .../hep/dataforge/meta/MutableMetaNode.kt | 0 .../hep/dataforge/meta/Specification.kt | 0 .../kotlin/hep/dataforge/meta/Styleable.kt | 0 .../main/kotlin/hep/dataforge/meta/Value.kt | 0 .../main/kotlin/hep/dataforge/names/Name.kt | 0 .../hep/dataforge/meta/MetaBuilderTest.kt | 0 .../hep/dataforge/meta/MetaDelegateTest.kt | 0 .../hep/dataforge/meta/SerializationTest.kt | 0 .../kotlin/hep/dataforge/names/NameTest.kt | 0 settings.gradle | 12 ++- 23 files changed, 118 insertions(+), 57 deletions(-) create mode 100644 dataforge-envelope/build.gradle create mode 100644 dataforge-envelope/src/main/kotlin/hep/dataforge/envelopes/Envelope.kt rename {dataforge-meta-common => dataforge-meta}/build.gradle (72%) create mode 100644 dataforge-meta/dataforge-meta-io/build.gradle create mode 100644 dataforge-meta/dataforge-meta-io/src/main/kotlin/hep/dataforge/meta/io/MetaFormat.kt rename {dataforge-meta-common/src/main/kotlin/hep/dataforge/meta => dataforge-meta/dataforge-meta-io/src/main/kotlin/hep/dataforge/meta/io}/Serializers.kt (77%) rename {dataforge-meta-js => dataforge-meta/dataforge-meta-js}/build.gradle (55%) rename {dataforge-meta-jvm => dataforge-meta/dataforge-meta-jvm}/build.gradle (60%) rename {dataforge-meta-common => dataforge-meta}/src/main/kotlin/hep/dataforge/meta/Config.kt (100%) rename {dataforge-meta-common => dataforge-meta}/src/main/kotlin/hep/dataforge/meta/Delegates.kt (100%) rename {dataforge-meta-common => dataforge-meta}/src/main/kotlin/hep/dataforge/meta/Meta.kt (98%) rename {dataforge-meta-common => dataforge-meta}/src/main/kotlin/hep/dataforge/meta/MetaBuilder.kt (100%) rename {dataforge-meta-common => dataforge-meta}/src/main/kotlin/hep/dataforge/meta/MutableMetaNode.kt (100%) rename {dataforge-meta-common => dataforge-meta}/src/main/kotlin/hep/dataforge/meta/Specification.kt (100%) rename {dataforge-meta-common => dataforge-meta}/src/main/kotlin/hep/dataforge/meta/Styleable.kt (100%) rename {dataforge-meta-common => dataforge-meta}/src/main/kotlin/hep/dataforge/meta/Value.kt (100%) rename {dataforge-meta-common => dataforge-meta}/src/main/kotlin/hep/dataforge/names/Name.kt (100%) rename {dataforge-meta-common => dataforge-meta}/src/test/kotlin/hep/dataforge/meta/MetaBuilderTest.kt (100%) rename {dataforge-meta-common => dataforge-meta}/src/test/kotlin/hep/dataforge/meta/MetaDelegateTest.kt (100%) rename {dataforge-meta-common => dataforge-meta}/src/test/kotlin/hep/dataforge/meta/SerializationTest.kt (100%) rename {dataforge-meta-common => dataforge-meta}/src/test/kotlin/hep/dataforge/names/NameTest.kt (100%) diff --git a/build.gradle b/build.gradle index ae66ee48..96797a98 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,7 @@ buildscript { ext.kotlin_version = '1.3.0-rc-57' ext.serialization_version = '0.8.0-rc13' + ext.kotlinx_io_version = '0.1.0-alpha-15-rc13' repositories { jcenter() maven { @@ -34,7 +35,3 @@ allprojects { //maven { url 'https://jitpack.io' } } } - -subprojects { - apply plugin: 'kotlinx-serialization' -} diff --git a/dataforge-envelope/build.gradle b/dataforge-envelope/build.gradle new file mode 100644 index 00000000..fd1ea5e8 --- /dev/null +++ b/dataforge-envelope/build.gradle @@ -0,0 +1,13 @@ +plugins{ + id 'kotlin-platform-common' +} + +dependencies { + compile project(":dataforge-meta") + + compile "org.jetbrains.kotlinx:kotlinx-io:$kotlinx_io_version" + + compile "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version" + testCompile "org.jetbrains.kotlin:kotlin-test-annotations-common" + testCompile "org.jetbrains.kotlin:kotlin-test-common" +} \ No newline at end of file diff --git a/dataforge-envelope/src/main/kotlin/hep/dataforge/envelopes/Envelope.kt b/dataforge-envelope/src/main/kotlin/hep/dataforge/envelopes/Envelope.kt new file mode 100644 index 00000000..9368a452 --- /dev/null +++ b/dataforge-envelope/src/main/kotlin/hep/dataforge/envelopes/Envelope.kt @@ -0,0 +1,9 @@ +package hep.dataforge.envelopes + +import hep.dataforge.meta.Meta +import kotlinx.io.core.IoBuffer + +interface Envelope{ + val meta: Meta + val data: IoBuffer +} \ No newline at end of file diff --git a/dataforge-meta-common/build.gradle b/dataforge-meta/build.gradle similarity index 72% rename from dataforge-meta-common/build.gradle rename to dataforge-meta/build.gradle index a396466f..e13aef58 100644 --- a/dataforge-meta-common/build.gradle +++ b/dataforge-meta/build.gradle @@ -4,7 +4,7 @@ plugins{ dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version" - compile "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version" + testCompile "org.jetbrains.kotlin:kotlin-test-annotations-common" testCompile "org.jetbrains.kotlin:kotlin-test-common" } \ No newline at end of file diff --git a/dataforge-meta/dataforge-meta-io/build.gradle b/dataforge-meta/dataforge-meta-io/build.gradle new file mode 100644 index 00000000..e56fdc44 --- /dev/null +++ b/dataforge-meta/dataforge-meta-io/build.gradle @@ -0,0 +1,19 @@ +plugins{ + id 'kotlin-platform-common' +} + +dependencies { + compile project(":dataforge-meta") + + + compile "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version" + compile "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version" + compile "org.jetbrains.kotlinx:kotlinx-io:$kotlinx_io_version" + + testCompile "org.jetbrains.kotlin:kotlin-test-annotations-common" + testCompile "org.jetbrains.kotlin:kotlin-test-common" +} + +allprojects { + apply plugin: 'kotlinx-serialization' +} \ No newline at end of file diff --git a/dataforge-meta/dataforge-meta-io/src/main/kotlin/hep/dataforge/meta/io/MetaFormat.kt b/dataforge-meta/dataforge-meta-io/src/main/kotlin/hep/dataforge/meta/io/MetaFormat.kt new file mode 100644 index 00000000..75cd9773 --- /dev/null +++ b/dataforge-meta/dataforge-meta-io/src/main/kotlin/hep/dataforge/meta/io/MetaFormat.kt @@ -0,0 +1,26 @@ +package hep.dataforge.meta.io + +import hep.dataforge.meta.Meta +import kotlinx.io.core.Input +import kotlinx.io.core.Output + +/** + * A format for meta serialization + */ +interface MetaFormat { + val name : String + val key : Short + + suspend fun write(meta: Meta, out: Output) + suspend fun read(input: Input): Meta +} + +/** + * Resolve format by its name. Null if not provided + */ +expect fun resolveFormat(name: String): MetaFormat? + +/** + * Resolve format by its binary key. Null if not provided + */ +expect fun resolveFormat(key: Short): MetaFormat? \ No newline at end of file diff --git a/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Serializers.kt b/dataforge-meta/dataforge-meta-io/src/main/kotlin/hep/dataforge/meta/io/Serializers.kt similarity index 77% rename from dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Serializers.kt rename to dataforge-meta/dataforge-meta-io/src/main/kotlin/hep/dataforge/meta/io/Serializers.kt index 1fd0a316..2297673e 100644 --- a/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Serializers.kt +++ b/dataforge-meta/dataforge-meta-io/src/main/kotlin/hep/dataforge/meta/io/Serializers.kt @@ -1,46 +1,47 @@ -package hep.dataforge.meta +package hep.dataforge.meta.io -import kotlinx.serialization.json.* +import hep.dataforge.meta.* +import kotlinx.serialization.Serializable /*Universal serialization*/ -//sealed class MetaItemProxy { -// -// @Serializable -// class NumberValueProxy(val number: Number) : MetaItemProxy() -// -// @Serializable -// class StringValueProxy(val string: String) : MetaItemProxy() -// -// @Serializable -// class BooleanValueProxy(val boolean: Boolean) : MetaItemProxy() -// -// @Serializable -// object NullValueProxy : MetaItemProxy() -// -// @Serializable -// class MetaProxy(@Serializable val map: Map) : MetaItemProxy() -// -// @Serializable -// class MetaListProxy(@Serializable val nodes: List) : MetaItemProxy() -//} -// -// -//fun Meta.toMap(): Map { -// return this.items.mapValues { (_, value) -> -// when (value) { -// is MetaItem.ValueItem -> when (value.value.type) { -// ValueType.NUMBER -> MetaItemProxy.NumberValueProxy(value.value.number) -// ValueType.STRING -> MetaItemProxy.StringValueProxy(value.value.string) -// ValueType.BOOLEAN -> MetaItemProxy.BooleanValueProxy(value.value.boolean) -// ValueType.NULL -> MetaItemProxy.NullValueProxy -// } -// is MetaItem.SingleNodeItem -> MetaItemProxy.MetaProxy(value.node.toMap()) -// is MetaItem.MultiNodeItem -> MetaItemProxy.MetaListProxy(value.nodes.map { MetaItemProxy.MetaProxy(it.toMap()) }) -// } -// } -//} +sealed class MetaItemProxy { + + @Serializable + class NumberValueProxy(val number: Number) : MetaItemProxy() + + @Serializable + class StringValueProxy(val string: String) : MetaItemProxy() + + @Serializable + class BooleanValueProxy(val boolean: Boolean) : MetaItemProxy() + + @Serializable + object NullValueProxy : MetaItemProxy() + + @Serializable + class MetaProxy(@Serializable val map: Map) : MetaItemProxy() + + @Serializable + class MetaListProxy(@Serializable val nodes: List) : MetaItemProxy() +} + + +fun Meta.toMap(): Map { + return this.items.mapValues { (_, value) -> + when (value) { + is MetaItem.ValueItem<*> -> when (value.value.type) { + ValueType.NUMBER -> MetaItemProxy.NumberValueProxy(value.value.number) + ValueType.STRING -> MetaItemProxy.StringValueProxy(value.value.string) + ValueType.BOOLEAN -> MetaItemProxy.BooleanValueProxy(value.value.boolean) + ValueType.NULL -> MetaItemProxy.NullValueProxy + } + is MetaItem.SingleNodeItem<*> -> MetaItemProxy.MetaProxy(value.node.toMap()) + is MetaItem.MultiNodeItem<*> -> MetaItemProxy.MetaListProxy(value.nodes.map { MetaItemProxy.MetaProxy(it.toMap()) }) + } + } +} /*Direct JSON serialization*/ diff --git a/dataforge-meta-js/build.gradle b/dataforge-meta/dataforge-meta-js/build.gradle similarity index 55% rename from dataforge-meta-js/build.gradle rename to dataforge-meta/dataforge-meta-js/build.gradle index f884412d..af0d28ca 100644 --- a/dataforge-meta-js/build.gradle +++ b/dataforge-meta/dataforge-meta-js/build.gradle @@ -3,9 +3,8 @@ plugins{ } dependencies { - expectedBy project(":dataforge-meta-common") + expectedBy project(":dataforge-meta") compile "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version" - compile "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version" testCompile "org.jetbrains.kotlin:kotlin-test-js" } diff --git a/dataforge-meta-jvm/build.gradle b/dataforge-meta/dataforge-meta-jvm/build.gradle similarity index 60% rename from dataforge-meta-jvm/build.gradle rename to dataforge-meta/dataforge-meta-jvm/build.gradle index f371911a..1f4379b0 100644 --- a/dataforge-meta-jvm/build.gradle +++ b/dataforge-meta/dataforge-meta-jvm/build.gradle @@ -3,11 +3,9 @@ plugins{ } dependencies { - expectedBy project(":dataforge-meta-common") + expectedBy project(":dataforge-meta") compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" - compile "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version" testCompile "org.jetbrains.kotlin:kotlin-test" testCompile "org.jetbrains.kotlin:kotlin-test-junit" } diff --git a/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Config.kt b/dataforge-meta/src/main/kotlin/hep/dataforge/meta/Config.kt similarity index 100% rename from dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Config.kt rename to dataforge-meta/src/main/kotlin/hep/dataforge/meta/Config.kt diff --git a/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Delegates.kt b/dataforge-meta/src/main/kotlin/hep/dataforge/meta/Delegates.kt similarity index 100% rename from dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Delegates.kt rename to dataforge-meta/src/main/kotlin/hep/dataforge/meta/Delegates.kt diff --git a/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/main/kotlin/hep/dataforge/meta/Meta.kt similarity index 98% rename from dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Meta.kt rename to dataforge-meta/src/main/kotlin/hep/dataforge/meta/Meta.kt index b84a84e7..2d5eb1f1 100644 --- a/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/main/kotlin/hep/dataforge/meta/Meta.kt @@ -96,11 +96,6 @@ abstract class MetaNode> : Meta { override fun hashCode(): Int { return items.hashCode() } - - override fun toString(): String { - return toJSON().toString() - } - } /** diff --git a/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/MetaBuilder.kt b/dataforge-meta/src/main/kotlin/hep/dataforge/meta/MetaBuilder.kt similarity index 100% rename from dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/MetaBuilder.kt rename to dataforge-meta/src/main/kotlin/hep/dataforge/meta/MetaBuilder.kt diff --git a/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/MutableMetaNode.kt b/dataforge-meta/src/main/kotlin/hep/dataforge/meta/MutableMetaNode.kt similarity index 100% rename from dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/MutableMetaNode.kt rename to dataforge-meta/src/main/kotlin/hep/dataforge/meta/MutableMetaNode.kt diff --git a/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Specification.kt b/dataforge-meta/src/main/kotlin/hep/dataforge/meta/Specification.kt similarity index 100% rename from dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Specification.kt rename to dataforge-meta/src/main/kotlin/hep/dataforge/meta/Specification.kt diff --git a/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Styleable.kt b/dataforge-meta/src/main/kotlin/hep/dataforge/meta/Styleable.kt similarity index 100% rename from dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Styleable.kt rename to dataforge-meta/src/main/kotlin/hep/dataforge/meta/Styleable.kt diff --git a/dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Value.kt b/dataforge-meta/src/main/kotlin/hep/dataforge/meta/Value.kt similarity index 100% rename from dataforge-meta-common/src/main/kotlin/hep/dataforge/meta/Value.kt rename to dataforge-meta/src/main/kotlin/hep/dataforge/meta/Value.kt diff --git a/dataforge-meta-common/src/main/kotlin/hep/dataforge/names/Name.kt b/dataforge-meta/src/main/kotlin/hep/dataforge/names/Name.kt similarity index 100% rename from dataforge-meta-common/src/main/kotlin/hep/dataforge/names/Name.kt rename to dataforge-meta/src/main/kotlin/hep/dataforge/names/Name.kt diff --git a/dataforge-meta-common/src/test/kotlin/hep/dataforge/meta/MetaBuilderTest.kt b/dataforge-meta/src/test/kotlin/hep/dataforge/meta/MetaBuilderTest.kt similarity index 100% rename from dataforge-meta-common/src/test/kotlin/hep/dataforge/meta/MetaBuilderTest.kt rename to dataforge-meta/src/test/kotlin/hep/dataforge/meta/MetaBuilderTest.kt diff --git a/dataforge-meta-common/src/test/kotlin/hep/dataforge/meta/MetaDelegateTest.kt b/dataforge-meta/src/test/kotlin/hep/dataforge/meta/MetaDelegateTest.kt similarity index 100% rename from dataforge-meta-common/src/test/kotlin/hep/dataforge/meta/MetaDelegateTest.kt rename to dataforge-meta/src/test/kotlin/hep/dataforge/meta/MetaDelegateTest.kt diff --git a/dataforge-meta-common/src/test/kotlin/hep/dataforge/meta/SerializationTest.kt b/dataforge-meta/src/test/kotlin/hep/dataforge/meta/SerializationTest.kt similarity index 100% rename from dataforge-meta-common/src/test/kotlin/hep/dataforge/meta/SerializationTest.kt rename to dataforge-meta/src/test/kotlin/hep/dataforge/meta/SerializationTest.kt diff --git a/dataforge-meta-common/src/test/kotlin/hep/dataforge/names/NameTest.kt b/dataforge-meta/src/test/kotlin/hep/dataforge/names/NameTest.kt similarity index 100% rename from dataforge-meta-common/src/test/kotlin/hep/dataforge/names/NameTest.kt rename to dataforge-meta/src/test/kotlin/hep/dataforge/names/NameTest.kt diff --git a/settings.gradle b/settings.gradle index 655d929b..3cd11386 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,8 +14,12 @@ pluginManagement { } } -rootProject.name = 'dataforge-meta' +rootProject.name = 'dataforge-core' -include ":dataforge-meta-common" -include ":dataforge-meta-jvm" -include ":dataforge-meta-js" \ No newline at end of file +include ":dataforge-meta" +include ":dataforge-meta:dataforge-meta-jvm" +include ":dataforge-meta:dataforge-meta-js" + +include ":dataforge-meta:dataforge-meta-io" + +include ":dataforge-envelope" \ No newline at end of file From c1d004ec3e9c3cc45474f0a59a7b78db98f7382d Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 25 Sep 2018 22:35:28 +0300 Subject: [PATCH 04/37] Moved to mpp structure --- build.gradle | 6 +- dataforge-meta-io/build.gradle | 65 +++++++++++++++++ .../hep/dataforge/meta/io/MetaFormat.kt | 18 ++--- .../kotlin/hep/dataforge/meta/io/MetaProxy.kt | 51 ++++++++++++++ .../hep/dataforge/meta/io/Serializers.kt | 42 +---------- .../commonTest/kotlin/io/MetaItemProxyTest.kt | 47 +++++++++++++ dataforge-meta/build.gradle | 69 ++++++++++++++++--- dataforge-meta/dataforge-meta-io/build.gradle | 19 ----- dataforge-meta/dataforge-meta-js/build.gradle | 10 --- .../dataforge-meta-jvm/build.gradle | 19 ----- .../kotlin/hep/dataforge/meta/Config.kt | 0 .../kotlin/hep/dataforge/meta/Delegates.kt | 0 .../kotlin/hep/dataforge/meta/Meta.kt | 0 .../kotlin/hep/dataforge/meta/MetaBuilder.kt | 0 .../hep/dataforge/meta/MutableMetaNode.kt | 0 .../hep/dataforge/meta/Specification.kt | 0 .../kotlin/hep/dataforge/meta/Styleable.kt | 0 .../kotlin/hep/dataforge/meta/Value.kt | 0 .../kotlin/hep/dataforge/names/Name.kt | 0 .../hep/dataforge/meta/MetaBuilderTest.kt | 0 .../hep/dataforge/meta/MetaDelegateTest.kt | 0 .../kotlin/hep/dataforge/names/NameTest.kt | 0 .../hep/dataforge/meta/SerializationTest.kt | 52 -------------- settings.gradle | 15 ++-- 24 files changed, 248 insertions(+), 165 deletions(-) create mode 100644 dataforge-meta-io/build.gradle rename {dataforge-meta/dataforge-meta-io/src/main => dataforge-meta-io/src/commonMain}/kotlin/hep/dataforge/meta/io/MetaFormat.kt (56%) create mode 100644 dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaProxy.kt rename {dataforge-meta/dataforge-meta-io/src/main => dataforge-meta-io/src/commonMain}/kotlin/hep/dataforge/meta/io/Serializers.kt (77%) create mode 100644 dataforge-meta-io/src/commonTest/kotlin/io/MetaItemProxyTest.kt delete mode 100644 dataforge-meta/dataforge-meta-io/build.gradle delete mode 100644 dataforge-meta/dataforge-meta-js/build.gradle delete mode 100644 dataforge-meta/dataforge-meta-jvm/build.gradle rename dataforge-meta/src/{main => commonMain}/kotlin/hep/dataforge/meta/Config.kt (100%) rename dataforge-meta/src/{main => commonMain}/kotlin/hep/dataforge/meta/Delegates.kt (100%) rename dataforge-meta/src/{main => commonMain}/kotlin/hep/dataforge/meta/Meta.kt (100%) rename dataforge-meta/src/{main => commonMain}/kotlin/hep/dataforge/meta/MetaBuilder.kt (100%) rename dataforge-meta/src/{main => commonMain}/kotlin/hep/dataforge/meta/MutableMetaNode.kt (100%) rename dataforge-meta/src/{main => commonMain}/kotlin/hep/dataforge/meta/Specification.kt (100%) rename dataforge-meta/src/{main => commonMain}/kotlin/hep/dataforge/meta/Styleable.kt (100%) rename dataforge-meta/src/{main => commonMain}/kotlin/hep/dataforge/meta/Value.kt (100%) rename dataforge-meta/src/{main => commonMain}/kotlin/hep/dataforge/names/Name.kt (100%) rename dataforge-meta/src/{test => commonTest}/kotlin/hep/dataforge/meta/MetaBuilderTest.kt (100%) rename dataforge-meta/src/{test => commonTest}/kotlin/hep/dataforge/meta/MetaDelegateTest.kt (100%) rename dataforge-meta/src/{test => commonTest}/kotlin/hep/dataforge/names/NameTest.kt (100%) delete mode 100644 dataforge-meta/src/test/kotlin/hep/dataforge/meta/SerializationTest.kt diff --git a/build.gradle b/build.gradle index 96797a98..25c077f0 100644 --- a/build.gradle +++ b/build.gradle @@ -24,14 +24,14 @@ buildscript { description = "The basic interfaces for DataForge meta-data" -group 'hep.dataforge' -version '0.1.1-SNAPSHOT' +group = 'hep.dataforge' +version = '0.1.1-SNAPSHOT' allprojects { repositories { jcenter() maven { url = "http://dl.bintray.com/kotlin/kotlin-eap" } - maven { url "https://kotlin.bintray.com/kotlinx" } + maven { url = "https://kotlin.bintray.com/kotlinx" } //maven { url 'https://jitpack.io' } } } diff --git a/dataforge-meta-io/build.gradle b/dataforge-meta-io/build.gradle new file mode 100644 index 00000000..51ffa452 --- /dev/null +++ b/dataforge-meta-io/build.gradle @@ -0,0 +1,65 @@ +plugins { + id 'kotlin-multiplatform'// version '1.3.0-rc-57' + id 'kotlinx-serialization' +} +repositories { + maven { url = 'http://dl.bintray.com/kotlin/kotlin-eap' } + mavenCentral() +} +kotlin { + targets { + fromPreset(presets.jvm, 'jvm') + fromPreset(presets.js, 'js') + // For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64 + // For Linux, preset should be changed to e.g. presets.linuxX64 + // For MacOS, preset should be changed to e.g. presets.macosX64 + //fromPreset(presets.iosX64, 'ios') + } + sourceSets { + commonMain { + dependencies { + implementation project(":dataforge-meta") + implementation "org.jetbrains.kotlin:kotlin-stdlib-common" + implementation 'org.jetbrains.kotlin:kotlin-reflect' + implementation "org.jetbrains.kotlinx:kotlinx-io:$kotlinx_io_version" + implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version" + } + } + commonTest { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-test-common' + implementation 'org.jetbrains.kotlin:kotlin-test-annotations-common' + implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version" + } + } + jvmMain { + dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + implementation "org.jetbrains.kotlinx:kotlinx-io-jvm:$kotlinx_io_version" + implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version" + } + } + jvmTest { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-test' + implementation 'org.jetbrains.kotlin:kotlin-test-junit' + } + } + jsMain { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-stdlib-js' + implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version" + } + } + jsTest { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-test-js' + implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version" + } + } +// iosMain { +// } +// iosTest { +// } + } +} \ No newline at end of file diff --git a/dataforge-meta/dataforge-meta-io/src/main/kotlin/hep/dataforge/meta/io/MetaFormat.kt b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt similarity index 56% rename from dataforge-meta/dataforge-meta-io/src/main/kotlin/hep/dataforge/meta/io/MetaFormat.kt rename to dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt index 75cd9773..7eb3b641 100644 --- a/dataforge-meta/dataforge-meta-io/src/main/kotlin/hep/dataforge/meta/io/MetaFormat.kt +++ b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt @@ -15,12 +15,12 @@ interface MetaFormat { suspend fun read(input: Input): Meta } -/** - * Resolve format by its name. Null if not provided - */ -expect fun resolveFormat(name: String): MetaFormat? - -/** - * Resolve format by its binary key. Null if not provided - */ -expect fun resolveFormat(key: Short): MetaFormat? \ No newline at end of file +///** +// * Resolve format by its name. Null if not provided +// */ +//expect fun resolveFormat(name: String): MetaFormat? +// +///** +// * Resolve format by its binary key. Null if not provided +// */ +//expect fun resolveFormat(key: Short): MetaFormat? \ No newline at end of file diff --git a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaProxy.kt b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaProxy.kt new file mode 100644 index 00000000..3e1e1681 --- /dev/null +++ b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaProxy.kt @@ -0,0 +1,51 @@ +package hep.dataforge.meta.io + +import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaItem +import hep.dataforge.meta.ValueType +import hep.dataforge.meta.boolean +import kotlinx.serialization.Polymorphic +import kotlinx.serialization.Serializable + +/*Universal serialization*/ + +@Serializable +class MetaProxy(val map: Map) + + +@Serializable +sealed class MetaItemProxy { + @Serializable + class NumberValueProxy(val number: Number) : MetaItemProxy() + + @Serializable + class StringValueProxy(val string: String) : MetaItemProxy() + + @Serializable + class BooleanValueProxy(val boolean: Boolean) : MetaItemProxy() + + @Serializable + object NullValueProxy : MetaItemProxy() + + @Serializable + class SingleMetaProxy(val node: MetaProxy) : MetaItemProxy() + + @Serializable + class MetaListProxy(val list: List<@Polymorphic MetaProxy>) : MetaItemProxy() +} + + +fun Meta.toMap(): MetaProxy { + return MetaProxy(this.items.mapValues { (_, value) -> + when (value) { + is MetaItem.ValueItem<*> -> when (value.value.type) { + ValueType.NUMBER -> MetaItemProxy.NumberValueProxy(value.value.number) + ValueType.STRING -> MetaItemProxy.StringValueProxy(value.value.string) + ValueType.BOOLEAN -> MetaItemProxy.BooleanValueProxy(value.value.boolean) + ValueType.NULL -> MetaItemProxy.NullValueProxy + } + is MetaItem.SingleNodeItem<*> -> MetaItemProxy.SingleMetaProxy(value.node.toMap()) + is MetaItem.MultiNodeItem<*> -> MetaItemProxy.MetaListProxy(value.nodes.map { it.toMap() }) + } + }) +} diff --git a/dataforge-meta/dataforge-meta-io/src/main/kotlin/hep/dataforge/meta/io/Serializers.kt b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Serializers.kt similarity index 77% rename from dataforge-meta/dataforge-meta-io/src/main/kotlin/hep/dataforge/meta/io/Serializers.kt rename to dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Serializers.kt index 2297673e..d52b7be1 100644 --- a/dataforge-meta/dataforge-meta-io/src/main/kotlin/hep/dataforge/meta/io/Serializers.kt +++ b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Serializers.kt @@ -1,47 +1,7 @@ package hep.dataforge.meta.io import hep.dataforge.meta.* -import kotlinx.serialization.Serializable - - -/*Universal serialization*/ - -sealed class MetaItemProxy { - - @Serializable - class NumberValueProxy(val number: Number) : MetaItemProxy() - - @Serializable - class StringValueProxy(val string: String) : MetaItemProxy() - - @Serializable - class BooleanValueProxy(val boolean: Boolean) : MetaItemProxy() - - @Serializable - object NullValueProxy : MetaItemProxy() - - @Serializable - class MetaProxy(@Serializable val map: Map) : MetaItemProxy() - - @Serializable - class MetaListProxy(@Serializable val nodes: List) : MetaItemProxy() -} - - -fun Meta.toMap(): Map { - return this.items.mapValues { (_, value) -> - when (value) { - is MetaItem.ValueItem<*> -> when (value.value.type) { - ValueType.NUMBER -> MetaItemProxy.NumberValueProxy(value.value.number) - ValueType.STRING -> MetaItemProxy.StringValueProxy(value.value.string) - ValueType.BOOLEAN -> MetaItemProxy.BooleanValueProxy(value.value.boolean) - ValueType.NULL -> MetaItemProxy.NullValueProxy - } - is MetaItem.SingleNodeItem<*> -> MetaItemProxy.MetaProxy(value.node.toMap()) - is MetaItem.MultiNodeItem<*> -> MetaItemProxy.MetaListProxy(value.nodes.map { MetaItemProxy.MetaProxy(it.toMap()) }) - } - } -} +import kotlinx.serialization.json.* /*Direct JSON serialization*/ diff --git a/dataforge-meta-io/src/commonTest/kotlin/io/MetaItemProxyTest.kt b/dataforge-meta-io/src/commonTest/kotlin/io/MetaItemProxyTest.kt new file mode 100644 index 00000000..cd12d611 --- /dev/null +++ b/dataforge-meta-io/src/commonTest/kotlin/io/MetaItemProxyTest.kt @@ -0,0 +1,47 @@ +package io + +import hep.dataforge.meta.buildMeta +import hep.dataforge.meta.io.MetaItemProxy +import hep.dataforge.meta.io.toMap +import kotlinx.serialization.json.JSON +import kotlinx.serialization.serializer +import kotlin.test.Test + +class MetaItemProxyTest { + @Test + fun testGeneration() { + MetaItemProxy::class.serializer() + } + + + @Test + fun testProxySerialization() { + val meta = buildMeta { + "a" to 2 + "b" to { + "c" to "ddd" + "d" to 2.2 + } + } + val json = JSON.indented.stringify(meta.toMap()) + println(json) +// val result: Map = JSON.parse(json) +// assertEquals(meta,result.to) + } + +// @Test +// fun testJSONSerialization() { +// val meta = buildMeta { +// "a" to 2 +// "b" to { +// "c" to "ddd" +// "d" to 2.2 +// } +// } +// val json = meta.toJSON() +// println(json) +// val result = json.toMeta() +// assertEquals(meta, result) +// } + +} \ No newline at end of file diff --git a/dataforge-meta/build.gradle b/dataforge-meta/build.gradle index e13aef58..34599174 100644 --- a/dataforge-meta/build.gradle +++ b/dataforge-meta/build.gradle @@ -1,10 +1,63 @@ -plugins{ - id 'kotlin-platform-common' +plugins { + id 'kotlin-multiplatform'// version '1.3.0-rc-57' + id 'kotlinx-serialization' } - -dependencies { - compile "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version" - - testCompile "org.jetbrains.kotlin:kotlin-test-annotations-common" - testCompile "org.jetbrains.kotlin:kotlin-test-common" +repositories { + maven { url = 'http://dl.bintray.com/kotlin/kotlin-eap' } + mavenCentral() +} +kotlin { + targets { + fromPreset(presets.jvm, 'jvm') + fromPreset(presets.js, 'js') + // For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64 + // For Linux, preset should be changed to e.g. presets.linuxX64 + // For MacOS, preset should be changed to e.g. presets.macosX64 + //fromPreset(presets.iosX64, 'ios') + } + sourceSets { + commonMain { + dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-common" + implementation 'org.jetbrains.kotlin:kotlin-reflect' + implementation "org.jetbrains.kotlinx:kotlinx-io:$kotlinx_io_version" + implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version" + } + } + commonTest { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-test-common' + implementation 'org.jetbrains.kotlin:kotlin-test-annotations-common' + implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version" + } + } + jvmMain { + dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version" + } + } + jvmTest { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-test' + implementation 'org.jetbrains.kotlin:kotlin-test-junit' + } + } + jsMain { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-stdlib-js' + implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version" + } + } + jsTest { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-test-js' + implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version" + } + } +// iosMain { +// } +// iosTest { +// } + } } \ No newline at end of file diff --git a/dataforge-meta/dataforge-meta-io/build.gradle b/dataforge-meta/dataforge-meta-io/build.gradle deleted file mode 100644 index e56fdc44..00000000 --- a/dataforge-meta/dataforge-meta-io/build.gradle +++ /dev/null @@ -1,19 +0,0 @@ -plugins{ - id 'kotlin-platform-common' -} - -dependencies { - compile project(":dataforge-meta") - - - compile "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version" - compile "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version" - compile "org.jetbrains.kotlinx:kotlinx-io:$kotlinx_io_version" - - testCompile "org.jetbrains.kotlin:kotlin-test-annotations-common" - testCompile "org.jetbrains.kotlin:kotlin-test-common" -} - -allprojects { - apply plugin: 'kotlinx-serialization' -} \ No newline at end of file diff --git a/dataforge-meta/dataforge-meta-js/build.gradle b/dataforge-meta/dataforge-meta-js/build.gradle deleted file mode 100644 index af0d28ca..00000000 --- a/dataforge-meta/dataforge-meta-js/build.gradle +++ /dev/null @@ -1,10 +0,0 @@ -plugins{ - id 'kotlin-platform-js' -} - -dependencies { - expectedBy project(":dataforge-meta") - - compile "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version" - testCompile "org.jetbrains.kotlin:kotlin-test-js" -} diff --git a/dataforge-meta/dataforge-meta-jvm/build.gradle b/dataforge-meta/dataforge-meta-jvm/build.gradle deleted file mode 100644 index 1f4379b0..00000000 --- a/dataforge-meta/dataforge-meta-jvm/build.gradle +++ /dev/null @@ -1,19 +0,0 @@ -plugins{ - id 'kotlin-platform-jvm' -} - -dependencies { - expectedBy project(":dataforge-meta") - - compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - testCompile "org.jetbrains.kotlin:kotlin-test" - testCompile "org.jetbrains.kotlin:kotlin-test-junit" -} - -compileKotlin { - kotlinOptions.jvmTarget = "1.8" -} -compileTestKotlin { - kotlinOptions.jvmTarget = "1.8" -} -sourceCompatibility = "1.8" \ No newline at end of file diff --git a/dataforge-meta/src/main/kotlin/hep/dataforge/meta/Config.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt similarity index 100% rename from dataforge-meta/src/main/kotlin/hep/dataforge/meta/Config.kt rename to dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt diff --git a/dataforge-meta/src/main/kotlin/hep/dataforge/meta/Delegates.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Delegates.kt similarity index 100% rename from dataforge-meta/src/main/kotlin/hep/dataforge/meta/Delegates.kt rename to dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Delegates.kt diff --git a/dataforge-meta/src/main/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt similarity index 100% rename from dataforge-meta/src/main/kotlin/hep/dataforge/meta/Meta.kt rename to dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt diff --git a/dataforge-meta/src/main/kotlin/hep/dataforge/meta/MetaBuilder.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt similarity index 100% rename from dataforge-meta/src/main/kotlin/hep/dataforge/meta/MetaBuilder.kt rename to dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt diff --git a/dataforge-meta/src/main/kotlin/hep/dataforge/meta/MutableMetaNode.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt similarity index 100% rename from dataforge-meta/src/main/kotlin/hep/dataforge/meta/MutableMetaNode.kt rename to dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt diff --git a/dataforge-meta/src/main/kotlin/hep/dataforge/meta/Specification.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt similarity index 100% rename from dataforge-meta/src/main/kotlin/hep/dataforge/meta/Specification.kt rename to dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt diff --git a/dataforge-meta/src/main/kotlin/hep/dataforge/meta/Styleable.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styleable.kt similarity index 100% rename from dataforge-meta/src/main/kotlin/hep/dataforge/meta/Styleable.kt rename to dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styleable.kt diff --git a/dataforge-meta/src/main/kotlin/hep/dataforge/meta/Value.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Value.kt similarity index 100% rename from dataforge-meta/src/main/kotlin/hep/dataforge/meta/Value.kt rename to dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Value.kt diff --git a/dataforge-meta/src/main/kotlin/hep/dataforge/names/Name.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt similarity index 100% rename from dataforge-meta/src/main/kotlin/hep/dataforge/names/Name.kt rename to dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt diff --git a/dataforge-meta/src/test/kotlin/hep/dataforge/meta/MetaBuilderTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaBuilderTest.kt similarity index 100% rename from dataforge-meta/src/test/kotlin/hep/dataforge/meta/MetaBuilderTest.kt rename to dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaBuilderTest.kt diff --git a/dataforge-meta/src/test/kotlin/hep/dataforge/meta/MetaDelegateTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaDelegateTest.kt similarity index 100% rename from dataforge-meta/src/test/kotlin/hep/dataforge/meta/MetaDelegateTest.kt rename to dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaDelegateTest.kt diff --git a/dataforge-meta/src/test/kotlin/hep/dataforge/names/NameTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/names/NameTest.kt similarity index 100% rename from dataforge-meta/src/test/kotlin/hep/dataforge/names/NameTest.kt rename to dataforge-meta/src/commonTest/kotlin/hep/dataforge/names/NameTest.kt diff --git a/dataforge-meta/src/test/kotlin/hep/dataforge/meta/SerializationTest.kt b/dataforge-meta/src/test/kotlin/hep/dataforge/meta/SerializationTest.kt deleted file mode 100644 index b1e28ab7..00000000 --- a/dataforge-meta/src/test/kotlin/hep/dataforge/meta/SerializationTest.kt +++ /dev/null @@ -1,52 +0,0 @@ -package hep.dataforge.meta - -import kotlin.test.Test -import kotlin.test.assertEquals - -class SerializationTest { - - @Test - fun testJSONSerialization() { - val meta = buildMeta { - "a" to 2 - "b" to { - "c" to "ddd" - "d" to 2.2 - } - } - val json = meta.toJSON() - println(json) - val result = json.toMeta() - assertEquals(meta, result) - } - -// @Test -// fun testIndirectSerialization() { -// val meta = buildMeta { -// "a" to 2 -// "b" to { -// "c" to "ddd" -// "d" to 2.2 -// } -// } -// val json = JSON.stringify(meta.toMap()) -// println(json) -//// val result = json.toMeta() -//// assertEquals(meta, result) -// } - -// @Test -// fun testWeirdSerialization() { -// val meta = buildMeta { -// "a" to 2 -// "b" to { -// "c" to "ddd" -// "d" to 2.2 -// } -// } -// val json = JSON.stringify(meta.toJSON()) -// println(json) -// val result: JsonObject = JSON.parse(json) -// assertEquals(meta, result.toMeta()) -// } -} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 3cd11386..665278b9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -10,16 +10,23 @@ pluginManagement { if (requested.id.id == "kotlin-platform-js") { useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") } + if (requested.id.id == "kotlin-multiplatform") { + useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") + } } } + + repositories { + maven { url = 'http://dl.bintray.com/kotlin/kotlin-eap' } + mavenCentral() + maven { url = 'https://plugins.gradle.org/m2/' } + } } + rootProject.name = 'dataforge-core' include ":dataforge-meta" -include ":dataforge-meta:dataforge-meta-jvm" -include ":dataforge-meta:dataforge-meta-js" - -include ":dataforge-meta:dataforge-meta-io" +include ":dataforge-meta-io" include ":dataforge-envelope" \ No newline at end of file From 13a00d0d19cb8c923e31aae3ae6dc87b37c98ea7 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 26 Oct 2018 14:10:41 +0300 Subject: [PATCH 05/37] Json serialization for JVM and Binary serialization in common --- .gitignore | 1 + build.gradle | 29 ++-- dataforge-envelope/build.gradle | 13 -- .../hep/dataforge/envelopes/Envelope.kt | 9 - dataforge-meta-io/build.gradle | 35 +--- .../hep/dataforge/meta/io/MetaFormat.kt | 155 +++++++++++++++++- .../kotlin/hep/dataforge/meta/io/MetaProxy.kt | 51 ------ .../hep/dataforge/meta/io/Serializers.kt | 145 ++++++---------- .../commonTest/kotlin/io/MetaItemProxyTest.kt | 47 ------ .../kotlin/hep/dataforge/meta/io/JSMeta.kt | 36 ++++ .../hep/dataforge/meta/io/JSONMetaFormat.kt | 17 ++ .../kotlin/hep/dataforge/meta/JSMetaTest.kt | 18 ++ .../hep/dataforge/meta/io/JSONMetaFormat.kt | 118 +++++++++++++ dataforge-meta/build.gradle | 27 ++- .../kotlin/hep/dataforge/meta/Meta.kt | 31 +--- .../kotlin/hep/dataforge/meta/MetaBuilder.kt | 2 +- settings.gradle | 3 +- 17 files changed, 432 insertions(+), 305 deletions(-) delete mode 100644 dataforge-envelope/build.gradle delete mode 100644 dataforge-envelope/src/main/kotlin/hep/dataforge/envelopes/Envelope.kt delete mode 100644 dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaProxy.kt delete mode 100644 dataforge-meta-io/src/commonTest/kotlin/io/MetaItemProxyTest.kt create mode 100644 dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSMeta.kt create mode 100644 dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt create mode 100644 dataforge-meta-io/src/jsTest/kotlin/hep/dataforge/meta/JSMetaTest.kt create mode 100644 dataforge-meta-io/src/jvmMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt diff --git a/.gitignore b/.gitignore index d35e96f9..534e4d13 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ out/ !gradle-wrapper.jar +artifactory.gradle \ No newline at end of file diff --git a/build.gradle b/build.gradle index 25c077f0..5dd5baab 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ buildscript { - ext.kotlin_version = '1.3.0-rc-57' - ext.serialization_version = '0.8.0-rc13' - ext.kotlinx_io_version = '0.1.0-alpha-15-rc13' + ext.kotlin_version = '1.3.0-rc-190' + ext.serialization_version = '0.8.3-rc13' + ext.kotlinx_io_version = '0.1.0-alpha-24-rc13' repositories { jcenter() maven { @@ -12,26 +12,25 @@ buildscript { dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4+" } } -//plugins { -// id 'kotlin-platform-common' version "$kotlin_version" apply false -// id 'kotlin-platform-jvm' version "$kotlin_version" apply false -// id 'kotlin-platform-js' version "$kotlin_version" apply false -// id 'kotlinx-serialization' version "$kotlin_version" apply false -//} - -description = "The basic interfaces for DataForge meta-data" - -group = 'hep.dataforge' -version = '0.1.1-SNAPSHOT' - allprojects { + apply plugin: 'maven-publish' + apply plugin: "com.jfrog.artifactory" + repositories { jcenter() maven { url = "http://dl.bintray.com/kotlin/kotlin-eap" } maven { url = "https://kotlin.bintray.com/kotlinx" } //maven { url 'https://jitpack.io' } } + + group = 'hep.dataforge' + version = '0.1.1-SNAPSHOT' } + +if(file('aritfactory.gradle').exists()) { + apply from: 'aritfactory.gradle' +} \ No newline at end of file diff --git a/dataforge-envelope/build.gradle b/dataforge-envelope/build.gradle deleted file mode 100644 index fd1ea5e8..00000000 --- a/dataforge-envelope/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -plugins{ - id 'kotlin-platform-common' -} - -dependencies { - compile project(":dataforge-meta") - - compile "org.jetbrains.kotlinx:kotlinx-io:$kotlinx_io_version" - - compile "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version" - testCompile "org.jetbrains.kotlin:kotlin-test-annotations-common" - testCompile "org.jetbrains.kotlin:kotlin-test-common" -} \ No newline at end of file diff --git a/dataforge-envelope/src/main/kotlin/hep/dataforge/envelopes/Envelope.kt b/dataforge-envelope/src/main/kotlin/hep/dataforge/envelopes/Envelope.kt deleted file mode 100644 index 9368a452..00000000 --- a/dataforge-envelope/src/main/kotlin/hep/dataforge/envelopes/Envelope.kt +++ /dev/null @@ -1,9 +0,0 @@ -package hep.dataforge.envelopes - -import hep.dataforge.meta.Meta -import kotlinx.io.core.IoBuffer - -interface Envelope{ - val meta: Meta - val data: IoBuffer -} \ No newline at end of file diff --git a/dataforge-meta-io/build.gradle b/dataforge-meta-io/build.gradle index 51ffa452..26c0493d 100644 --- a/dataforge-meta-io/build.gradle +++ b/dataforge-meta-io/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'kotlin-multiplatform'// version '1.3.0-rc-57' + id 'kotlin-multiplatform' id 'kotlinx-serialization' } repositories { @@ -18,45 +18,28 @@ kotlin { sourceSets { commonMain { dependencies { - implementation project(":dataforge-meta") - implementation "org.jetbrains.kotlin:kotlin-stdlib-common" + api project(":dataforge-meta") implementation 'org.jetbrains.kotlin:kotlin-reflect' + implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version" implementation "org.jetbrains.kotlinx:kotlinx-io:$kotlinx_io_version" - implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version" - } - } - commonTest { - dependencies { - implementation 'org.jetbrains.kotlin:kotlin-test-common' - implementation 'org.jetbrains.kotlin:kotlin-test-annotations-common' - implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version" } } + commonTest {} jvmMain { dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" - implementation "org.jetbrains.kotlinx:kotlinx-io-jvm:$kotlinx_io_version" + implementation 'com.github.cliftonlabs:json-simple:3.0.2' implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version" + implementation "org.jetbrains.kotlinx:kotlinx-io-jvm:$kotlinx_io_version" } } - jvmTest { - dependencies { - implementation 'org.jetbrains.kotlin:kotlin-test' - implementation 'org.jetbrains.kotlin:kotlin-test-junit' - } - } + jvmTest {} jsMain { dependencies { - implementation 'org.jetbrains.kotlin:kotlin-stdlib-js' - implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version" - } - } - jsTest { - dependencies { - implementation 'org.jetbrains.kotlin:kotlin-test-js' implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version" + implementation "org.jetbrains.kotlinx:kotlinx-io-js:$kotlinx_io_version" } } + jsTest {} // iosMain { // } // iosTest { diff --git a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt index 7eb3b641..0f0a662d 100644 --- a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt +++ b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt @@ -1,15 +1,17 @@ package hep.dataforge.meta.io -import hep.dataforge.meta.Meta +import hep.dataforge.meta.* import kotlinx.io.core.Input import kotlinx.io.core.Output +import kotlinx.io.core.readText +import kotlinx.io.core.writeText /** * A format for meta serialization */ interface MetaFormat { - val name : String - val key : Short + val name: String + val key: Short suspend fun write(meta: Meta, out: Output) suspend fun read(input: Input): Meta @@ -23,4 +25,149 @@ interface MetaFormat { ///** // * Resolve format by its binary key. Null if not provided // */ -//expect fun resolveFormat(key: Short): MetaFormat? \ No newline at end of file +//expect fun resolveFormat(key: Short): MetaFormat? + +internal expect fun writeJson(meta: Meta, out: Output) +internal expect fun readJson(input: Input, length: Int = -1): Meta + +object JSONMetaFormat : MetaFormat { + override val name: String = "json" + override val key: Short = 0x4a53//"JS" + + override suspend fun write(meta: Meta, out: Output) = writeJson(meta, out) + override suspend fun read(input: Input): Meta = readJson(input) +} + +object BinaryMetaFormat : MetaFormat { + override val name: String = "bin" + override val key: Short = 0x4249//BI + + override suspend fun write(meta: Meta, out: Output) { + out.writeMeta(meta) + } + + override suspend fun read(input: Input): Meta { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + private fun Output.writeChar(char: Char) = writeByte(char.toByte()) + + private fun Output.writeString(str: String) { + writeInt(str.length) + writeText(str) + } + + private fun Output.writeValue(value: Value) { + if (value.isList()) { + writeChar('L') + writeInt(value.list.size) + value.list.forEach { + writeValue(it) + } + } else when (value.type) { + ValueType.NUMBER -> when (value.value) { + is Short -> { + writeChar('s') + writeShort(value.number.toShort()) + } + is Int -> { + writeChar('i') + writeInt(value.number.toInt()) + } + is Long -> { + writeChar('l') + writeLong(value.number.toLong()) + } + is Float -> { + writeChar('f') + writeFloat(value.number.toFloat()) + } + else -> { + writeChar('d') + writeDouble(value.number.toDouble()) + } + } + ValueType.STRING -> { + writeChar('S') + writeString(value.string) + } + ValueType.BOOLEAN -> { + if (value.boolean) { + writeChar('+') + } else { + writeChar('-') + } + } + ValueType.NULL -> { + writeChar('N') + } + } + } + + private fun Output.writeMeta(meta: Meta) { + writeChar('M') + writeInt(meta.items.size) + meta.items.forEach { (key, item) -> + writeString(key) + when (item) { + is MetaItem.ValueItem -> { + writeValue(item.value) + } + is MetaItem.SingleNodeItem -> { + writeMeta(item.node) + } + is MetaItem.MultiNodeItem -> { + writeChar('#') + writeInt(item.nodes.size) + item.nodes.forEach { + writeMeta(it) + } + } + } + } + } + + private fun Input.readString(): String { + val length = readInt() + return readText(max = length) + } + + private fun Input.readMetaItem(): MetaItem { + val keyChar = readByte().toChar() + return when (keyChar) { + 'S' -> MetaItem.ValueItem(StringValue(readString())) + 'N' -> MetaItem.ValueItem(Null) + '+' -> MetaItem.ValueItem(True) + '-' -> MetaItem.ValueItem(True) + 's' -> MetaItem.ValueItem(NumberValue(readShort())) + 'i' -> MetaItem.ValueItem(NumberValue(readInt())) + 'l' -> MetaItem.ValueItem(NumberValue(readInt())) + 'f' -> MetaItem.ValueItem(NumberValue(readFloat())) + 'd' -> MetaItem.ValueItem(NumberValue(readDouble())) + 'L' -> { + val length = readInt() + val list = (1..length).map { (readMetaItem() as MetaItem.ValueItem).value } + MetaItem.ValueItem(Value.of(list)) + } + 'M' -> { + val length = readInt() + val meta = buildMeta { + (1..length).forEach { _ -> + val name = readString() + val item = readMetaItem() + set(name,item) + } + } + MetaItem.SingleNodeItem(meta) + } + '#' -> { + val length = readInt() + val nodes = (1..length).map { (readMetaItem() as MetaItem.SingleNodeItem).node } + MetaItem.MultiNodeItem(nodes) + } + else -> error("Unknown serialization key character: $keyChar") + } + } + +} + diff --git a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaProxy.kt b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaProxy.kt deleted file mode 100644 index 3e1e1681..00000000 --- a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaProxy.kt +++ /dev/null @@ -1,51 +0,0 @@ -package hep.dataforge.meta.io - -import hep.dataforge.meta.Meta -import hep.dataforge.meta.MetaItem -import hep.dataforge.meta.ValueType -import hep.dataforge.meta.boolean -import kotlinx.serialization.Polymorphic -import kotlinx.serialization.Serializable - -/*Universal serialization*/ - -@Serializable -class MetaProxy(val map: Map) - - -@Serializable -sealed class MetaItemProxy { - @Serializable - class NumberValueProxy(val number: Number) : MetaItemProxy() - - @Serializable - class StringValueProxy(val string: String) : MetaItemProxy() - - @Serializable - class BooleanValueProxy(val boolean: Boolean) : MetaItemProxy() - - @Serializable - object NullValueProxy : MetaItemProxy() - - @Serializable - class SingleMetaProxy(val node: MetaProxy) : MetaItemProxy() - - @Serializable - class MetaListProxy(val list: List<@Polymorphic MetaProxy>) : MetaItemProxy() -} - - -fun Meta.toMap(): MetaProxy { - return MetaProxy(this.items.mapValues { (_, value) -> - when (value) { - is MetaItem.ValueItem<*> -> when (value.value.type) { - ValueType.NUMBER -> MetaItemProxy.NumberValueProxy(value.value.number) - ValueType.STRING -> MetaItemProxy.StringValueProxy(value.value.string) - ValueType.BOOLEAN -> MetaItemProxy.BooleanValueProxy(value.value.boolean) - ValueType.NULL -> MetaItemProxy.NullValueProxy - } - is MetaItem.SingleNodeItem<*> -> MetaItemProxy.SingleMetaProxy(value.node.toMap()) - is MetaItem.MultiNodeItem<*> -> MetaItemProxy.MetaListProxy(value.nodes.map { it.toMap() }) - } - }) -} diff --git a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Serializers.kt b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Serializers.kt index d52b7be1..d1811a72 100644 --- a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Serializers.kt +++ b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Serializers.kt @@ -6,59 +6,59 @@ import kotlinx.serialization.json.* /*Direct JSON serialization*/ -fun Value.toJson(): JsonElement = if (isList()) { - JsonArray(list.map { it.toJson() }) -} else { - when (type) { - ValueType.NUMBER -> JsonPrimitive(number) - ValueType.STRING -> JsonPrimitive(string) - ValueType.BOOLEAN -> JsonPrimitive(boolean) - ValueType.NULL -> JsonNull - } -} - -fun Meta.toJSON(): JsonObject { - val map = this.items.mapValues { (_, value) -> - when (value) { - is MetaItem.ValueItem -> value.value.toJson() - is MetaItem.SingleNodeItem -> value.node.toJSON() - is MetaItem.MultiNodeItem -> JsonArray(value.nodes.map { it.toJSON() }) - } - } - return JsonObject(map) -} - -fun JsonPrimitive.toValue(): Value { - return when (this) { - is JsonLiteral -> LazyParsedValue(content) - is JsonNull -> Null - } -} - -fun JsonObject.toMeta(): Meta { - return buildMeta { - this@toMeta.forEach { (key, value) -> - when (value) { - is JsonPrimitive -> set(key, value.toValue()) - is JsonObject -> set(key, value.toMeta()) - is JsonArray -> if (value.all { it is JsonPrimitive }) { - set(key, ListValue(value.map { (it as JsonPrimitive).toValue() })) - } else { - set( - key, - value.map { - if (it is JsonObject) { - it.toMeta() - } else { - buildMeta { "@value" to it.primitive.toValue() } - } - } - ) - } - } - } - } -} +//fun Value.toJson(): JsonElement = if (isList()) { +// JsonArray(list.map { it.toJson() }) +//} else { +// when (type) { +// ValueType.NUMBER -> JsonPrimitive(number) +// ValueType.STRING -> JsonPrimitive(string) +// ValueType.BOOLEAN -> JsonPrimitive(boolean) +// ValueType.NULL -> JsonNull +// } +//} +// +//fun Meta.toJSON(): JsonObject { +// val map = this.items.mapValues { (_, value) -> +// when (value) { +// is MetaItem.ValueItem -> value.value.toJson() +// is MetaItem.SingleNodeItem -> value.node.toJSON() +// is MetaItem.MultiNodeItem -> JsonArray(value.nodes.map { it.toJSON() }) +// } +// } +// return JsonObject(map) +//} +// +//fun JsonPrimitive.toValue(): Value { +// return when (this) { +// is JsonLiteral -> LazyParsedValue(content) +// is JsonNull -> Null +// } +//} +// +//fun JsonObject.toMeta(): Meta { +// return buildMeta { +// this@toMeta.forEach { (key, value) -> +// when (value) { +// is JsonPrimitive -> set(key, value.toValue()) +// is JsonObject -> set(key, value.toMeta()) +// is JsonArray -> if (value.all { it is JsonPrimitive }) { +// set(key, ListValue(value.map { (it as JsonPrimitive).toValue() })) +// } else { +// set( +// key, +// value.map { +// if (it is JsonObject) { +// it.toMeta() +// } else { +// buildMeta { "@value" to it.primitive.toValue() } +// } +// } +// ) +// } +// } +// } +// } +//} /*Direct CBOR serialization*/ @@ -67,45 +67,6 @@ fun JsonObject.toMeta(): Meta { // encodeNumber(char.toByte().toLong()) // } // -// fun CBOR.CBOREncoder.encodeValue(value: Value) { -// if (value.isList()) { -// encodeChar('L') -// startArray() -// value.list.forEach { -// encodeValue(it) -// } -// end() -// } else when (value.type) { -// ValueType.NUMBER -> when (value.value) { -// is Int, is Short, is Long -> { -// encodeChar('i') -// encodeNumber(value.number.toLong()) -// } -// is Float -> { -// encodeChar('f') -// encodeFloat(value.number.toFloat()) -// } -// else -> { -// encodeChar('d') -// encodeDouble(value.number.toDouble()) -// } -// } -// ValueType.STRING -> { -// encodeChar('S') -// encodeString(value.string) -// } -// ValueType.BOOLEAN -> { -// if (value.boolean) { -// encodeChar('+') -// } else { -// encodeChar('-') -// } -// } -// ValueType.NULL -> { -// encodeChar('N') -// } -// } -// } // // fun CBOR.CBOREncoder.encodeMeta(meta: Meta) { // meta.items.forEach { (key, item) -> diff --git a/dataforge-meta-io/src/commonTest/kotlin/io/MetaItemProxyTest.kt b/dataforge-meta-io/src/commonTest/kotlin/io/MetaItemProxyTest.kt deleted file mode 100644 index cd12d611..00000000 --- a/dataforge-meta-io/src/commonTest/kotlin/io/MetaItemProxyTest.kt +++ /dev/null @@ -1,47 +0,0 @@ -package io - -import hep.dataforge.meta.buildMeta -import hep.dataforge.meta.io.MetaItemProxy -import hep.dataforge.meta.io.toMap -import kotlinx.serialization.json.JSON -import kotlinx.serialization.serializer -import kotlin.test.Test - -class MetaItemProxyTest { - @Test - fun testGeneration() { - MetaItemProxy::class.serializer() - } - - - @Test - fun testProxySerialization() { - val meta = buildMeta { - "a" to 2 - "b" to { - "c" to "ddd" - "d" to 2.2 - } - } - val json = JSON.indented.stringify(meta.toMap()) - println(json) -// val result: Map = JSON.parse(json) -// assertEquals(meta,result.to) - } - -// @Test -// fun testJSONSerialization() { -// val meta = buildMeta { -// "a" to 2 -// "b" to { -// "c" to "ddd" -// "d" to 2.2 -// } -// } -// val json = meta.toJSON() -// println(json) -// val result = json.toMeta() -// assertEquals(meta, result) -// } - -} \ No newline at end of file diff --git a/dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSMeta.kt b/dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSMeta.kt new file mode 100644 index 00000000..75d0136f --- /dev/null +++ b/dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSMeta.kt @@ -0,0 +1,36 @@ +package hep.dataforge.meta.io + +import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaItem +import hep.dataforge.meta.Value + +/** + * Represent any js object as meta + */ +class JSMeta(val obj: Any) : Meta { + override val items: Map> + get() = listKeys(obj).associateWith { convert(js("obj[it]")) } + + private fun listKeys(obj: Any): List = js("Object").keys(obj) as List + + private fun isList(obj: Any): Boolean = js("Array").isArray(obj) as Boolean + + private fun isPrimitive(obj: Any?): Boolean = js("obj !== Object(obj)") as Boolean + + private fun convert(obj: Any?): MetaItem { + return when (obj) { + null, isPrimitive(obj), is Number, is String, is Boolean -> MetaItem.ValueItem(Value.of(obj)) + isList(obj) -> { + val list = obj as List<*> + //if first value is primitive, treat as value + if (isPrimitive(list.first())) { + MetaItem.ValueItem(Value.of(list)) + } else { + //else treat as meta list + MetaItem.MultiNodeItem(list.map { JSMeta(it!!) }) + } + } + else -> MetaItem.SingleNodeItem(JSMeta(obj)) + } + } +} \ No newline at end of file diff --git a/dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt b/dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt new file mode 100644 index 00000000..60b1446e --- /dev/null +++ b/dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt @@ -0,0 +1,17 @@ +package hep.dataforge.meta.io + +import hep.dataforge.meta.Meta +import kotlinx.io.core.Input +import kotlinx.io.core.Output +import kotlinx.io.core.readText +import kotlinx.io.core.writeText +import kotlin.js.Json + +internal actual fun writeJson(meta: Meta, out: Output) { + out.writeText(JSON.stringify(meta)) +} + +internal actual fun readJson(input: Input, length: Int): Meta { + val json: Json = JSON.parse(input.readText(max = if (length > 0) length else Int.MAX_VALUE)) + return JSMeta(json) +} \ No newline at end of file diff --git a/dataforge-meta-io/src/jsTest/kotlin/hep/dataforge/meta/JSMetaTest.kt b/dataforge-meta-io/src/jsTest/kotlin/hep/dataforge/meta/JSMetaTest.kt new file mode 100644 index 00000000..e925b92c --- /dev/null +++ b/dataforge-meta-io/src/jsTest/kotlin/hep/dataforge/meta/JSMetaTest.kt @@ -0,0 +1,18 @@ +package hep.dataforge.meta + +import hep.dataforge.meta.io.JSMeta +import kotlin.js.json +import kotlin.test.Test +import kotlin.test.assertEquals + +class JSMetaTest{ + @Test + fun testConverstion(){ + val test = json( + "a" to 2, + "b" to "ddd" + ) + val meta = JSMeta(test) + assertEquals(2, meta["a"]!!.int) + } +} \ No newline at end of file diff --git a/dataforge-meta-io/src/jvmMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt b/dataforge-meta-io/src/jvmMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt new file mode 100644 index 00000000..f3bff93c --- /dev/null +++ b/dataforge-meta-io/src/jvmMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt @@ -0,0 +1,118 @@ +package hep.dataforge.meta.io + +import com.github.cliftonlabs.json_simple.JsonArray +import com.github.cliftonlabs.json_simple.JsonObject +import com.github.cliftonlabs.json_simple.Jsoner +import hep.dataforge.meta.* +import kotlinx.io.core.* +import java.io.ByteArrayInputStream +import java.io.InputStreamReader +import java.io.Reader +import java.text.ParseException + +internal actual fun writeJson(meta: Meta, out: Output) { + val json = meta.toJson() + val string = Jsoner.prettyPrint(Jsoner.serialize(json)) + out.writeText(string) +} + +private fun Value.toJson(): Any { + return if (list.size == 1) { + when (type) { + ValueType.NUMBER -> number + ValueType.BOOLEAN -> boolean + else -> string + } + } else { + JsonArray().apply { + list.forEach { add(it.toJson()) } + } + } +} + +private fun Meta.toJson(): JsonObject { + val builder = JsonObject() + items.forEach { name, item -> + when (item) { + is MetaItem.ValueItem -> builder[name] = item.value.toJson() + is MetaItem.SingleNodeItem -> builder[name] = item.node.toJson() + is MetaItem.MultiNodeItem -> { + val array = JsonArray() + item.nodes.forEach { array.add(it.toJson()) } + builder[name] = array + } + } + } + return builder +} + + +internal actual fun readJson(input: Input, length: Int): Meta { + return if (length == 0) { + EmptyMeta + } else { + val json = if (length > 0) { + //Read into intermediate buffer + val buffer = ByteArray(length) + input.readAvailable(buffer, length) + Jsoner.deserialize(InputStreamReader(ByteArrayInputStream(buffer), Charsets.UTF_8)) as JsonObject + } else { + //automatic + val reader = object : Reader() { + override fun close() { + input.close() + } + + override fun read(cbuf: CharArray, off: Int, len: Int): Int { + val block = input.readText(Charsets.UTF_8, len).toCharArray() + System.arraycopy(block, 0, cbuf, off, block.size) + return block.size + } + + } + Jsoner.deserialize(reader) as JsonObject + } + json.toMeta() + } +} + +@Throws(ParseException::class) +private fun JsonObject.toMeta(): Meta { + return buildMeta { + this@toMeta.forEach { key, value -> appendValue(key as String, value) } + } +} + +private fun JsonArray.toListValue(): Value { + val list: List = this.map { value -> + when (value) { + null -> Null + is JsonArray -> value.toListValue() + is Number -> NumberValue(value) + is Boolean -> if (value) True else False + is String -> LazyParsedValue(value) + is JsonObject -> error("Object values inside multidimensional arrays are not allowed") + else -> error("Unknown token $value in json") + } + } + return Value.of(list) +} + +private fun MetaBuilder.appendValue(key: String, value: Any?) { + when (value) { + is JsonObject -> this[key] = value.toMeta() + is JsonArray -> { + value.forEach { + if (it is JsonArray) { + this[key] = it.toListValue() + } else { + appendValue(key, it) + } + } + } + is Number -> this[key] = NumberValue(value) + is Boolean -> this[key] = value + is String -> this[key] = LazyParsedValue(value) + //ignore anything else + } +} diff --git a/dataforge-meta/build.gradle b/dataforge-meta/build.gradle index 34599174..bba6b93b 100644 --- a/dataforge-meta/build.gradle +++ b/dataforge-meta/build.gradle @@ -1,6 +1,5 @@ plugins { - id 'kotlin-multiplatform'// version '1.3.0-rc-57' - id 'kotlinx-serialization' + id 'kotlin-multiplatform' } repositories { maven { url = 'http://dl.bintray.com/kotlin/kotlin-eap' } @@ -18,41 +17,35 @@ kotlin { sourceSets { commonMain { dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-common" - implementation 'org.jetbrains.kotlin:kotlin-reflect' - implementation "org.jetbrains.kotlinx:kotlinx-io:$kotlinx_io_version" - implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version" + api "org.jetbrains.kotlin:kotlin-stdlib-common" + } } commonTest { dependencies { - implementation 'org.jetbrains.kotlin:kotlin-test-common' - implementation 'org.jetbrains.kotlin:kotlin-test-annotations-common' - implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version" + api 'org.jetbrains.kotlin:kotlin-test-common' + api 'org.jetbrains.kotlin:kotlin-test-annotations-common' } } jvmMain { dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" - implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version" + api "org.jetbrains.kotlin:kotlin-stdlib-jdk8" } } jvmTest { dependencies { - implementation 'org.jetbrains.kotlin:kotlin-test' - implementation 'org.jetbrains.kotlin:kotlin-test-junit' + api 'org.jetbrains.kotlin:kotlin-test' + api 'org.jetbrains.kotlin:kotlin-test-junit' } } jsMain { dependencies { - implementation 'org.jetbrains.kotlin:kotlin-stdlib-js' - implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version" + api 'org.jetbrains.kotlin:kotlin-stdlib-js' } } jsTest { dependencies { - implementation 'org.jetbrains.kotlin:kotlin-test-js' - implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version" + api 'org.jetbrains.kotlin:kotlin-test-js' } } // iosMain { diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt index 2d5eb1f1..88495f4e 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -10,34 +10,9 @@ import hep.dataforge.names.toName * * a list of nodes */ sealed class MetaItem { - class ValueItem(val value: Value) : MetaItem(){ - override fun equals(other: Any?): Boolean { - return this.value == (other as? ValueItem<*>)?.value - } - - override fun hashCode(): Int { - return value.hashCode() - } - } - class SingleNodeItem(val node: M) : MetaItem(){ - override fun equals(other: Any?): Boolean { - return this.node == (other as? SingleNodeItem<*>)?.node - } - - override fun hashCode(): Int { - return node.hashCode() - } - } - - class MultiNodeItem(val nodes: List) : MetaItem(){ - override fun equals(other: Any?): Boolean { - return this.nodes == (other as? MultiNodeItem<*>)?.nodes - } - - override fun hashCode(): Int { - return nodes.hashCode() - } - } + data class ValueItem(val value: Value) : MetaItem() + data class SingleNodeItem(val node: M) : MetaItem() + data class MultiNodeItem(val nodes: List) : MetaItem() } operator fun List.get(query: String): M? { diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt index c7f6724d..647e560d 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt @@ -45,4 +45,4 @@ fun Meta.builder(): MetaBuilder { } } -fun buildMeta(builder: MetaBuilder.() -> Unit): Meta = MetaBuilder().apply(builder) \ No newline at end of file +fun buildMeta(builder: MetaBuilder.() -> Unit): MetaBuilder = MetaBuilder().apply(builder) \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 665278b9..f7aab5b8 100644 --- a/settings.gradle +++ b/settings.gradle @@ -28,5 +28,4 @@ rootProject.name = 'dataforge-core' include ":dataforge-meta" include ":dataforge-meta-io" - -include ":dataforge-envelope" \ No newline at end of file +//include ":dataforge-envelope" \ No newline at end of file From a7a1f776a76c3c546e896e12db437c65b6f40546 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 26 Oct 2018 16:15:27 +0300 Subject: [PATCH 06/37] Test for binary serialization --- dataforge-meta-io/build.gradle | 30 ++-- .../hep/dataforge/meta/io/MetaFormat.kt | 31 +++-- .../hep/dataforge/meta/io/Serializers.kt | 128 ------------------ .../hep/dataforge/meta/io/MetaFormatTest.kt | 22 +++ dataforge-meta/build.gradle | 10 +- 5 files changed, 68 insertions(+), 153 deletions(-) delete mode 100644 dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Serializers.kt create mode 100644 dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt diff --git a/dataforge-meta-io/build.gradle b/dataforge-meta-io/build.gradle index 26c0493d..00ecb138 100644 --- a/dataforge-meta-io/build.gradle +++ b/dataforge-meta-io/build.gradle @@ -1,6 +1,6 @@ plugins { id 'kotlin-multiplatform' - id 'kotlinx-serialization' + //id 'kotlinx-serialization' } repositories { maven { url = 'http://dl.bintray.com/kotlin/kotlin-eap' } @@ -19,27 +19,41 @@ kotlin { commonMain { dependencies { api project(":dataforge-meta") - implementation 'org.jetbrains.kotlin:kotlin-reflect' - implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version" + //implementation 'org.jetbrains.kotlin:kotlin-reflect' + //implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version" implementation "org.jetbrains.kotlinx:kotlinx-io:$kotlinx_io_version" } } - commonTest {} + commonTest { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-test-common' + implementation 'org.jetbrains.kotlin:kotlin-test-annotations-common' + } + } jvmMain { dependencies { implementation 'com.github.cliftonlabs:json-simple:3.0.2' - implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version" + //implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version" implementation "org.jetbrains.kotlinx:kotlinx-io-jvm:$kotlinx_io_version" } } - jvmTest {} + jvmTest { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-test' + implementation 'org.jetbrains.kotlin:kotlin-test-junit' + } + } jsMain { dependencies { - implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version" + //implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version" implementation "org.jetbrains.kotlinx:kotlinx-io-js:$kotlinx_io_version" } } - jsTest {} + jsTest { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-test-js' + } + } // iosMain { // } // iosTest { diff --git a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt index 0f0a662d..7fd73460 100644 --- a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt +++ b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt @@ -1,10 +1,7 @@ package hep.dataforge.meta.io import hep.dataforge.meta.* -import kotlinx.io.core.Input -import kotlinx.io.core.Output -import kotlinx.io.core.readText -import kotlinx.io.core.writeText +import kotlinx.io.core.* /** * A format for meta serialization @@ -13,8 +10,18 @@ interface MetaFormat { val name: String val key: Short - suspend fun write(meta: Meta, out: Output) - suspend fun read(input: Input): Meta + fun write(meta: Meta, out: Output) + fun read(input: Input): Meta +} + +fun MetaFormat.stringify(meta: Meta): String { + val builder = BytePacketBuilder() + write(meta,builder) + return builder.build().readText() +} + +fun MetaFormat.parse(str: String): Meta{ + return read(ByteReadPacket(str.toByteArray())) } ///** @@ -34,20 +41,20 @@ object JSONMetaFormat : MetaFormat { override val name: String = "json" override val key: Short = 0x4a53//"JS" - override suspend fun write(meta: Meta, out: Output) = writeJson(meta, out) - override suspend fun read(input: Input): Meta = readJson(input) + override fun write(meta: Meta, out: Output) = writeJson(meta, out) + override fun read(input: Input): Meta = readJson(input) } object BinaryMetaFormat : MetaFormat { override val name: String = "bin" override val key: Short = 0x4249//BI - override suspend fun write(meta: Meta, out: Output) { + override fun write(meta: Meta, out: Output) { out.writeMeta(meta) } - override suspend fun read(input: Input): Meta { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + override fun read(input: Input): Meta { + return (input.readMetaItem() as MetaItem.SingleNodeItem).node } private fun Output.writeChar(char: Char) = writeByte(char.toByte()) @@ -155,7 +162,7 @@ object BinaryMetaFormat : MetaFormat { (1..length).forEach { _ -> val name = readString() val item = readMetaItem() - set(name,item) + set(name, item) } } MetaItem.SingleNodeItem(meta) diff --git a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Serializers.kt b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Serializers.kt deleted file mode 100644 index d1811a72..00000000 --- a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Serializers.kt +++ /dev/null @@ -1,128 +0,0 @@ -package hep.dataforge.meta.io - -import hep.dataforge.meta.* -import kotlinx.serialization.json.* - - -/*Direct JSON serialization*/ - -//fun Value.toJson(): JsonElement = if (isList()) { -// JsonArray(list.map { it.toJson() }) -//} else { -// when (type) { -// ValueType.NUMBER -> JsonPrimitive(number) -// ValueType.STRING -> JsonPrimitive(string) -// ValueType.BOOLEAN -> JsonPrimitive(boolean) -// ValueType.NULL -> JsonNull -// } -//} -// -//fun Meta.toJSON(): JsonObject { -// val map = this.items.mapValues { (_, value) -> -// when (value) { -// is MetaItem.ValueItem -> value.value.toJson() -// is MetaItem.SingleNodeItem -> value.node.toJSON() -// is MetaItem.MultiNodeItem -> JsonArray(value.nodes.map { it.toJSON() }) -// } -// } -// return JsonObject(map) -//} -// -//fun JsonPrimitive.toValue(): Value { -// return when (this) { -// is JsonLiteral -> LazyParsedValue(content) -// is JsonNull -> Null -// } -//} -// -//fun JsonObject.toMeta(): Meta { -// return buildMeta { -// this@toMeta.forEach { (key, value) -> -// when (value) { -// is JsonPrimitive -> set(key, value.toValue()) -// is JsonObject -> set(key, value.toMeta()) -// is JsonArray -> if (value.all { it is JsonPrimitive }) { -// set(key, ListValue(value.map { (it as JsonPrimitive).toValue() })) -// } else { -// set( -// key, -// value.map { -// if (it is JsonObject) { -// it.toMeta() -// } else { -// buildMeta { "@value" to it.primitive.toValue() } -// } -// } -// ) -// } -// } -// } -// } -//} - -/*Direct CBOR serialization*/ - -//fun Meta.toBinary(out: OutputStream) { -// fun CBOR.CBOREncoder.encodeChar(char: Char) { -// encodeNumber(char.toByte().toLong()) -// } -// -// -// fun CBOR.CBOREncoder.encodeMeta(meta: Meta) { -// meta.items.forEach { (key, item) -> -// this.startMap() -// encodeString(key) -// when (item) { -// is MetaItem.ValueItem -> { -// encodeChar('V') -// encodeValue(item.value) -// } -// is MetaItem.SingleNodeItem -> { -// startArray() -// encodeMeta(item.node) -// } -// is MetaItem.MultiNodeItem -> { -// startArray() -// item.nodes.forEach { -// encodeMeta(it) -// } -// end() -// } -// } -// } -// } -// -// -// CBOR.CBOREncoder(out).apply { -// encodeMeta(this@toBinary) -// } -//} -// -//fun InputStream.readBinaryMeta(): Meta { -// fun CBOR.CBORDecoder.nextChar(): Char = nextNumber().toByte().toChar() -// -// fun CBOR.CBORDecoder.nextValue(): Value { -// val key = nextChar() -// return when(key){ -// 'L' -> { -// val size = startArray() -// val res = (0 until size).map { nextValue() } -// end() -// ListValue(res) -// } -// 'S' -> StringValue(nextString()) -// 'N' -> Null -// '+' -> True -// '-' -> False -// 'i' -> NumberValue(nextNumber()) -// 'f' -> NumberValue(nextFloat()) -// 'd' -> NumberValue(nextDouble()) -// else -> error("Unknown binary key: $key") -// } -// } -// -// fun CBOR.CBORDecoder.nextMeta(): Meta{ -// -// } -// -//} \ No newline at end of file diff --git a/dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt b/dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt new file mode 100644 index 00000000..1a4e9060 --- /dev/null +++ b/dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt @@ -0,0 +1,22 @@ +package hep.dataforge.meta.io + +import hep.dataforge.meta.buildMeta +import kotlin.test.Test +import kotlin.test.assertEquals + +class MetaFormatTest{ + @Test + fun testBinaryMetaFormat(){ + val meta = buildMeta { + "a" to 22 + "node" to { + "b" to "DDD" + "c" to 11.1 + } + } + val string = BinaryMetaFormat.stringify(meta) + val result = BinaryMetaFormat.parse(string) + assertEquals(meta,result) + } + +} \ No newline at end of file diff --git a/dataforge-meta/build.gradle b/dataforge-meta/build.gradle index bba6b93b..f37628a2 100644 --- a/dataforge-meta/build.gradle +++ b/dataforge-meta/build.gradle @@ -23,8 +23,8 @@ kotlin { } commonTest { dependencies { - api 'org.jetbrains.kotlin:kotlin-test-common' - api 'org.jetbrains.kotlin:kotlin-test-annotations-common' + implementation 'org.jetbrains.kotlin:kotlin-test-common' + implementation 'org.jetbrains.kotlin:kotlin-test-annotations-common' } } jvmMain { @@ -34,8 +34,8 @@ kotlin { } jvmTest { dependencies { - api 'org.jetbrains.kotlin:kotlin-test' - api 'org.jetbrains.kotlin:kotlin-test-junit' + implementation 'org.jetbrains.kotlin:kotlin-test' + implementation 'org.jetbrains.kotlin:kotlin-test-junit' } } jsMain { @@ -45,7 +45,7 @@ kotlin { } jsTest { dependencies { - api 'org.jetbrains.kotlin:kotlin-test-js' + implementation 'org.jetbrains.kotlin:kotlin-test-js' } } // iosMain { From 3033cc630462d253b627071c1001cc62878abca7 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 2 Nov 2018 15:11:42 +0300 Subject: [PATCH 07/37] Moved to K1.3. Removed MultiNodeItems and replaced same name siblings by queries. Laminate merge. --- build.gradle | 10 +- dataforge-meta-io/build.gradle | 5 +- .../hep/dataforge/meta/io/MetaFormat.kt | 20 +-- .../kotlin/hep/dataforge/meta/io/JSMeta.kt | 15 +-- .../hep/dataforge/meta/io/JSONMetaFormat.kt | 9 +- dataforge-meta/build.gradle | 5 +- .../kotlin/hep/dataforge/meta/Config.kt | 3 +- .../kotlin/hep/dataforge/meta/Laminate.kt | 72 ++++++++++ .../kotlin/hep/dataforge/meta/Meta.kt | 126 ++++++++---------- .../kotlin/hep/dataforge/meta/MetaBuilder.kt | 5 +- .../hep/dataforge/meta/MutableMetaNode.kt | 68 +++++----- .../hep/dataforge/meta/Specification.kt | 2 +- .../kotlin/hep/dataforge/meta/Styleable.kt | 14 +- .../kotlin/hep/dataforge/names/Name.kt | 4 +- settings.gradle | 1 - 15 files changed, 193 insertions(+), 166 deletions(-) create mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt diff --git a/build.gradle b/build.gradle index 5dd5baab..76235d81 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,10 @@ buildscript { - ext.kotlin_version = '1.3.0-rc-190' - ext.serialization_version = '0.8.3-rc13' - ext.kotlinx_io_version = '0.1.0-alpha-24-rc13' + ext.kotlin_version = '1.3.0' + ext.serialization_version = '0.9.0' + ext.kotlinx_io_version = '0.1.0-beta-1' + repositories { jcenter() - maven { - url = "http://dl.bintray.com/kotlin/kotlin-eap" - } } dependencies { diff --git a/dataforge-meta-io/build.gradle b/dataforge-meta-io/build.gradle index 00ecb138..242532bb 100644 --- a/dataforge-meta-io/build.gradle +++ b/dataforge-meta-io/build.gradle @@ -2,10 +2,11 @@ plugins { id 'kotlin-multiplatform' //id 'kotlinx-serialization' } + repositories { - maven { url = 'http://dl.bintray.com/kotlin/kotlin-eap' } - mavenCentral() + jcenter() } + kotlin { targets { fromPreset(presets.jvm, 'jvm') diff --git a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt index 7fd73460..552f9532 100644 --- a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt +++ b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt @@ -54,7 +54,7 @@ object BinaryMetaFormat : MetaFormat { } override fun read(input: Input): Meta { - return (input.readMetaItem() as MetaItem.SingleNodeItem).node + return (input.readMetaItem() as MetaItem.NodeItem).node } private fun Output.writeChar(char: Char) = writeByte(char.toByte()) @@ -115,21 +115,14 @@ object BinaryMetaFormat : MetaFormat { writeChar('M') writeInt(meta.items.size) meta.items.forEach { (key, item) -> - writeString(key) + writeString(key.toString()) when (item) { is MetaItem.ValueItem -> { writeValue(item.value) } - is MetaItem.SingleNodeItem -> { + is MetaItem.NodeItem -> { writeMeta(item.node) } - is MetaItem.MultiNodeItem -> { - writeChar('#') - writeInt(item.nodes.size) - item.nodes.forEach { - writeMeta(it) - } - } } } } @@ -165,12 +158,7 @@ object BinaryMetaFormat : MetaFormat { set(name, item) } } - MetaItem.SingleNodeItem(meta) - } - '#' -> { - val length = readInt() - val nodes = (1..length).map { (readMetaItem() as MetaItem.SingleNodeItem).node } - MetaItem.MultiNodeItem(nodes) + MetaItem.NodeItem(meta) } else -> error("Unknown serialization key character: $keyChar") } diff --git a/dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSMeta.kt b/dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSMeta.kt index 75d0136f..3efa97ee 100644 --- a/dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSMeta.kt +++ b/dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSMeta.kt @@ -3,13 +3,14 @@ package hep.dataforge.meta.io import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaItem import hep.dataforge.meta.Value +import hep.dataforge.names.NameToken /** * Represent any js object as meta */ class JSMeta(val obj: Any) : Meta { - override val items: Map> - get() = listKeys(obj).associateWith { convert(js("obj[it]")) } + override val items: Map> + get() = listKeys(obj).map { NameToken(it) }.associateWith { convert(js("obj[it]")) } private fun listKeys(obj: Any): List = js("Object").keys(obj) as List @@ -22,15 +23,9 @@ class JSMeta(val obj: Any) : Meta { null, isPrimitive(obj), is Number, is String, is Boolean -> MetaItem.ValueItem(Value.of(obj)) isList(obj) -> { val list = obj as List<*> - //if first value is primitive, treat as value - if (isPrimitive(list.first())) { - MetaItem.ValueItem(Value.of(list)) - } else { - //else treat as meta list - MetaItem.MultiNodeItem(list.map { JSMeta(it!!) }) - } + MetaItem.ValueItem(Value.of(list)) } - else -> MetaItem.SingleNodeItem(JSMeta(obj)) + else -> MetaItem.NodeItem(JSMeta(obj)) } } } \ No newline at end of file diff --git a/dataforge-meta-io/src/jvmMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt b/dataforge-meta-io/src/jvmMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt index f3bff93c..e4ac27b9 100644 --- a/dataforge-meta-io/src/jvmMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt +++ b/dataforge-meta-io/src/jvmMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt @@ -34,13 +34,8 @@ private fun Meta.toJson(): JsonObject { val builder = JsonObject() items.forEach { name, item -> when (item) { - is MetaItem.ValueItem -> builder[name] = item.value.toJson() - is MetaItem.SingleNodeItem -> builder[name] = item.node.toJson() - is MetaItem.MultiNodeItem -> { - val array = JsonArray() - item.nodes.forEach { array.add(it.toJson()) } - builder[name] = array - } + is MetaItem.ValueItem -> builder[name.toString()] = item.value.toJson() + is MetaItem.NodeItem -> builder[name.toString()] = item.node.toJson() } } return builder diff --git a/dataforge-meta/build.gradle b/dataforge-meta/build.gradle index f37628a2..8d57006e 100644 --- a/dataforge-meta/build.gradle +++ b/dataforge-meta/build.gradle @@ -1,10 +1,11 @@ plugins { id 'kotlin-multiplatform' } + repositories { - maven { url = 'http://dl.bintray.com/kotlin/kotlin-eap' } - mavenCentral() + jcenter() } + kotlin { targets { fromPreset(presets.jvm, 'jvm') diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt index 1f53ba89..1caef790 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt @@ -22,8 +22,7 @@ fun Meta.toConfig(): Config = this as? Config ?: Config().also { builder -> val item = entry.value builder[entry.key] = when (item) { is MetaItem.ValueItem -> MetaItem.ValueItem(item.value) - is MetaItem.SingleNodeItem -> MetaItem.SingleNodeItem(item.node.toConfig()) - is MetaItem.MultiNodeItem -> MetaItem.MultiNodeItem(item.nodes.map { it.toConfig() }) + is MetaItem.NodeItem -> MetaItem.NodeItem(item.node.toConfig()) } } } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt new file mode 100644 index 00000000..295f825b --- /dev/null +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt @@ -0,0 +1,72 @@ +package hep.dataforge.meta + +import hep.dataforge.names.NameToken + +/** + * A meta laminate consisting of multiple immutable meta layers. For mutable front layer, use [StyledConfig]. + * + * + */ +class Laminate(val layers: List) : Meta { + + override val items: Map> + get() = layers.map { it.items.keys }.flatten().associateWith { key -> + layers.asSequence().map { it.items[key] }.filterNotNull().let(replaceRule) + } + + /** + * Generate sealed meta using [mergeRule] + */ + fun merge(): SealedMeta { + val items = layers.map { it.items.keys }.flatten().associateWith { key -> + layers.asSequence().map { it.items[key] }.filterNotNull().merge() + } + return SealedMeta(items) + } + + companion object { + + /** + * The default rule which always uses the first found item in sequence alongside with its children. + * + * TODO add picture + */ + val replaceRule: (Sequence>) -> MetaItem = { it.first().seal() } + + private fun Sequence>.merge(): MetaItem { + return when { + all { it is MetaItem.ValueItem } -> //If all items are values, take first + first().seal() + all { it is MetaItem.NodeItem } -> { + //list nodes in item + val nodes = map { it.node } + //represent as key->value entries + val entries = nodes.flatMap { it.items.entries.asSequence() } + //group by keys + val groups = entries.groupBy { it.key } + // recursively apply the rule + val items = groups.mapValues { entry -> + entry.value.asSequence().map { it.value }.merge() + } + MetaItem.NodeItem(SealedMeta(items)) + + } + else -> map { + when (it) { + is MetaItem.ValueItem -> MetaItem.NodeItem(buildMeta { Meta.VALUE_KEY to it.value }) + is MetaItem.NodeItem -> it + } + }.merge() + } + } + + + /** + * The values a replaced but meta children are joined + * TODO add picture + */ + val mergeRule: (Sequence>) -> MetaItem = { it.merge() } + } +} + +//TODO add custom rules for Laminate merge diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt index 88495f4e..f27db651 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -1,47 +1,57 @@ package hep.dataforge.meta +import hep.dataforge.meta.Meta.Companion.VALUE_KEY +import hep.dataforge.meta.MetaItem.NodeItem +import hep.dataforge.meta.MetaItem.ValueItem import hep.dataforge.names.Name +import hep.dataforge.names.NameToken import hep.dataforge.names.toName /** * A member of the meta tree. Could be represented as one of following: - * * a value - * * a single node - * * a list of nodes + * * a [ValueItem] (leaf) + * * a [NodeItem] (node) */ sealed class MetaItem { data class ValueItem(val value: Value) : MetaItem() - data class SingleNodeItem(val node: M) : MetaItem() - data class MultiNodeItem(val nodes: List) : MetaItem() -} - -operator fun List.get(query: String): M? { - return if (query.isEmpty()) { - first() - } else { - //TODO add custom key queries - get(query.toInt()) - } + data class NodeItem(val node: M) : MetaItem() } /** * Generic meta tree representation. Elements are [MetaItem] objects that could be represented by three different entities: * * [MetaItem.ValueItem] (leaf) - * * [MetaItem.SingleNodeItem] single node - * * [MetaItem.MultiNodeItem] multi-value node + * * [MetaItem.NodeItem] single node + * + * * Same name siblings are supported via elements with the same [Name] but different queries */ interface Meta { - val items: Map> -} + val items: Map> -operator fun Meta.get(name: Name): MetaItem? { - return when (name.length) { - 0 -> error("Can't resolve element from empty name") - 1 -> items[name.first()!!.body] - else -> name.first()!!.let { token -> items[token.body]?.nodes?.get(token.query) }?.get(name.cutFirst()) + companion object { + /** + * A key for single value node + */ + const val VALUE_KEY = "@value" } } +/** + * Fast [String]-based accessor for item map + */ +operator fun Map.get(body: String, query: String = ""): T? = get(NameToken(body, query)) + +operator fun Meta.get(name: Name): MetaItem? { + return name.first()?.let { token -> + val tail = name.cutFirst() + when (tail.length) { + 0 -> items[token] + else -> items[token]?.node?.get(tail) + } + } +} + +operator fun Meta.get(token: NameToken): MetaItem? = items[token] + //TODO create Java helper for meta operations operator fun Meta.get(key: String): MetaItem? = get(key.toName()) @@ -49,13 +59,15 @@ operator fun Meta.get(key: String): MetaItem? = get(key.toName()) * A meta node that ensures that all of its descendants has at least the same type */ abstract class MetaNode> : Meta { - abstract override val items: Map> + abstract override val items: Map> operator fun get(name: Name): MetaItem? { - return when (name.length) { - 0 -> error("Can't resolve element from empty name") - 1 -> items[name.first()!!.body] - else -> name.first()!!.let { token -> items[token.body]?.nodes?.get(token.query) }?.get(name.cutFirst()) + return name.first()?.let { token -> + val tail = name.cutFirst() + when (tail.length) { + 0 -> items[token] + else -> items[token]?.node?.get(tail) + } } } @@ -78,34 +90,19 @@ abstract class MetaNode> : Meta { * * If the argument is possibly mutable node, it is copied on creation */ -class SealedMeta internal constructor(override val items: Map>) : MetaNode() { - - companion object { - fun seal(meta: Meta): SealedMeta { - val items = if (meta is SealedMeta) { - meta.items - } else { - meta.items.mapValues { entry -> - val item = entry.value - when (item) { - is MetaItem.ValueItem -> MetaItem.ValueItem(item.value) - is MetaItem.SingleNodeItem -> MetaItem.SingleNodeItem(seal(item.node)) - is MetaItem.MultiNodeItem -> MetaItem.MultiNodeItem(item.nodes.map { seal(it) }) - } - } - } - return SealedMeta(items) - } - } -} - +class SealedMeta internal constructor(override val items: Map>) : MetaNode() /** * Generate sealed node from [this]. If it is already sealed return it as is */ -fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta.seal(this) +fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta(items.mapValues { entry -> entry.value.seal() }) + +fun MetaItem<*>.seal(): MetaItem = when (this) { + is MetaItem.ValueItem -> MetaItem.ValueItem(value) + is MetaItem.NodeItem -> MetaItem.NodeItem(node.seal()) +} object EmptyMeta : Meta { - override val items: Map> = emptyMap() + override val items: Map> = emptyMap() } /** @@ -113,40 +110,23 @@ object EmptyMeta : Meta { */ val MetaItem<*>.value - get() = (this as? MetaItem.ValueItem)?.value ?: error("Trying to interpret node meta item as value item") + get() = (this as? MetaItem.ValueItem)?.value + ?: (this.node[VALUE_KEY] as? MetaItem.ValueItem)?.value + ?: error("Trying to interpret node meta item as value item") val MetaItem<*>.string get() = value.string val MetaItem<*>.boolean get() = value.boolean val MetaItem<*>.number get() = value.number val MetaItem<*>.double get() = number.toDouble() val MetaItem<*>.int get() = number.toInt() val MetaItem<*>.long get() = number.toLong() +val MetaItem<*>.short get() = number.toShort() val MetaItem.node: M get() = when (this) { is MetaItem.ValueItem -> error("Trying to interpret value meta item as node item") - is MetaItem.SingleNodeItem -> node - is MetaItem.MultiNodeItem -> nodes.first() + is MetaItem.NodeItem -> node } -/** - * Utility method to access item content as list of nodes. - * Returns empty list if it is value item. - */ -val MetaItem.nodes: List - get() = when (this) { - is MetaItem.ValueItem -> emptyList()//error("Trying to interpret value meta item as node item") - is MetaItem.SingleNodeItem -> listOf(node) - is MetaItem.MultiNodeItem -> nodes - } - -fun MetaItem.indexOf(meta: M): Int { - return when (this) { - is MetaItem.ValueItem -> -1 - is MetaItem.SingleNodeItem -> if (node == meta) 0 else -1 - is MetaItem.MultiNodeItem -> nodes.indexOf(meta) - } -} - /** * Generic meta-holder object */ diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt index 647e560d..eb071ee7 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt @@ -37,9 +37,8 @@ fun Meta.builder(): MetaBuilder { items.mapValues { entry -> val item = entry.value builder[entry.key] = when (item) { - is MetaItem.ValueItem -> MetaItem.ValueItem(item.value) - is MetaItem.SingleNodeItem -> MetaItem.SingleNodeItem(item.node.builder()) - is MetaItem.MultiNodeItem -> MetaItem.MultiNodeItem(item.nodes.map { it.builder() }) + is MetaItem.ValueItem -> MetaItem.ValueItem(item.value) + is MetaItem.NodeItem -> MetaItem.NodeItem(item.node.builder()) } } } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt index 4c7b40a4..324194b0 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt @@ -1,6 +1,7 @@ package hep.dataforge.meta import hep.dataforge.names.Name +import hep.dataforge.names.NameToken import hep.dataforge.names.plus import hep.dataforge.names.toName @@ -10,14 +11,16 @@ class MetaListener(val owner: Any? = null, val action: (name: Name, oldItem: Met interface MutableMeta> : Meta { - override val items: Map> + override val items: Map> operator fun set(name: Name, item: MetaItem?) fun onChange(owner: Any? = null, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit) fun removeListener(owner: Any) } /** - * A mutable meta node with attachable change listener + * A mutable meta node with attachable change listener. + * + * Changes in Meta are not thread safe. */ abstract class MutableMetaNode> : MetaNode(), MutableMeta { private val listeners = HashSet() @@ -36,26 +39,24 @@ abstract class MutableMetaNode> : MetaNode(), MutableM listeners.removeAll { it.owner === owner } } - private val _items: MutableMap> = HashMap() + private val _items: MutableMap> = HashMap() - override val items: Map> + override val items: Map> get() = _items protected fun itemChanged(name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) { listeners.forEach { it(name, oldItem, newItem) } } - protected open fun replaceItem(key: String, oldItem: MetaItem?, newItem: MetaItem?) { + protected open fun replaceItem(key: NameToken, oldItem: MetaItem?, newItem: MetaItem?) { if (newItem == null) { _items.remove(key) - oldItem?.nodes?.forEach { - it.removeListener(this) - } + oldItem?.node?.removeListener(this) } else { _items[key] = newItem - newItem.nodes.forEach { - it.onChange(this) { name, oldItem, newItem -> - itemChanged(key.toName() + name, oldItem, newItem) + if(newItem is MetaItem.NodeItem) { + newItem.node.onChange(this) { name, oldChild, newChild -> + itemChanged(key + name, oldChild, newChild) } } } @@ -79,14 +80,13 @@ abstract class MutableMetaNode> : MetaNode(), MutableM 0 -> error("Can't set meta item for empty name") 1 -> { val token = name.first()!! - if (token.hasQuery()) TODO("Queries are not supported in set operations on meta") - replaceItem(token.body, get(name), item) + replaceItem(token, get(name), item) } else -> { val token = name.first()!! //get existing or create new node. Query is ignored for new node - val child = this.items[token.body]?.nodes?.get(token.query) - ?: empty().also { this[token.body.toName()] = MetaItem.SingleNodeItem(it) } + val child = this.items[token]?.node + ?: empty().also { this[token.body.toName()] = MetaItem.NodeItem(it) } child[name.cutFirst()] = item } } @@ -99,13 +99,11 @@ fun > M.remove(name: Name) = set(name, null) fun > M.remove(name: String) = remove(name.toName()) operator fun > M.set(name: Name, value: Value) = set(name, MetaItem.ValueItem(value)) -operator fun > M.set(name: Name, meta: Meta) = set(name, MetaItem.SingleNodeItem(wrap(name, meta))) -operator fun > M.set(name: Name, metas: List) = set(name, MetaItem.MultiNodeItem(metas.map { wrap(name, it) })) - +operator fun > M.set(name: Name, meta: Meta) = set(name, MetaItem.NodeItem(wrap(name, meta))) operator fun > M.set(name: String, item: MetaItem) = set(name.toName(), item) operator fun > M.set(name: String, value: Value) = set(name.toName(), MetaItem.ValueItem(value)) operator fun > M.set(name: String, meta: Meta) = set(name.toName(), meta) -operator fun > M.set(name: String, metas: List) = set(name.toName(), metas) +operator fun > M.set(token: NameToken, item: MetaItem?) = set(token.toName(), item) /** @@ -129,19 +127,23 @@ fun > M.update(meta: Meta) { meta.items.forEach { entry -> val value = entry.value when (value) { - is MetaItem.ValueItem -> this[entry.key] = value.value - is MetaItem.SingleNodeItem -> (this[entry.key] as? MetaItem.SingleNodeItem) - ?.node?.update(value.node) ?: kotlin.run { this[entry.key] = value.node } - is MetaItem.MultiNodeItem -> { - val existing = this[entry.key] - if (existing is MetaItem.MultiNodeItem && existing.nodes.size == value.nodes.size) { - existing.nodes.forEachIndexed { index, m -> - m.update(value.nodes[index]) - } - } else { - this[entry.key] = value.nodes - } - } + is MetaItem.ValueItem -> this[entry.key.toName()] = value.value + is MetaItem.NodeItem -> (this[entry.key.toName()] as? MetaItem.NodeItem)?.node?.update(value.node) + ?: run { this[entry.key.toName()] = value.node } } } -} \ No newline at end of file +} + +// Same name siblings generation + +fun > M.setIndexed(name: Name, metas: Iterable, queryFactory: (Int) -> String = { it.toString() }) { + val tokens = name.tokens.toMutableList() + val last = tokens.last() + metas.forEachIndexed { index, meta -> + val indexedToken = NameToken(last.body, last.query + queryFactory(index)) + tokens[tokens.lastIndex] = indexedToken + set(Name(tokens), meta) + } +} + +operator fun > M.set(name: Name, metas: Iterable) = setIndexed(name, metas) 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 c44763dd..132fba06 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt @@ -4,7 +4,7 @@ package hep.dataforge.meta * Marker interface for specifications */ interface Specification: Configurable{ - operator fun get(name: String): MetaItem? = config.get(name) + operator fun get(name: String): MetaItem? = config[name] } /** diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styleable.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styleable.kt index be8465e2..915ddc89 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styleable.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styleable.kt @@ -1,6 +1,7 @@ package hep.dataforge.meta import hep.dataforge.names.Name +import hep.dataforge.names.NameToken import hep.dataforge.names.toName /** @@ -27,12 +28,11 @@ class StyledConfig(val config: Config, style: Meta = EmptyMeta) : Config() { when (item) { null -> config.remove(name) is MetaItem.ValueItem -> config[name] = item.value - is MetaItem.SingleNodeItem -> config[name] = item.node - is MetaItem.MultiNodeItem -> config[name] = item.nodes + is MetaItem.NodeItem -> config[name] = item.node } } - override val items: Map> + override val items: Map> get() = (config.items.keys + style.items.keys).associate { key -> val value = config.items[key] val styleValue = style[key] @@ -40,16 +40,12 @@ class StyledConfig(val config: Config, style: Meta = EmptyMeta) : Config() { null -> when (styleValue) { null -> error("Should be unreachable") is MetaItem.ValueItem -> MetaItem.ValueItem(styleValue.value) - is MetaItem.SingleNodeItem -> MetaItem.SingleNodeItem(StyledConfig(config.empty(), styleValue.node)) - is MetaItem.MultiNodeItem -> MetaItem.MultiNodeItem(styleValue.nodes.map { StyledConfig(config.empty(), it) }) + is MetaItem.NodeItem -> MetaItem.NodeItem(StyledConfig(config.empty(), styleValue.node)) } is MetaItem.ValueItem -> MetaItem.ValueItem(value.value) - is MetaItem.SingleNodeItem -> MetaItem.SingleNodeItem( + is MetaItem.NodeItem -> MetaItem.NodeItem( StyledConfig(value.node, styleValue?.node ?: EmptyMeta) ) - is MetaItem.MultiNodeItem -> MetaItem.MultiNodeItem(value.nodes.map { - StyledConfig(it, styleValue?.node ?: EmptyMeta) - }) } key to item } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt index 71c61379..98c7733b 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt @@ -58,7 +58,7 @@ class Name internal constructor(val tokens: List) { * Following symbols are prohibited in name tokens: `{}.:\`. * A name token could have appendix in square brackets called *query* */ -data class NameToken internal constructor(val body: String, val query: String) { +data class NameToken(val body: String, val query: String = "") { init { if (body.isEmpty()) error("Syntax error: Name token body is empty") @@ -108,6 +108,8 @@ fun String.toName(): Name { return Name(tokens.toList()) } +operator fun NameToken.plus(other: Name): Name = Name(listOf(this) + other.tokens) + operator fun Name.plus(other: Name): Name = Name(this.tokens + other.tokens) operator fun Name.plus(other: String): Name = this + other.toName() diff --git a/settings.gradle b/settings.gradle index f7aab5b8..71f46856 100644 --- a/settings.gradle +++ b/settings.gradle @@ -17,7 +17,6 @@ pluginManagement { } repositories { - maven { url = 'http://dl.bintray.com/kotlin/kotlin-eap' } mavenCentral() maven { url = 'https://plugins.gradle.org/m2/' } } From dd05897759deb522524780ee068efcaaea3c23e7 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 3 Nov 2018 22:15:36 +0300 Subject: [PATCH 08/37] Fixed json conversion issue for new Meta structure. --- .../hep/dataforge/meta/io/JSONMetaFormat.kt | 17 ++++++++++---- .../kotlin/hep/dataforge/meta/Meta.kt | 23 ++++++++++++++++--- .../hep/dataforge/meta/MutableMetaNode.kt | 13 +++++++---- .../kotlin/hep/dataforge/names/Name.kt | 4 ++-- 4 files changed, 42 insertions(+), 15 deletions(-) diff --git a/dataforge-meta-io/src/jvmMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt b/dataforge-meta-io/src/jvmMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt index e4ac27b9..15314218 100644 --- a/dataforge-meta-io/src/jvmMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt +++ b/dataforge-meta-io/src/jvmMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt @@ -4,6 +4,7 @@ import com.github.cliftonlabs.json_simple.JsonArray import com.github.cliftonlabs.json_simple.JsonObject import com.github.cliftonlabs.json_simple.Jsoner import hep.dataforge.meta.* +import hep.dataforge.names.toName import kotlinx.io.core.* import java.io.ByteArrayInputStream import java.io.InputStreamReader @@ -97,12 +98,18 @@ private fun MetaBuilder.appendValue(key: String, value: Any?) { when (value) { is JsonObject -> this[key] = value.toMeta() is JsonArray -> { - value.forEach { - if (it is JsonArray) { - this[key] = it.toListValue() - } else { - appendValue(key, it) + if (value.none { it is JsonObject }) { + //If all values are primitives or arrays + this[key] = value.toListValue() + } else { + val list = value.map { + when(it){ + is JsonObject -> it.toMeta() + is JsonArray -> it.toListValue().toMeta() + else -> Value.of(it).toMeta() + } } + setIndexed(key.toName(),list) } } is Number -> this[key] = NumberValue(value) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt index f27db651..e12251b1 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -35,6 +35,8 @@ interface Meta { } } +/* Get operations*/ + /** * Fast [String]-based accessor for item map */ @@ -51,10 +53,22 @@ operator fun Meta.get(name: Name): MetaItem? { } operator fun Meta.get(token: NameToken): MetaItem? = items[token] - -//TODO create Java helper for meta operations operator fun Meta.get(key: String): MetaItem? = get(key.toName()) +/** + * Get all items matching given name. + */ +fun Meta.getByName(name: Name): Map> { + if (name.length == 0) error("Can't use empty name for that") + val (body, query) = name.last()!! + val regex = query.toRegex() + return (this[name.cutLast()] as? NodeItem<*>)?.node?.items + ?.filter { it.key.body == body && (query.isEmpty()|| regex.matches(it.key.query)) } + ?.mapKeys { it.key.query } + ?: emptyMap() + +} + /** * A meta node that ensures that all of its descendants has at least the same type */ @@ -91,6 +105,7 @@ abstract class MetaNode> : Meta { * If the argument is possibly mutable node, it is copied on creation */ class SealedMeta internal constructor(override val items: Map>) : MetaNode() + /** * Generate sealed node from [this]. If it is already sealed return it as is */ @@ -132,4 +147,6 @@ val MetaItem.node: M */ interface Metoid { val meta: Meta -} \ No newline at end of file +} + +fun Value.toMeta() = buildMeta { Meta.VALUE_KEY to this } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt index 324194b0..be51180e 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt @@ -54,7 +54,7 @@ abstract class MutableMetaNode> : MetaNode(), MutableM oldItem?.node?.removeListener(this) } else { _items[key] = newItem - if(newItem is MetaItem.NodeItem) { + if (newItem is MetaItem.NodeItem) { newItem.node.onChange(this) { name, oldChild, newChild -> itemChanged(key + name, oldChild, newChild) } @@ -105,7 +105,6 @@ operator fun > M.set(name: String, value: Value) = set(name.t operator fun > M.set(name: String, meta: Meta) = set(name.toName(), meta) operator fun > M.set(token: NameToken, item: MetaItem?) = set(token.toName(), item) - /** * Universal set method */ @@ -134,16 +133,20 @@ fun > M.update(meta: Meta) { } } -// Same name siblings generation +/* Same name siblings generation */ -fun > M.setIndexed(name: Name, metas: Iterable, queryFactory: (Int) -> String = { it.toString() }) { +fun > M.setIndexed(name: Name, items: Iterable>, queryFactory: (Int) -> String = { it.toString() }) { val tokens = name.tokens.toMutableList() val last = tokens.last() - metas.forEachIndexed { index, meta -> + items.forEachIndexed { index, meta -> val indexedToken = NameToken(last.body, last.query + queryFactory(index)) tokens[tokens.lastIndex] = indexedToken set(Name(tokens), meta) } } +fun > M.setIndexed(name: Name, metas: Iterable, queryFactory: (Int) -> String = { it.toString() }) { + setIndexed(name, metas.map { wrap(name, it) }, queryFactory) +} + operator fun > M.set(name: Name, metas: Iterable) = setIndexed(name, metas) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt index 98c7733b..1eae4182 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt @@ -22,12 +22,12 @@ class Name internal constructor(val tokens: List) { fun last(): NameToken? = tokens.lastOrNull() /** - * The reminder of the name after first element is cut + * The reminder of the name after first element is cut. For empty name return itself. */ fun cutFirst(): Name = Name(tokens.drop(1)) /** - * The reminder of the name after last element is cut + * The reminder of the name after last element is cut. For empty name return itself. */ fun cutLast(): Name = Name(tokens.dropLast(1)) From d3ce88eb3f8532106de150f2481b149171ee7917 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 6 Nov 2018 14:09:43 +0300 Subject: [PATCH 09/37] Context module --- build.gradle | 2 - dataforge-context/build.gradle | 32 ++++ .../kotlin/hep/dataforge/context/Context.kt | 106 +++++++++++++ .../kotlin/hep/dataforge/context/Named.kt | 57 +++++++ .../kotlin/hep/dataforge/context/Plugin.kt | 78 ++++++++++ .../hep/dataforge/context/PluginManager.kt | 145 ++++++++++++++++++ .../hep/dataforge/context/PluginRepository.kt | 25 +++ .../kotlin/hep/dataforge/context/PluginTag.kt | 62 ++++++++ .../kotlin/hep/dataforge/provider/Path.kt | 75 +++++++++ .../kotlin/hep/dataforge/provider/Provider.kt | 89 +++++++++++ .../kotlin/hep/dataforge/context/Global.kt | 76 +++++++++ .../hep/dataforge/context/JVMContext.kt | 125 +++++++++++++++ dataforge-data/build.gradle | 25 +++ dataforge-io/build.gradle | 25 +++ .../hep/dataforge/meta/io/MetaFormat.kt | 16 +- .../hep/dataforge/meta/io/MetaFormatTest.kt | 14 ++ .../kotlin/hep/dataforge/meta/io/JSMeta.kt | 4 +- .../hep/dataforge/meta/io/JSONMetaFormat.kt | 16 +- .../kotlin/hep/dataforge/meta/Config.kt | 3 +- .../kotlin/hep/dataforge/meta/Delegates.kt | 2 + .../kotlin/hep/dataforge/meta/Laminate.kt | 12 +- .../kotlin/hep/dataforge/meta/Meta.kt | 35 ++++- .../kotlin/hep/dataforge/meta/MetaBuilder.kt | 4 +- .../hep/dataforge/meta/MutableMetaNode.kt | 30 ++-- .../kotlin/hep/dataforge/meta/Styleable.kt | 4 +- .../kotlin/hep/dataforge/names/Name.kt | 17 +- .../hep/dataforge/{meta => values}/Value.kt | 38 ++++- .../hep/dataforge/meta/MetaBuilderTest.kt | 1 + .../kotlin/hep/dataforge/meta/MetaTest.kt | 33 ++++ .../kotlin/hep/dataforge/names/NameTest.kt | 7 + dataforge-tables/build.gradle | 25 +++ dataforge-workspace/build.gradle | 25 +++ settings.gradle | 8 + 33 files changed, 1151 insertions(+), 65 deletions(-) create mode 100644 dataforge-context/build.gradle create mode 100644 dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt create mode 100644 dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Named.kt create mode 100644 dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt create mode 100644 dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt create mode 100644 dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginRepository.kt create mode 100644 dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginTag.kt create mode 100644 dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Path.kt create mode 100644 dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Provider.kt create mode 100644 dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/Global.kt create mode 100644 dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt create mode 100644 dataforge-data/build.gradle create mode 100644 dataforge-io/build.gradle rename dataforge-meta/src/commonMain/kotlin/hep/dataforge/{meta => values}/Value.kt (80%) create mode 100644 dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaTest.kt create mode 100644 dataforge-tables/build.gradle create mode 100644 dataforge-workspace/build.gradle diff --git a/build.gradle b/build.gradle index 76235d81..38a7fe77 100644 --- a/build.gradle +++ b/build.gradle @@ -20,9 +20,7 @@ allprojects { repositories { jcenter() - maven { url = "http://dl.bintray.com/kotlin/kotlin-eap" } maven { url = "https://kotlin.bintray.com/kotlinx" } - //maven { url 'https://jitpack.io' } } group = 'hep.dataforge' diff --git a/dataforge-context/build.gradle b/dataforge-context/build.gradle new file mode 100644 index 00000000..c7228860 --- /dev/null +++ b/dataforge-context/build.gradle @@ -0,0 +1,32 @@ +plugins { + id 'kotlin-multiplatform' +} + +repositories { + jcenter() +} + +kotlin { + targets { + fromPreset(presets.jvm, 'jvm') + //fromPreset(presets.js, 'js') + // For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64 + // For Linux, preset should be changed to e.g. presets.linuxX64 + // For MacOS, preset should be changed to e.g. presets.macosX64 + //fromPreset(presets.iosX64, 'ios') + } + sourceSets { + commonMain { + dependencies { + api project(":dataforge-meta") + api "org.jetbrains.kotlin:kotlin-reflect" + api "io.github.microutils:kotlin-logging-common:1.6.10" + } + } + jvmMain{ + dependencies{ + api "io.github.microutils:kotlin-logging:1.6.10" + } + } + } +} \ No newline at end of file diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt new file mode 100644 index 00000000..f88e7cf3 --- /dev/null +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt @@ -0,0 +1,106 @@ +package hep.dataforge.context + +import hep.dataforge.meta.* +import hep.dataforge.names.Name +import hep.dataforge.names.toName +import hep.dataforge.provider.Provider +import hep.dataforge.provider.provideAll +import hep.dataforge.values.Value +import mu.KLogger +import mu.KotlinLogging +import kotlin.reflect.KClass + +interface Context : Named, MetaRepr, Provider { + + val parent: Context? + + /** + * Context properties. Working as substitutes for environment variables + */ + val properties: Meta + + /** + * Context logger + */ + val logger: KLogger + + val plugins: PluginManager + + /** + * Defines if context is used in any kind of active computations. Active context properties and plugins could not be changed + */ + val isActive: Boolean + + /** + * Provide services for given type + */ + fun services(type: KClass): Sequence + + override val defaultTarget: String get() = Plugin.PLUGIN_TARGET + + override fun provideTop(target: String, name: Name): Any? { + return when (target) { + Plugin.PLUGIN_TARGET -> plugins[PluginTag.fromString(name.toString())] + Value.VALUE_TARGET -> properties[name]?.value + else -> null + } + } + + override fun listTop(target: String): Sequence { + return when (target) { + Plugin.PLUGIN_TARGET -> plugins.asSequence().map { it.name.toName() } + Value.VALUE_TARGET -> properties.asValueSequence().map { it.first } + else -> emptySequence() + } + } + + /** + * Mark context as active and used by [activator] + */ + fun activate(activator: Any) + + /** + * Mark context unused by [activator] + */ + fun deactivate(activator: Any) + + /** + * Detach all plugins and terminate context + */ + fun close() +} + +/** + * A sequences of all objects provided by plugins with given target and type + */ +inline fun Context.list(target: String): Sequence { + return plugins.asSequence().flatMap { provideAll(target) }.mapNotNull { it as? T } +} + +/** + * A global root context + */ +expect object Global : Context + +/** + * The interface for something that encapsulated in context + * + * @author Alexander Nozik + * @version $Id: $Id + */ +interface ContextAware { + /** + * Get context for this object + * + * @return + */ + val context: Context + + val logger: KLogger + get() = if (this is Named) { + KotlinLogging.logger(context.name + "." + (this as Named).name) + } else { + context.logger + } + +} \ No newline at end of file diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Named.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Named.kt new file mode 100644 index 00000000..1ac31702 --- /dev/null +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Named.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2015 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 hep.dataforge.context + +/** + * Any object that have name + * + * @author Alexander Nozik + */ +interface Named { + + /** + * The name of this object instance + * + * @return + */ + val name: String + + companion object { + const val ANONYMOUS = "" + + /** + * Get the name of given object. If object is Named its name is used, + * otherwise, use Object.toString + * + * @param obj + * @return + */ + fun nameOf(obj: Any): String { + return if (obj is Named) { + obj.name + } else { + obj.toString() + } + } + } +} + +/** + * Check if this object has an empty name and therefore is anonymous. + * @return + */ +val Named.isAnonymous: Boolean + get() = this.name == Named.ANONYMOUS diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt new file mode 100644 index 00000000..56b2a63d --- /dev/null +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt @@ -0,0 +1,78 @@ +package hep.dataforge.context + +import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaRepr +import hep.dataforge.meta.Metoid +import hep.dataforge.meta.buildMeta +import hep.dataforge.provider.Provider + +/** + * The interface to define a Context plugin. A plugin stores all runtime features of a context. + * The plugin is by default configurable and a Provider (both features could be ignored). + * The plugin must in most cases have an empty constructor in order to be able to load it from library. + * + * + * The plugin lifecycle is the following: + * + * + * create - configure - attach - detach - destroy + * + * + * Configuration of attached plugin is possible for a context which is not in a runtime mode, but it is not recommended. + * + * @author Alexander Nozik + */ +interface Plugin : Named, Metoid, ContextAware, Provider, MetaRepr { + + /** + * Get tag for this plugin + * + * @return + */ + val tag: PluginTag + + /** + * The name of this plugin ignoring version and group + * + * @return + */ + override val name: String + get() = tag.name + + /** + * Plugin dependencies which are required to attach this plugin. Plugin + * dependencies must be initialized and enabled in the Context before this + * plugin is enabled. + * + * @return + */ + fun dependsOn(): List + + /** + * Start this plugin and attach registration info to the context. This method + * should be called only via PluginManager to avoid dependency issues. + * + * @param context + */ + fun attach(context: Context) + + /** + * Stop this plugin and remove registration info from context and other + * plugins. This method should be called only via PluginManager to avoid + * dependency issues. + */ + fun detach() + + override fun toMeta(): Meta = buildMeta { + "context" to context.name + "type" to this::class.qualifiedName + "tag" to tag + "meta" to meta + } + + companion object { + + const val PLUGIN_TARGET = "plugin" + } + +} \ No newline at end of file diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt new file mode 100644 index 00000000..d50893e2 --- /dev/null +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt @@ -0,0 +1,145 @@ +package hep.dataforge.context + +import hep.dataforge.meta.EmptyMeta +import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaBuilder +import hep.dataforge.meta.buildMeta +import kotlin.reflect.KClass + +/** + * The manager for plugin system. Should monitor plugin dependencies and locks. + * + * @property context A context for this plugin manager + * @author Alexander Nozik + */ +class PluginManager(override val context: Context) : ContextAware, Iterable { + + /** + * A set of loaded plugins + */ + private val plugins = HashSet() + + private val parent: PluginManager? = context.parent?.plugins + + + fun sequence(recursive: Boolean): Sequence { + return if (recursive && parent != null) { + plugins.asSequence() + parent.sequence(true) + } else { + plugins.asSequence() + } + } + + /** + * Get for existing plugin + */ + fun get(recursive: Boolean = true, predicate: (Plugin) -> Boolean): Plugin? = sequence(recursive).find(predicate) + + + /** + * Find a loaded plugin via its tag + * + * @param tag + * @return + */ + operator fun get(tag: PluginTag, recursive: Boolean = true): Plugin? = get(recursive) { tag.matches(it.tag) } + + + /** + * Find a loaded plugin via its class + * + * @param tag + * @param type + * @param + * @return + */ + @Suppress("UNCHECKED_CAST") + operator fun get(type: KClass, recursive: Boolean = true): T? = get(recursive) { type.isInstance(it) } as T? + + inline fun get(recursive: Boolean = true): T? = get(T::class, recursive) + + + /** + * Load given plugin into this manager and return loaded instance. + * Throw error if plugin of the same class already exists in manager + * + * @param plugin + * @return + */ + fun load(plugin: T): T { + if (context.isActive) error("Can't load plugin into active context") + + if (get(plugin::class, false) != null) { + throw RuntimeException("Plugin of type ${plugin::class} already exists in ${context.name}") + } else { + loadDependencies(plugin) + + logger.info { "Loading plugin ${plugin.name} into ${context.name}" } + plugin.attach(context) + plugins.add(plugin) + return plugin + } + } + + private fun loadDependencies(plugin: Plugin) { + for (tag in plugin.dependsOn()) { + load(tag) + } + } + + fun remove(plugin: Plugin) { + if (context.isActive) error("Can't remove plugin from active context") + + if (plugins.contains(plugin)) { + logger.info { "Removing plugin ${plugin.name} from ${context.name}" } + plugin.detach() + plugins.remove(plugin) + } + } + + /** + * Get plugin instance via plugin reolver and load it. + * + * @param tag + * @return + */ + fun load(tag: PluginTag, meta: Meta = EmptyMeta): Plugin { + val loaded = get(tag, false) + return when { + loaded == null -> load(PluginRepository.fetch(tag, meta)) + loaded.meta == meta -> loaded // if meta is the same, return existing plugin + else -> throw RuntimeException("Can't load plugin with tag $tag. Plugin with this tag and different configuration already exists in context.") + } + } + + /** + * Load plugin by its class and meta. Ignore if plugin with this meta is already loaded. + */ + fun load(type: KClass, meta: Meta = EmptyMeta): T { + val loaded = get(type, false) + return when { + loaded == null -> { + val plugin = PluginRepository.list().first { it.type == type }.build(meta) + if (type.isInstance(plugin)) { + @Suppress("UNCHECKED_CAST") + load(plugin as T) + } else { + error("Corrupt type information in plugin repository") + } + } + loaded.meta == meta -> loaded // if meta is the same, return existing plugin + else -> throw RuntimeException("Can't load plugin with type $type. Plugin with this type and different configuration already exists in context.") + } + } + + inline fun load(noinline metaBuilder: MetaBuilder.() -> Unit = {}): T { + return load(T::class, buildMeta(metaBuilder)) + } + + fun load(name: String, meta: Meta = EmptyMeta): Plugin { + return load(PluginTag.fromString(name), meta) + } + + override fun iterator(): Iterator = plugins.iterator() + +} \ No newline at end of file diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginRepository.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginRepository.kt new file mode 100644 index 00000000..3ac99da4 --- /dev/null +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginRepository.kt @@ -0,0 +1,25 @@ +package hep.dataforge.context + +import hep.dataforge.meta.Meta +import kotlin.reflect.KClass + +interface PluginFactory { + val tag: PluginTag + val type: KClass + fun build(meta: Meta): Plugin +} + + +object PluginRepository { + + /** + * List plugins available in the repository + */ + fun list(): Sequence = Global.services(PluginFactory::class) + + /** + * Fetch specific plugin and instantiate it with given meta + */ + fun fetch(tag: PluginTag, meta: Meta): Plugin = PluginRepository.list().find { it.tag.matches(tag) }?.build(meta) + ?: error("Plugin with tag $tag not found in the repository") +} \ No newline at end of file diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginTag.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginTag.kt new file mode 100644 index 00000000..6196a8cb --- /dev/null +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginTag.kt @@ -0,0 +1,62 @@ +package hep.dataforge.context + +import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaRepr +import hep.dataforge.meta.buildMeta + +/** + * The tag which contains information about name, group and version of some + * object. It also could contain any complex rule to define version ranges + * + * @author Alexander Nozik + */ +data class PluginTag( + val name: String, + val group: String = "", + val version: String = "" +) : MetaRepr { + + /** + * Check if given tag is compatible (in range) of this tag + * + * @param otherTag + * @return + */ + fun matches(otherTag: PluginTag): Boolean { + return matchesName(otherTag) && matchesGroup(otherTag) + } + + private fun matchesGroup(otherTag: PluginTag): Boolean { + return this.group.isEmpty() || this.group == otherTag.group + } + + private fun matchesName(otherTag: PluginTag): Boolean { + return this.name == otherTag.name + } + + override fun toString(): String = listOf(group, name, version).joinToString(separator = ":") + + override fun toMeta(): Meta = buildMeta { + "name" to name + "group" to group + "version" to version + } + + companion object { + + /** + * Build new PluginTag from standard string representation + * + * @param tag + * @return + */ + fun fromString(tag: String): PluginTag { + val sepIndex = tag.indexOf(":") + return if (sepIndex >= 0) { + PluginTag(group = tag.substring(0, sepIndex), name = tag.substring(sepIndex + 1)) + } else { + PluginTag(tag) + } + } + } +} \ No newline at end of file diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Path.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Path.kt new file mode 100644 index 00000000..06c36ab0 --- /dev/null +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Path.kt @@ -0,0 +1,75 @@ +/* + * Copyright 2015 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 hep.dataforge.provider + +import hep.dataforge.names.Name +import hep.dataforge.names.toName + +/** + * + * + * Path interface. + * + * @author Alexander Nozik + * @version $Id: $Id + */ +inline class Path(val tokens: List) : Iterable { + + val head: PathToken? get() = tokens.firstOrNull() + + val length: Int get() = tokens.size + + /** + * Returns non-empty optional containing the chain without first segment in case of chain path. + * + * @return + */ + val tail: Path? get() = if (tokens.isEmpty()) null else Path(tokens.drop(1)) + + override fun iterator(): Iterator = tokens.iterator() + + companion object { + const val PATH_SEGMENT_SEPARATOR = "/" + + fun parse(path: String): Path { + val head = path.substringBefore(PATH_SEGMENT_SEPARATOR) + val tail = path.substringAfter(PATH_SEGMENT_SEPARATOR) + return PathToken.parse(head).toPath() + parse(tail) + } + } +} + +operator fun Path.plus(path: Path) = Path(this.tokens + path.tokens) + +data class PathToken(val name: Name, val target: String? = null) { + override fun toString(): String = if (target == null) { + name.toString() + } else { + "$target$TARGET_SEPARATOR$name" + } + + companion object { + const val TARGET_SEPARATOR = "::" + fun parse(token: String): PathToken { + val target = token.substringBefore(TARGET_SEPARATOR, "") + val name = token.substringAfter(TARGET_SEPARATOR).toName() + if (target.contains("[")) TODO("target separators in queries are not supported") + return PathToken(name, target) + } + } +} + +fun PathToken.toPath() = Path(listOf(this)) diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Provider.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Provider.kt new file mode 100644 index 00000000..7d37d609 --- /dev/null +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Provider.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2015 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 hep.dataforge.provider + +import hep.dataforge.names.Name +import hep.dataforge.names.toName + +/** + * A marker utility interface for providers. + * + * @author Alexander Nozik + */ +interface Provider { + + /** + * Default target for this provider + * + * @return + */ + val defaultTarget: String get() = "" + + /** + * Default target for next chain segment + * + * @return + */ + val defaultChainTarget: String get() = "" + + + /** + * Provide a top level element for this [Provider] or return null if element is not present + */ + fun provideTop(target: String, name: Name): Any? + + /** + * [Sequence] of available names with given target. Only top level names are listed, no chain path. + * + * @param target + * @return + */ + fun listTop(target: String): Sequence +} + +fun Provider.provide(path: Path, targetOverride: String? = null): Any? { + if (path.length == 0) throw IllegalArgumentException("Can't provide by empty path") + val first = path.first() + val top = provideTop(targetOverride ?: first.target ?: defaultTarget, first.name) + return when (path.length) { + 1 -> top + else -> { + when (top) { + null -> null + is Provider -> top.provide(path.tail!!, targetOverride = defaultChainTarget) + else -> throw IllegalStateException("Chain path not supported: child is not a provider") + } + } + } +} + +/** + * Type checked provide + */ +inline fun Provider.provide(path: String): T? { + return provide(Path.parse(path)) as? T +} + +inline fun Provider.provide(target: String, name: String): T? { + return provide(PathToken(name.toName(), target).toPath()) as? T +} + +/** + * [Sequence] of all elements with given target + */ +fun Provider.provideAll(target: String): Sequence { + return listTop(target).map { it -> provideTop(target, it) ?: error("The element $it is declared but not provided") } +} diff --git a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/Global.kt b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/Global.kt new file mode 100644 index 00000000..7564b5fb --- /dev/null +++ b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/Global.kt @@ -0,0 +1,76 @@ +/* + * 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 hep.dataforge.context + +import java.util.* +import kotlin.collections.HashMap + + +private fun Properties.asMeta(): Meta { + return buildMeta { + this@asMeta.forEach { key, value -> + set(key.toString().toName(), value) + } + } +} + +/** + * A singleton global context. Automatic root for the whole context hierarchy. Also stores the registry for active contexts. + * + * @author Alexander Nozik + */ +actual object Global : Context, JVMContext("GLOBAL", null, Thread.currentThread().contextClassLoader) { + + /** + * Closing all contexts + * + * @throws Exception + */ + @Throws(Exception::class) + override fun close() { + logger.info("Shutting down GLOBAL") + for (ctx in contextRegistry.values) { + ctx.close() + } + super.close() + } + + private val contextRegistry = HashMap() + + /** + * Get previously builder context o builder a new one + * + * @param name + * @return + */ + @Synchronized + fun getContext(name: String): Context { + return contextRegistry.getOrPut(name) { JVMContext(name) } + } + + /** + * Close all contexts and terminate framework + */ + @JvmStatic + fun terminate() { + try { + close() + } catch (e: Exception) { + logger.error("Exception while terminating DataForge framework", e) + } + + } +} diff --git a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt new file mode 100644 index 00000000..448eb63e --- /dev/null +++ b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt @@ -0,0 +1,125 @@ +/* + * 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 hep.dataforge.context + +import mu.KLogger +import mu.KotlinLogging +import java.lang.ref.WeakReference +import java.util.* +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors +import kotlin.collections.HashSet +import kotlin.reflect.KClass +import kotlin.reflect.full.cast + +/** + * The local environment for anything being done in DataForge framework. Contexts are organized into tree structure with [Global] at the top. + * Each context has a set of named [Value] properties which are taken from parent context in case they are not found in local context. + * Context implements [ValueProvider] interface and therefore could be uses as a value source for substitutions etc. + * Context contains [PluginManager] which could be used any number of configurable named plugins. + * @author Alexander Nozik + */ +open class JVMContext( + final override val name: String, + final override val parent: JVMContext? = Global, + classLoader: ClassLoader? = null, + properties: Meta = EmptyMeta +) : Context, AutoCloseable { + + private val _properties = Config().apply { update(properties) } + override val properties: Meta + get() = if (parent == null) { + _properties + } else { + Laminate(_properties, parent.properties) + } + + override val plugins: PluginManager by lazy { PluginManager(this) } + override val logger: KLogger = KotlinLogging.logger(name) + + /** + * A class loader for this context. Parent class loader is used by default + */ + open val classLoader: ClassLoader = classLoader ?: parent?.classLoader ?: Global.classLoader + + /** + * A property showing that dispatch thread is started in the context + */ + private var started = false + + /** + * A dispatch thread executor for current context + * + * @return + */ + val dispatcher: ExecutorService by lazy { + logger.info("Initializing dispatch thread executor in {}", name) + Executors.newSingleThreadExecutor { r -> + Thread(r).apply { + priority = 8 // slightly higher priority + isDaemon = true + name = this@JVMContext.name + "_dispatch" + }.also { started = true } + } + } + + private val serviceCache: MutableMap, ServiceLoader<*>> = HashMap() + + override fun services(type: KClass): Sequence { + return serviceCache.getOrPut(type.java) { ServiceLoader.load(type.java, classLoader) }.asSequence().map { type.cast(it) } + } + + /** + * Get identity for this context + * + * @return + */ + override fun toMeta(): Meta { + return buildMeta { + "parent" to parent?.name + "properties" to properties.seal() + "plugins" to plugins.map { it.toMeta() } + } + } + + /** + * Free up resources associated with this context + * + * @throws Exception + */ + override fun close() { + if (isActive) error("Can't close active context") + //detach all plugins + plugins.forEach { it.detach() } + + if (started) { + dispatcher.shutdown() + } + } + + private val activators = HashSet>() + + override val isActive: Boolean = activators.all { it.get() == null } + + override fun activate(activator: Any) { + activators.add(WeakReference(activator)) + } + + override fun deactivate(activator: Any) { + activators.removeAll { it.get() == activator } + } +} + diff --git a/dataforge-data/build.gradle b/dataforge-data/build.gradle new file mode 100644 index 00000000..512fff84 --- /dev/null +++ b/dataforge-data/build.gradle @@ -0,0 +1,25 @@ +plugins { + id 'kotlin-multiplatform' +} + +repositories { + jcenter() +} + +kotlin { + targets { + fromPreset(presets.jvm, 'jvm') + //fromPreset(presets.js, 'js') + // For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64 + // For Linux, preset should be changed to e.g. presets.linuxX64 + // For MacOS, preset should be changed to e.g. presets.macosX64 + //fromPreset(presets.iosX64, 'ios') + } + sourceSets { + commonMain { + dependencies { + api project(":dataforge-context") + } + } + } +} \ No newline at end of file diff --git a/dataforge-io/build.gradle b/dataforge-io/build.gradle new file mode 100644 index 00000000..512fff84 --- /dev/null +++ b/dataforge-io/build.gradle @@ -0,0 +1,25 @@ +plugins { + id 'kotlin-multiplatform' +} + +repositories { + jcenter() +} + +kotlin { + targets { + fromPreset(presets.jvm, 'jvm') + //fromPreset(presets.js, 'js') + // For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64 + // For Linux, preset should be changed to e.g. presets.linuxX64 + // For MacOS, preset should be changed to e.g. presets.macosX64 + //fromPreset(presets.iosX64, 'ios') + } + sourceSets { + commonMain { + dependencies { + api project(":dataforge-context") + } + } + } +} \ No newline at end of file diff --git a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt index 552f9532..4f78661b 100644 --- a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt +++ b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt @@ -1,6 +1,7 @@ package hep.dataforge.meta.io import hep.dataforge.meta.* +import hep.dataforge.values.* import kotlinx.io.core.* /** @@ -20,20 +21,12 @@ fun MetaFormat.stringify(meta: Meta): String { return builder.build().readText() } +fun Meta.asString() = JSONMetaFormat.stringify(this) + fun MetaFormat.parse(str: String): Meta{ return read(ByteReadPacket(str.toByteArray())) } -///** -// * Resolve format by its name. Null if not provided -// */ -//expect fun resolveFormat(name: String): MetaFormat? -// -///** -// * Resolve format by its binary key. Null if not provided -// */ -//expect fun resolveFormat(key: Short): MetaFormat? - internal expect fun writeJson(meta: Meta, out: Output) internal expect fun readJson(input: Input, length: Int = -1): Meta @@ -155,7 +148,7 @@ object BinaryMetaFormat : MetaFormat { (1..length).forEach { _ -> val name = readString() val item = readMetaItem() - set(name, item) + setItem(name, item) } } MetaItem.NodeItem(meta) @@ -163,6 +156,5 @@ object BinaryMetaFormat : MetaFormat { else -> error("Unknown serialization key character: $keyChar") } } - } diff --git a/dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt b/dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt index 1a4e9060..72851d90 100644 --- a/dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt +++ b/dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt @@ -19,4 +19,18 @@ class MetaFormatTest{ assertEquals(meta,result) } + @Test + fun testJsonMetaFormat(){ + val meta = buildMeta { + "a" to 22 + "node" to { + "b" to "DDD" + "c" to 11.1 + } + } + val string = JSONMetaFormat.stringify(meta) + val result = JSONMetaFormat.parse(string) + assertEquals(meta,result) + } + } \ No newline at end of file diff --git a/dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSMeta.kt b/dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSMeta.kt index 3efa97ee..e024c319 100644 --- a/dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSMeta.kt +++ b/dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSMeta.kt @@ -2,8 +2,8 @@ package hep.dataforge.meta.io import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaItem -import hep.dataforge.meta.Value import hep.dataforge.names.NameToken +import hep.dataforge.values.Value /** * Represent any js object as meta @@ -16,7 +16,7 @@ class JSMeta(val obj: Any) : Meta { private fun isList(obj: Any): Boolean = js("Array").isArray(obj) as Boolean - private fun isPrimitive(obj: Any?): Boolean = js("obj !== Object(obj)") as Boolean + private fun isPrimitive(@Suppress("UNUSED_PARAMETER") obj: Any?): Boolean = js("obj !== Object(obj)") as Boolean private fun convert(obj: Any?): MetaItem { return when (obj) { diff --git a/dataforge-meta-io/src/jvmMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt b/dataforge-meta-io/src/jvmMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt index 15314218..ae597b53 100644 --- a/dataforge-meta-io/src/jvmMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt +++ b/dataforge-meta-io/src/jvmMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt @@ -5,10 +5,12 @@ import com.github.cliftonlabs.json_simple.JsonObject import com.github.cliftonlabs.json_simple.Jsoner import hep.dataforge.meta.* import hep.dataforge.names.toName +import hep.dataforge.values.* import kotlinx.io.core.* import java.io.ByteArrayInputStream import java.io.InputStreamReader import java.io.Reader +import java.nio.ByteBuffer import java.text.ParseException internal actual fun writeJson(meta: Meta, out: Output) { @@ -31,7 +33,7 @@ private fun Value.toJson(): Any { } } -private fun Meta.toJson(): JsonObject { +fun Meta.toJson(): JsonObject { val builder = JsonObject() items.forEach { name, item -> when (item) { @@ -60,9 +62,11 @@ internal actual fun readJson(input: Input, length: Int): Meta { } override fun read(cbuf: CharArray, off: Int, len: Int): Int { - val block = input.readText(Charsets.UTF_8, len).toCharArray() - System.arraycopy(block, 0, cbuf, off, block.size) - return block.size + val buffer = ByteBuffer.allocate(len) + val res = input.readAvailable(buffer) + val chars = String(buffer.array()).toCharArray() + System.arraycopy(chars, 0, cbuf, off, chars.size) + return res } } @@ -103,13 +107,13 @@ private fun MetaBuilder.appendValue(key: String, value: Any?) { this[key] = value.toListValue() } else { val list = value.map { - when(it){ + when (it) { is JsonObject -> it.toMeta() is JsonArray -> it.toListValue().toMeta() else -> Value.of(it).toMeta() } } - setIndexed(key.toName(),list) + setIndexed(key.toName(), list) } } is Number -> this[key] = NumberValue(value) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt index 1caef790..4a5d23fe 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt @@ -1,6 +1,7 @@ package hep.dataforge.meta import hep.dataforge.names.Name +import hep.dataforge.names.toName //TODO add validator to configuration @@ -20,7 +21,7 @@ open class Config : MutableMetaNode() { fun Meta.toConfig(): Config = this as? Config ?: Config().also { builder -> this.items.mapValues { entry -> val item = entry.value - builder[entry.key] = when (item) { + builder[entry.key.toName()] = when (item) { is MetaItem.ValueItem -> MetaItem.ValueItem(item.value) is MetaItem.NodeItem -> MetaItem.NodeItem(item.node.toConfig()) } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Delegates.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Delegates.kt index bfb5eaae..f63a271b 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Delegates.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Delegates.kt @@ -1,5 +1,7 @@ package hep.dataforge.meta +import hep.dataforge.values.Null +import hep.dataforge.values.Value import kotlin.jvm.JvmName import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadWriteProperty diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt index 295f825b..5d8f13c5 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt @@ -7,7 +7,17 @@ import hep.dataforge.names.NameToken * * */ -class Laminate(val layers: List) : Meta { +class Laminate(layers: List) : Meta { + + val layers: List = layers.flatMap { + if(it is Laminate){ + it.layers + } else{ + listOf(it) + } + } + + constructor(vararg layers: Meta): this(layers.asList()) override val items: Map> get() = layers.map { it.items.keys }.flatten().associateWith { key -> diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt index e12251b1..12dc3e2f 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -5,7 +5,11 @@ import hep.dataforge.meta.MetaItem.NodeItem import hep.dataforge.meta.MetaItem.ValueItem import hep.dataforge.names.Name import hep.dataforge.names.NameToken +import hep.dataforge.names.plus import hep.dataforge.names.toName +import hep.dataforge.values.Value +import hep.dataforge.values.boolean + /** * A member of the meta tree. Could be represented as one of following: @@ -17,6 +21,14 @@ sealed class MetaItem { data class NodeItem(val node: M) : MetaItem() } +/** + * The object that could be represented as [Meta]. Meta provided by [toMeta] method should fully represent object state. + * Meaning that two states with the same meta are equal. + */ +interface MetaRepr { + fun toMeta(): Meta +} + /** * Generic meta tree representation. Elements are [MetaItem] objects that could be represented by three different entities: * * [MetaItem.ValueItem] (leaf) @@ -24,9 +36,11 @@ sealed class MetaItem { * * * Same name siblings are supported via elements with the same [Name] but different queries */ -interface Meta { +interface Meta : MetaRepr { val items: Map> + override fun toMeta(): Meta = this + companion object { /** * A key for single value node @@ -58,17 +72,32 @@ operator fun Meta.get(key: String): MetaItem? = get(key.toName()) /** * Get all items matching given name. */ -fun Meta.getByName(name: Name): Map> { +fun Meta.getAll(name: Name): Map> { if (name.length == 0) error("Can't use empty name for that") val (body, query) = name.last()!! val regex = query.toRegex() return (this[name.cutLast()] as? NodeItem<*>)?.node?.items - ?.filter { it.key.body == body && (query.isEmpty()|| regex.matches(it.key.query)) } + ?.filter { it.key.body == body && (query.isEmpty() || regex.matches(it.key.query)) } ?.mapKeys { it.key.query } ?: emptyMap() } +/** + * Transform meta to sequence of [Name]-[Value] pairs + */ +fun Meta.asValueSequence(): Sequence> { + return items.asSequence().flatMap { entry -> + val item = entry.value + when (item) { + is ValueItem -> sequenceOf(entry.key.toName() to item.value) + is NodeItem -> item.node.asValueSequence().map { pair -> (entry.key.toName() + pair.first) to pair.second } + } + } +} + +operator fun Meta.iterator(): Iterator> = asValueSequence().iterator() + /** * A meta node that ensures that all of its descendants has at least the same type */ diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt index eb071ee7..61310d67 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt @@ -1,6 +1,8 @@ package hep.dataforge.meta import hep.dataforge.names.Name +import hep.dataforge.names.toName +import hep.dataforge.values.Value /** * DSL builder for meta. Is not intended to store mutable state @@ -36,7 +38,7 @@ fun Meta.builder(): MetaBuilder { return MetaBuilder().also { builder -> items.mapValues { entry -> val item = entry.value - builder[entry.key] = when (item) { + builder[entry.key.toName()] = when (item) { is MetaItem.ValueItem -> MetaItem.ValueItem(item.value) is MetaItem.NodeItem -> MetaItem.NodeItem(item.node.builder()) } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt index be51180e..48729efb 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt @@ -4,6 +4,7 @@ import hep.dataforge.names.Name import hep.dataforge.names.NameToken import hep.dataforge.names.plus import hep.dataforge.names.toName +import hep.dataforge.values.Value class MetaListener(val owner: Any? = null, val action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit) { operator fun invoke(name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) = action(name, oldItem, newItem) @@ -77,7 +78,7 @@ abstract class MutableMetaNode> : MetaNode(), MutableM override operator fun set(name: Name, item: MetaItem?) { when (name.length) { - 0 -> error("Can't set meta item for empty name") + 0 -> error("Can't setValue meta item for empty name") 1 -> { val token = name.first()!! replaceItem(token, get(name), item) @@ -98,24 +99,27 @@ abstract class MutableMetaNode> : MetaNode(), MutableM fun > M.remove(name: Name) = set(name, null) fun > M.remove(name: String) = remove(name.toName()) -operator fun > M.set(name: Name, value: Value) = set(name, MetaItem.ValueItem(value)) -operator fun > M.set(name: Name, meta: Meta) = set(name, MetaItem.NodeItem(wrap(name, meta))) -operator fun > M.set(name: String, item: MetaItem) = set(name.toName(), item) -operator fun > M.set(name: String, value: Value) = set(name.toName(), MetaItem.ValueItem(value)) -operator fun > M.set(name: String, meta: Meta) = set(name.toName(), meta) -operator fun > M.set(token: NameToken, item: MetaItem?) = set(token.toName(), item) +fun > M.setValue(name: Name, value: Value) = set(name, MetaItem.ValueItem(value)) +fun > M.setItem(name: String, item: MetaItem) = set(name.toName(), item) +fun > M.setValue(name: String, value: Value) = set(name.toName(), MetaItem.ValueItem(value)) +fun > M.setItem(token: NameToken, item: MetaItem?) = set(token.toName(), item) + +fun > M.setNode(name: Name, node: Meta) = set(name, MetaItem.NodeItem(wrap(name, node))) +fun > M.setNode(name: String, node: Meta) = setNode(name.toName(), node) /** * Universal set method */ -operator fun > M.set(key: String, value: Any?) { +operator fun > M.set(name: Name, value: Any?) { when (value) { - null -> remove(key) - is Meta -> set(key, value) - else -> set(key, Value.of(value)) + null -> remove(name) + is Meta -> setNode(name, value) + else -> setValue(name, Value.of(value)) } } +operator fun > M.set(key: String, value: Any?) = set(key.toName(), value) + /** * Update existing mutable node with another node. The rules are following: * * value replaces anything @@ -126,9 +130,9 @@ fun > M.update(meta: Meta) { meta.items.forEach { entry -> val value = entry.value when (value) { - is MetaItem.ValueItem -> this[entry.key.toName()] = value.value + is MetaItem.ValueItem -> setValue(entry.key.toName(),value.value) is MetaItem.NodeItem -> (this[entry.key.toName()] as? MetaItem.NodeItem)?.node?.update(value.node) - ?: run { this[entry.key.toName()] = value.node } + ?: run { setNode(entry.key.toName(),value.node)} } } } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styleable.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styleable.kt index 915ddc89..5b4af6d2 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styleable.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styleable.kt @@ -27,8 +27,8 @@ class StyledConfig(val config: Config, style: Meta = EmptyMeta) : Config() { override fun set(name: Name, item: MetaItem?) { when (item) { null -> config.remove(name) - is MetaItem.ValueItem -> config[name] = item.value - is MetaItem.NodeItem -> config[name] = item.node + is MetaItem.ValueItem -> config.setValue(name, item.value) + is MetaItem.NodeItem -> config.setNode(name, item.node) } } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt index 1eae4182..57395b1b 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt @@ -6,7 +6,7 @@ package hep.dataforge.names * The name is a dot separated list of strings like `token1.token2.token3`. * Each token could contain additional query in square brackets. */ -class Name internal constructor(val tokens: List) { +inline class Name constructor(val tokens: List) { val length get() = tokens.size @@ -35,19 +35,6 @@ class Name internal constructor(val tokens: List) { override fun toString(): String = tokens.joinToString(separator = NAME_SEPARATOR) { it.toString() } - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is Name) return false - - if (tokens != other.tokens) return false - - return true - } - - override fun hashCode(): Int { - return tokens.hashCode() - } - companion object { const val NAME_SEPARATOR = "." } @@ -80,7 +67,7 @@ fun String.toName(): Name { var bracketCount: Int = 0 fun queryOn() = bracketCount > 0 - this@toName.asSequence().forEach { + asSequence().forEach { if (queryOn()) { when (it) { '[' -> bracketCount++ diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Value.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt similarity index 80% rename from dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Value.kt rename to dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt index 53c5a8f3..53cc1996 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Value.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt @@ -1,4 +1,4 @@ -package hep.dataforge.meta +package hep.dataforge.values /** @@ -44,19 +44,22 @@ interface Value { get() = listOf(this) companion object { + const val VALUE_TARGET = "value" + /** * Convert object to value */ fun of(value: Any?): Value { return when (value) { null -> Null + is Value -> value true -> True false -> False is Number -> NumberValue(value) is Iterable<*> -> ListValue(value.map { of(it) }) is Enum<*> -> EnumValue(value) is CharSequence -> StringValue(value.toString()) - else -> throw IllegalArgumentException("Unrecognized type of the object converted to Value") + else -> throw IllegalArgumentException("Unrecognized type of the object (${value::class}) converted to Value") } } } @@ -70,6 +73,8 @@ object Null : Value { override val type: ValueType get() = ValueType.NULL override val number: Number get() = Double.NaN override val string: String get() = "@null" + + override fun toString(): String = value.toString() } /** @@ -86,6 +91,8 @@ object True : Value { override val type: ValueType get() = ValueType.BOOLEAN override val number: Number get() = 1.0 override val string: String get() = "+" + + override fun toString(): String = value.toString() } /** @@ -106,12 +113,21 @@ class NumberValue(override val number: Number) : Value { override val string: String get() = number.toString() override fun equals(other: Any?): Boolean { - return this.number == (other as? Value)?.number + if (other !is Value) return false + return when (number) { + is Short -> number == other.number.toShort() + is Long -> number == other.number.toLong() + is Byte -> number == other.number.toByte() + is Int -> number == other.number.toInt() + is Float -> number == other.number.toFloat() + is Double -> number == other.number.toDouble() + else -> number.toString() == other.number.toString() + } } override fun hashCode(): Int = number.hashCode() - + override fun toString(): String = value.toString() } class StringValue(override val string: String) : Value { @@ -123,7 +139,9 @@ class StringValue(override val string: String) : Value { return this.string == (other as? Value)?.string } - override fun hashCode(): Int = string.hashCode() + override fun hashCode(): Int = string.hashCode() + + override fun toString(): String = value.toString() } class EnumValue>(override val value: E) : Value { @@ -136,6 +154,8 @@ class EnumValue>(override val value: E) : Value { } override fun hashCode(): Int = value.hashCode() + + override fun toString(): String = value.toString() } class ListValue(override val list: List) : Value { @@ -149,6 +169,8 @@ class ListValue(override val list: List) : Value { override val type: ValueType get() = list.first().type override val number: Number get() = list.first().number override val string: String get() = list.first().string + + override fun toString(): String = value.toString() } /** @@ -201,8 +223,8 @@ fun String.parseValue(): Value { return StringValue(this) } -class LazyParsedValue(override val string: String): Value{ - private val parsedValue by lazy { string.parseValue() } +class LazyParsedValue(override val string: String) : Value { + private val parsedValue by lazy { string.parseValue() } override val value: Any? get() = parsedValue.value @@ -210,4 +232,6 @@ class LazyParsedValue(override val string: String): Value{ get() = parsedValue.type override val number: Number get() = parsedValue.number + + override fun toString(): String = value.toString() } \ No newline at end of file diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaBuilderTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaBuilderTest.kt index 26e10354..8cda9003 100644 --- a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaBuilderTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaBuilderTest.kt @@ -1,5 +1,6 @@ package hep.dataforge.meta +import hep.dataforge.values.asValue import kotlin.test.Test import kotlin.test.assertEquals diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaTest.kt new file mode 100644 index 00000000..7f83b0b2 --- /dev/null +++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaTest.kt @@ -0,0 +1,33 @@ +package hep.dataforge.meta + +import hep.dataforge.values.NumberValue +import hep.dataforge.values.True +import hep.dataforge.values.Value +import kotlin.test.Test +import kotlin.test.assertEquals + +class MetaTest { + @Test + fun valueEqualityTest() { + assertEquals(NumberValue(22), NumberValue(22)) + assertEquals(NumberValue(22.0), NumberValue(22)) + assertEquals(True, Value.of(true)) + } + + @Test + fun metaEqualityTest() { + val meta1 = buildMeta { + "a" to 22 + "b" to { + "c" to "ddd" + } + } + val meta2 = buildMeta { + "b" to { + "c" to "ddd" + } + "a" to 22 + }.seal() + assertEquals(meta1, meta2) + } +} \ No newline at end of file diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/names/NameTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/names/NameTest.kt index 764d23c2..52fb478d 100644 --- a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/names/NameTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/names/NameTest.kt @@ -9,4 +9,11 @@ class NameTest{ val name = "token1.token2.token3".toName() assertEquals("token2", name[1].toString()) } + + @Test + fun equalityTest(){ + val name1 = "token1.token2[2].token3".toName() + val name2 = "token1".toName() + "token2[2].token3" + assertEquals(name1,name2) + } } \ No newline at end of file diff --git a/dataforge-tables/build.gradle b/dataforge-tables/build.gradle new file mode 100644 index 00000000..512fff84 --- /dev/null +++ b/dataforge-tables/build.gradle @@ -0,0 +1,25 @@ +plugins { + id 'kotlin-multiplatform' +} + +repositories { + jcenter() +} + +kotlin { + targets { + fromPreset(presets.jvm, 'jvm') + //fromPreset(presets.js, 'js') + // For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64 + // For Linux, preset should be changed to e.g. presets.linuxX64 + // For MacOS, preset should be changed to e.g. presets.macosX64 + //fromPreset(presets.iosX64, 'ios') + } + sourceSets { + commonMain { + dependencies { + api project(":dataforge-context") + } + } + } +} \ No newline at end of file diff --git a/dataforge-workspace/build.gradle b/dataforge-workspace/build.gradle new file mode 100644 index 00000000..512fff84 --- /dev/null +++ b/dataforge-workspace/build.gradle @@ -0,0 +1,25 @@ +plugins { + id 'kotlin-multiplatform' +} + +repositories { + jcenter() +} + +kotlin { + targets { + fromPreset(presets.jvm, 'jvm') + //fromPreset(presets.js, 'js') + // For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64 + // For Linux, preset should be changed to e.g. presets.linuxX64 + // For MacOS, preset should be changed to e.g. presets.macosX64 + //fromPreset(presets.iosX64, 'ios') + } + sourceSets { + commonMain { + dependencies { + api project(":dataforge-context") + } + } + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 71f46856..22a42916 100644 --- a/settings.gradle +++ b/settings.gradle @@ -27,4 +27,12 @@ rootProject.name = 'dataforge-core' include ":dataforge-meta" include ":dataforge-meta-io" + +include ":dataforge-context" +include ":dataforge-data" +include ":dataforge-io" + +include ":dataforge-tables" + +include ":dataforge-workspace" //include ":dataforge-envelope" \ No newline at end of file From 3d65a1b4090f7fd4ff9052765742f656391ec17f Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 8 Nov 2018 20:59:41 +0300 Subject: [PATCH 10/37] Switched to kotlinx serialization for json processing --- .../kotlin/hep/dataforge/context/Global.kt | 3 + .../hep/dataforge/context/JVMContext.kt | 1 + dataforge-meta-io/build.gradle | 9 +- .../hep/dataforge/meta/io/JsonMetaFormat.kt | 77 +++++++++++ .../hep/dataforge/meta/io/MetaFormat.kt | 16 +-- .../hep/dataforge/meta/io/MetaFormatTest.kt | 4 +- .../kotlin/hep/dataforge/meta/io/JSMeta.kt | 31 ----- .../hep/dataforge/meta/io/JSONMetaFormat.kt | 17 --- .../kotlin/hep/dataforge/meta/JSMetaTest.kt | 18 --- .../hep/dataforge/meta/io/JSONMetaFormat.kt | 124 ------------------ 10 files changed, 89 insertions(+), 211 deletions(-) create mode 100644 dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/JsonMetaFormat.kt delete mode 100644 dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSMeta.kt delete mode 100644 dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt delete mode 100644 dataforge-meta-io/src/jsTest/kotlin/hep/dataforge/meta/JSMetaTest.kt delete mode 100644 dataforge-meta-io/src/jvmMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt diff --git a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/Global.kt b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/Global.kt index 7564b5fb..d560ddd2 100644 --- a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/Global.kt +++ b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/Global.kt @@ -15,6 +15,9 @@ */ package hep.dataforge.context +import hep.dataforge.meta.Meta +import hep.dataforge.meta.buildMeta +import hep.dataforge.names.toName import java.util.* import kotlin.collections.HashMap diff --git a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt index 448eb63e..942fafb8 100644 --- a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt +++ b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt @@ -15,6 +15,7 @@ */ package hep.dataforge.context +import hep.dataforge.meta.* import mu.KLogger import mu.KotlinLogging import java.lang.ref.WeakReference diff --git a/dataforge-meta-io/build.gradle b/dataforge-meta-io/build.gradle index 242532bb..1af12d27 100644 --- a/dataforge-meta-io/build.gradle +++ b/dataforge-meta-io/build.gradle @@ -1,6 +1,6 @@ plugins { id 'kotlin-multiplatform' - //id 'kotlinx-serialization' + id 'kotlinx-serialization' } repositories { @@ -21,7 +21,7 @@ kotlin { dependencies { api project(":dataforge-meta") //implementation 'org.jetbrains.kotlin:kotlin-reflect' - //implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version" + implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version" implementation "org.jetbrains.kotlinx:kotlinx-io:$kotlinx_io_version" } } @@ -33,8 +33,7 @@ kotlin { } jvmMain { dependencies { - implementation 'com.github.cliftonlabs:json-simple:3.0.2' - //implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version" + implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version" implementation "org.jetbrains.kotlinx:kotlinx-io-jvm:$kotlinx_io_version" } } @@ -46,7 +45,7 @@ kotlin { } jsMain { dependencies { - //implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version" + implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version" implementation "org.jetbrains.kotlinx:kotlinx-io-js:$kotlinx_io_version" } } diff --git a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/JsonMetaFormat.kt b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/JsonMetaFormat.kt new file mode 100644 index 00000000..ec588160 --- /dev/null +++ b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/JsonMetaFormat.kt @@ -0,0 +1,77 @@ +package hep.dataforge.meta.io + +import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaItem +import hep.dataforge.names.NameToken +import hep.dataforge.values.* +import kotlinx.io.core.Input +import kotlinx.io.core.Output +import kotlinx.io.core.readText +import kotlinx.io.core.writeText +import kotlinx.serialization.json.* + + +object JSONMetaFormat : MetaFormat { + override val name: String = "json" + override val key: Short = 0x4a53//"JS" + + override fun write(meta: Meta, out: Output) { + val str = meta.toJson().toString() + out.writeText(str) + } + + override fun read(input: Input): Meta { + val str = input.readText() + val json = JsonTreeParser.parse(str) + return json.toMeta() + } +} + +fun Value.toJson(): JsonElement { + return when (type) { + ValueType.NUMBER -> JsonPrimitive(number) + ValueType.STRING -> JsonPrimitive(string) + ValueType.BOOLEAN -> JsonPrimitive(boolean) + ValueType.NULL -> JsonNull + } +} + +fun Meta.toJson(): JsonObject { + val map = this.items.mapValues { entry -> + val value = entry.value + when (value) { + is MetaItem.ValueItem -> value.value.toJson() + is MetaItem.NodeItem -> value.node.toJson() + } + }.mapKeys { it.key.toString() } + return JsonObject(map) +} + +fun JsonObject.toMeta() = JsonMeta(this) + +private fun JsonPrimitive.toValue(): Value { + return when (this) { + JsonNull -> Null + else -> this.content.parseValue() // Optimize number and boolean parsing + } +} + +class JsonMeta(val json: JsonObject) : Meta { + override val items: Map> by lazy { + json.mapKeys { NameToken(it.key) }.mapValues { entry -> + val element = entry.value + when (element) { + is JsonPrimitive -> MetaItem.ValueItem(element.toValue()) + is JsonObject -> MetaItem.NodeItem(element.toMeta()) + is JsonArray -> { + if (element.all { it is JsonPrimitive }) { + val value = ListValue(element.map { (it as JsonPrimitive).toValue() }) + MetaItem.ValueItem(value) + } else { + TODO("mixed nodes json") + } + } + } + } + } +} \ No newline at end of file diff --git a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt index 4f78661b..ec9d9f2b 100644 --- a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt +++ b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt @@ -15,28 +15,16 @@ interface MetaFormat { fun read(input: Input): Meta } -fun MetaFormat.stringify(meta: Meta): String { +fun Meta.asString(format: MetaFormat = JSONMetaFormat): String{ val builder = BytePacketBuilder() - write(meta,builder) + format.write(this,builder) return builder.build().readText() } -fun Meta.asString() = JSONMetaFormat.stringify(this) - fun MetaFormat.parse(str: String): Meta{ return read(ByteReadPacket(str.toByteArray())) } -internal expect fun writeJson(meta: Meta, out: Output) -internal expect fun readJson(input: Input, length: Int = -1): Meta - -object JSONMetaFormat : MetaFormat { - override val name: String = "json" - override val key: Short = 0x4a53//"JS" - - override fun write(meta: Meta, out: Output) = writeJson(meta, out) - override fun read(input: Input): Meta = readJson(input) -} object BinaryMetaFormat : MetaFormat { override val name: String = "bin" diff --git a/dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt b/dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt index 72851d90..a077f920 100644 --- a/dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt +++ b/dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt @@ -14,7 +14,7 @@ class MetaFormatTest{ "c" to 11.1 } } - val string = BinaryMetaFormat.stringify(meta) + val string = meta.asString(BinaryMetaFormat) val result = BinaryMetaFormat.parse(string) assertEquals(meta,result) } @@ -28,7 +28,7 @@ class MetaFormatTest{ "c" to 11.1 } } - val string = JSONMetaFormat.stringify(meta) + val string = meta.asString(JSONMetaFormat) val result = JSONMetaFormat.parse(string) assertEquals(meta,result) } diff --git a/dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSMeta.kt b/dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSMeta.kt deleted file mode 100644 index e024c319..00000000 --- a/dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSMeta.kt +++ /dev/null @@ -1,31 +0,0 @@ -package hep.dataforge.meta.io - -import hep.dataforge.meta.Meta -import hep.dataforge.meta.MetaItem -import hep.dataforge.names.NameToken -import hep.dataforge.values.Value - -/** - * Represent any js object as meta - */ -class JSMeta(val obj: Any) : Meta { - override val items: Map> - get() = listKeys(obj).map { NameToken(it) }.associateWith { convert(js("obj[it]")) } - - private fun listKeys(obj: Any): List = js("Object").keys(obj) as List - - private fun isList(obj: Any): Boolean = js("Array").isArray(obj) as Boolean - - private fun isPrimitive(@Suppress("UNUSED_PARAMETER") obj: Any?): Boolean = js("obj !== Object(obj)") as Boolean - - private fun convert(obj: Any?): MetaItem { - return when (obj) { - null, isPrimitive(obj), is Number, is String, is Boolean -> MetaItem.ValueItem(Value.of(obj)) - isList(obj) -> { - val list = obj as List<*> - MetaItem.ValueItem(Value.of(list)) - } - else -> MetaItem.NodeItem(JSMeta(obj)) - } - } -} \ No newline at end of file diff --git a/dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt b/dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt deleted file mode 100644 index 60b1446e..00000000 --- a/dataforge-meta-io/src/jsMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt +++ /dev/null @@ -1,17 +0,0 @@ -package hep.dataforge.meta.io - -import hep.dataforge.meta.Meta -import kotlinx.io.core.Input -import kotlinx.io.core.Output -import kotlinx.io.core.readText -import kotlinx.io.core.writeText -import kotlin.js.Json - -internal actual fun writeJson(meta: Meta, out: Output) { - out.writeText(JSON.stringify(meta)) -} - -internal actual fun readJson(input: Input, length: Int): Meta { - val json: Json = JSON.parse(input.readText(max = if (length > 0) length else Int.MAX_VALUE)) - return JSMeta(json) -} \ No newline at end of file diff --git a/dataforge-meta-io/src/jsTest/kotlin/hep/dataforge/meta/JSMetaTest.kt b/dataforge-meta-io/src/jsTest/kotlin/hep/dataforge/meta/JSMetaTest.kt deleted file mode 100644 index e925b92c..00000000 --- a/dataforge-meta-io/src/jsTest/kotlin/hep/dataforge/meta/JSMetaTest.kt +++ /dev/null @@ -1,18 +0,0 @@ -package hep.dataforge.meta - -import hep.dataforge.meta.io.JSMeta -import kotlin.js.json -import kotlin.test.Test -import kotlin.test.assertEquals - -class JSMetaTest{ - @Test - fun testConverstion(){ - val test = json( - "a" to 2, - "b" to "ddd" - ) - val meta = JSMeta(test) - assertEquals(2, meta["a"]!!.int) - } -} \ No newline at end of file diff --git a/dataforge-meta-io/src/jvmMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt b/dataforge-meta-io/src/jvmMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt deleted file mode 100644 index ae597b53..00000000 --- a/dataforge-meta-io/src/jvmMain/kotlin/hep/dataforge/meta/io/JSONMetaFormat.kt +++ /dev/null @@ -1,124 +0,0 @@ -package hep.dataforge.meta.io - -import com.github.cliftonlabs.json_simple.JsonArray -import com.github.cliftonlabs.json_simple.JsonObject -import com.github.cliftonlabs.json_simple.Jsoner -import hep.dataforge.meta.* -import hep.dataforge.names.toName -import hep.dataforge.values.* -import kotlinx.io.core.* -import java.io.ByteArrayInputStream -import java.io.InputStreamReader -import java.io.Reader -import java.nio.ByteBuffer -import java.text.ParseException - -internal actual fun writeJson(meta: Meta, out: Output) { - val json = meta.toJson() - val string = Jsoner.prettyPrint(Jsoner.serialize(json)) - out.writeText(string) -} - -private fun Value.toJson(): Any { - return if (list.size == 1) { - when (type) { - ValueType.NUMBER -> number - ValueType.BOOLEAN -> boolean - else -> string - } - } else { - JsonArray().apply { - list.forEach { add(it.toJson()) } - } - } -} - -fun Meta.toJson(): JsonObject { - val builder = JsonObject() - items.forEach { name, item -> - when (item) { - is MetaItem.ValueItem -> builder[name.toString()] = item.value.toJson() - is MetaItem.NodeItem -> builder[name.toString()] = item.node.toJson() - } - } - return builder -} - - -internal actual fun readJson(input: Input, length: Int): Meta { - return if (length == 0) { - EmptyMeta - } else { - val json = if (length > 0) { - //Read into intermediate buffer - val buffer = ByteArray(length) - input.readAvailable(buffer, length) - Jsoner.deserialize(InputStreamReader(ByteArrayInputStream(buffer), Charsets.UTF_8)) as JsonObject - } else { - //automatic - val reader = object : Reader() { - override fun close() { - input.close() - } - - override fun read(cbuf: CharArray, off: Int, len: Int): Int { - val buffer = ByteBuffer.allocate(len) - val res = input.readAvailable(buffer) - val chars = String(buffer.array()).toCharArray() - System.arraycopy(chars, 0, cbuf, off, chars.size) - return res - } - - } - Jsoner.deserialize(reader) as JsonObject - } - json.toMeta() - } -} - -@Throws(ParseException::class) -private fun JsonObject.toMeta(): Meta { - return buildMeta { - this@toMeta.forEach { key, value -> appendValue(key as String, value) } - } -} - -private fun JsonArray.toListValue(): Value { - val list: List = this.map { value -> - when (value) { - null -> Null - is JsonArray -> value.toListValue() - is Number -> NumberValue(value) - is Boolean -> if (value) True else False - is String -> LazyParsedValue(value) - is JsonObject -> error("Object values inside multidimensional arrays are not allowed") - else -> error("Unknown token $value in json") - } - } - return Value.of(list) -} - -private fun MetaBuilder.appendValue(key: String, value: Any?) { - when (value) { - is JsonObject -> this[key] = value.toMeta() - is JsonArray -> { - if (value.none { it is JsonObject }) { - //If all values are primitives or arrays - this[key] = value.toListValue() - } else { - val list = value.map { - when (it) { - is JsonObject -> it.toMeta() - is JsonArray -> it.toListValue().toMeta() - else -> Value.of(it).toMeta() - } - } - setIndexed(key.toName(), list) - } - } - is Number -> this[key] = NumberValue(value) - is Boolean -> this[key] = value - is String -> this[key] = LazyParsedValue(value) - //ignore anything else - } -} From 013a1a935c7a6c542971ad987c879d95bf9693ce Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 5 Dec 2018 09:55:30 +0300 Subject: [PATCH 11/37] MetaFormat service loader --- build.gradle | 6 +- dataforge-context/build.gradle | 2 +- dataforge-data/build.gradle | 2 +- dataforge-io/build.gradle | 2 +- dataforge-meta-io/build.gradle | 4 +- .../hep/dataforge/meta/io/BinaryMetaFormat.kt | 132 +++++++++++++++++ .../hep/dataforge/meta/io/JsonMetaFormat.kt | 11 +- .../hep/dataforge/meta/io/MetaFormat.kt | 138 ++---------------- .../hep/dataforge/meta/io/MetaFormatTest.kt | 4 +- .../hep.dataforge.meta.io.MetaFormatFactory | 2 + dataforge-meta/build.gradle | 2 +- dataforge-tables/build.gradle | 2 +- dataforge-workspace/build.gradle | 2 +- settings.gradle | 3 +- 14 files changed, 170 insertions(+), 142 deletions(-) create mode 100644 dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/BinaryMetaFormat.kt create mode 100644 dataforge-meta-io/src/jvmMain/resources/META-INF/services/hep.dataforge.meta.io.MetaFormatFactory diff --git a/build.gradle b/build.gradle index 38a7fe77..7b38794e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ buildscript { - ext.kotlin_version = '1.3.0' - ext.serialization_version = '0.9.0' - ext.kotlinx_io_version = '0.1.0-beta-1' + ext.kotlin_version = '1.3.10' + ext.serialization_version = '0.9.1' + ext.kotlinx_io_version = '0.1.1' repositories { jcenter() diff --git a/dataforge-context/build.gradle b/dataforge-context/build.gradle index c7228860..fed0f11f 100644 --- a/dataforge-context/build.gradle +++ b/dataforge-context/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'kotlin-multiplatform' + id "org.jetbrains.kotlin.multiplatform" } repositories { diff --git a/dataforge-data/build.gradle b/dataforge-data/build.gradle index 512fff84..a0011494 100644 --- a/dataforge-data/build.gradle +++ b/dataforge-data/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'kotlin-multiplatform' + id "org.jetbrains.kotlin.multiplatform" } repositories { diff --git a/dataforge-io/build.gradle b/dataforge-io/build.gradle index 512fff84..a0011494 100644 --- a/dataforge-io/build.gradle +++ b/dataforge-io/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'kotlin-multiplatform' + id "org.jetbrains.kotlin.multiplatform" } repositories { diff --git a/dataforge-meta-io/build.gradle b/dataforge-meta-io/build.gradle index 1af12d27..353f93e9 100644 --- a/dataforge-meta-io/build.gradle +++ b/dataforge-meta-io/build.gradle @@ -1,8 +1,10 @@ plugins { id 'kotlin-multiplatform' - id 'kotlinx-serialization' + id "org.jetbrains.kotlin.multiplatform" } +description = "IO for meta" + repositories { jcenter() } diff --git a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/BinaryMetaFormat.kt b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/BinaryMetaFormat.kt new file mode 100644 index 00000000..c2148ba4 --- /dev/null +++ b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/BinaryMetaFormat.kt @@ -0,0 +1,132 @@ +package hep.dataforge.meta.io + +import hep.dataforge.meta.* +import hep.dataforge.values.* +import kotlinx.io.core.Input +import kotlinx.io.core.Output +import kotlinx.io.core.readText +import kotlinx.io.core.writeText + +object BinaryMetaFormat : MetaFormat { + override fun write(meta: Meta, out: Output) { + out.writeMeta(meta) + } + + override fun read(input: Input): Meta { + return (input.readMetaItem() as MetaItem.NodeItem).node + } + + private fun Output.writeChar(char: Char) = writeByte(char.toByte()) + + private fun Output.writeString(str: String) { + writeInt(str.length) + writeText(str) + } + + private fun Output.writeValue(value: Value) { + if (value.isList()) { + writeChar('L') + writeInt(value.list.size) + value.list.forEach { + writeValue(it) + } + } else when (value.type) { + ValueType.NUMBER -> when (value.value) { + is Short -> { + writeChar('s') + writeShort(value.number.toShort()) + } + is Int -> { + writeChar('i') + writeInt(value.number.toInt()) + } + is Long -> { + writeChar('l') + writeLong(value.number.toLong()) + } + is Float -> { + writeChar('f') + writeFloat(value.number.toFloat()) + } + else -> { + writeChar('d') + writeDouble(value.number.toDouble()) + } + } + ValueType.STRING -> { + writeChar('S') + writeString(value.string) + } + ValueType.BOOLEAN -> { + if (value.boolean) { + writeChar('+') + } else { + writeChar('-') + } + } + ValueType.NULL -> { + writeChar('N') + } + } + } + + private fun Output.writeMeta(meta: Meta) { + writeChar('M') + writeInt(meta.items.size) + meta.items.forEach { (key, item) -> + writeString(key.toString()) + when (item) { + is MetaItem.ValueItem -> { + writeValue(item.value) + } + is MetaItem.NodeItem -> { + writeMeta(item.node) + } + } + } + } + + private fun Input.readString(): String { + val length = readInt() + return readText(max = length) + } + + private fun Input.readMetaItem(): MetaItem { + val keyChar = readByte().toChar() + return when (keyChar) { + 'S' -> MetaItem.ValueItem(StringValue(readString())) + 'N' -> MetaItem.ValueItem(Null) + '+' -> MetaItem.ValueItem(True) + '-' -> MetaItem.ValueItem(True) + 's' -> MetaItem.ValueItem(NumberValue(readShort())) + 'i' -> MetaItem.ValueItem(NumberValue(readInt())) + 'l' -> MetaItem.ValueItem(NumberValue(readInt())) + 'f' -> MetaItem.ValueItem(NumberValue(readFloat())) + 'd' -> MetaItem.ValueItem(NumberValue(readDouble())) + 'L' -> { + val length = readInt() + val list = (1..length).map { (readMetaItem() as MetaItem.ValueItem).value } + MetaItem.ValueItem(Value.of(list)) + } + 'M' -> { + val length = readInt() + val meta = buildMeta { + (1..length).forEach { _ -> + val name = readString() + val item = readMetaItem() + setItem(name, item) + } + } + MetaItem.NodeItem(meta) + } + else -> error("Unknown serialization key character: $keyChar") + } + } +} + +class BinaryMetaFormatFactory: MetaFormatFactory{ + override val name: String = "bin" + override val key: Short = 0x4249//BI + + override fun build(): MetaFormat = BinaryMetaFormat +} \ No newline at end of file diff --git a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/JsonMetaFormat.kt b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/JsonMetaFormat.kt index ec588160..cde54c25 100644 --- a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/JsonMetaFormat.kt +++ b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/JsonMetaFormat.kt @@ -11,9 +11,7 @@ import kotlinx.io.core.writeText import kotlinx.serialization.json.* -object JSONMetaFormat : MetaFormat { - override val name: String = "json" - override val key: Short = 0x4a53//"JS" +object JsonMetaFormat : MetaFormat { override fun write(meta: Meta, out: Output) { val str = meta.toJson().toString() @@ -74,4 +72,11 @@ class JsonMeta(val json: JsonObject) : Meta { } } } +} + +class JsonMetaFormatFactory: MetaFormatFactory{ + override val name: String = "json" + override val key: Short = 0x4a53//"JS" + + override fun build() = JsonMetaFormat } \ No newline at end of file diff --git a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt index ec9d9f2b..2f619adf 100644 --- a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt +++ b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt @@ -1,21 +1,27 @@ package hep.dataforge.meta.io -import hep.dataforge.meta.* -import hep.dataforge.values.* +import hep.dataforge.meta.Meta import kotlinx.io.core.* /** * A format for meta serialization */ interface MetaFormat { - val name: String - val key: Short - fun write(meta: Meta, out: Output) fun read(input: Input): Meta } -fun Meta.asString(format: MetaFormat = JSONMetaFormat): String{ +/** + * ServiceLoader compatible factory + */ +interface MetaFormatFactory { + val name: String + val key: Short + + fun build(): MetaFormat +} + +fun Meta.asString(format: MetaFormat = JsonMetaFormat): String{ val builder = BytePacketBuilder() format.write(this,builder) return builder.build().readText() @@ -26,123 +32,3 @@ fun MetaFormat.parse(str: String): Meta{ } -object BinaryMetaFormat : MetaFormat { - override val name: String = "bin" - override val key: Short = 0x4249//BI - - override fun write(meta: Meta, out: Output) { - out.writeMeta(meta) - } - - override fun read(input: Input): Meta { - return (input.readMetaItem() as MetaItem.NodeItem).node - } - - private fun Output.writeChar(char: Char) = writeByte(char.toByte()) - - private fun Output.writeString(str: String) { - writeInt(str.length) - writeText(str) - } - - private fun Output.writeValue(value: Value) { - if (value.isList()) { - writeChar('L') - writeInt(value.list.size) - value.list.forEach { - writeValue(it) - } - } else when (value.type) { - ValueType.NUMBER -> when (value.value) { - is Short -> { - writeChar('s') - writeShort(value.number.toShort()) - } - is Int -> { - writeChar('i') - writeInt(value.number.toInt()) - } - is Long -> { - writeChar('l') - writeLong(value.number.toLong()) - } - is Float -> { - writeChar('f') - writeFloat(value.number.toFloat()) - } - else -> { - writeChar('d') - writeDouble(value.number.toDouble()) - } - } - ValueType.STRING -> { - writeChar('S') - writeString(value.string) - } - ValueType.BOOLEAN -> { - if (value.boolean) { - writeChar('+') - } else { - writeChar('-') - } - } - ValueType.NULL -> { - writeChar('N') - } - } - } - - private fun Output.writeMeta(meta: Meta) { - writeChar('M') - writeInt(meta.items.size) - meta.items.forEach { (key, item) -> - writeString(key.toString()) - when (item) { - is MetaItem.ValueItem -> { - writeValue(item.value) - } - is MetaItem.NodeItem -> { - writeMeta(item.node) - } - } - } - } - - private fun Input.readString(): String { - val length = readInt() - return readText(max = length) - } - - private fun Input.readMetaItem(): MetaItem { - val keyChar = readByte().toChar() - return when (keyChar) { - 'S' -> MetaItem.ValueItem(StringValue(readString())) - 'N' -> MetaItem.ValueItem(Null) - '+' -> MetaItem.ValueItem(True) - '-' -> MetaItem.ValueItem(True) - 's' -> MetaItem.ValueItem(NumberValue(readShort())) - 'i' -> MetaItem.ValueItem(NumberValue(readInt())) - 'l' -> MetaItem.ValueItem(NumberValue(readInt())) - 'f' -> MetaItem.ValueItem(NumberValue(readFloat())) - 'd' -> MetaItem.ValueItem(NumberValue(readDouble())) - 'L' -> { - val length = readInt() - val list = (1..length).map { (readMetaItem() as MetaItem.ValueItem).value } - MetaItem.ValueItem(Value.of(list)) - } - 'M' -> { - val length = readInt() - val meta = buildMeta { - (1..length).forEach { _ -> - val name = readString() - val item = readMetaItem() - setItem(name, item) - } - } - MetaItem.NodeItem(meta) - } - else -> error("Unknown serialization key character: $keyChar") - } - } -} - diff --git a/dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt b/dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt index a077f920..c347c4e5 100644 --- a/dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt +++ b/dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt @@ -28,8 +28,8 @@ class MetaFormatTest{ "c" to 11.1 } } - val string = meta.asString(JSONMetaFormat) - val result = JSONMetaFormat.parse(string) + val string = meta.asString(JsonMetaFormat) + val result = JsonMetaFormat.parse(string) assertEquals(meta,result) } diff --git a/dataforge-meta-io/src/jvmMain/resources/META-INF/services/hep.dataforge.meta.io.MetaFormatFactory b/dataforge-meta-io/src/jvmMain/resources/META-INF/services/hep.dataforge.meta.io.MetaFormatFactory new file mode 100644 index 00000000..23bcfcd2 --- /dev/null +++ b/dataforge-meta-io/src/jvmMain/resources/META-INF/services/hep.dataforge.meta.io.MetaFormatFactory @@ -0,0 +1,2 @@ +hep.dataforge.meta.io.BinaryMetaFormatFactory +hep.dataforge.meta.io.JsonMetaFormatFactory \ No newline at end of file diff --git a/dataforge-meta/build.gradle b/dataforge-meta/build.gradle index 8d57006e..4dc3ebf4 100644 --- a/dataforge-meta/build.gradle +++ b/dataforge-meta/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'kotlin-multiplatform' + id "org.jetbrains.kotlin.multiplatform" } repositories { diff --git a/dataforge-tables/build.gradle b/dataforge-tables/build.gradle index 512fff84..a0011494 100644 --- a/dataforge-tables/build.gradle +++ b/dataforge-tables/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'kotlin-multiplatform' + id "org.jetbrains.kotlin.multiplatform" } repositories { diff --git a/dataforge-workspace/build.gradle b/dataforge-workspace/build.gradle index 512fff84..a0011494 100644 --- a/dataforge-workspace/build.gradle +++ b/dataforge-workspace/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'kotlin-multiplatform' + id "org.jetbrains.kotlin.multiplatform" } repositories { diff --git a/settings.gradle b/settings.gradle index 22a42916..63658cc3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -10,7 +10,7 @@ pluginManagement { if (requested.id.id == "kotlin-platform-js") { useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") } - if (requested.id.id == "kotlin-multiplatform") { + if (requested.id.id == "org.jetbrains.kotlin.multiplatform") { useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") } } @@ -22,6 +22,7 @@ pluginManagement { } } +enableFeaturePreview('GRADLE_METADATA') rootProject.name = 'dataforge-core' From 5eb1ffb38540c4a3fa580dc4acf27e2d419ff016 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 6 Dec 2018 13:49:20 +0300 Subject: [PATCH 12/37] Minor fixes in build --- build.gradle | 2 +- dataforge-meta-io/build.gradle | 1 - settings.gradle | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 7b38794e..8c698eaf 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ buildscript { ext.kotlin_version = '1.3.10' ext.serialization_version = '0.9.1' - ext.kotlinx_io_version = '0.1.1' + ext.kotlinx_io_version = '0.1.0' repositories { jcenter() diff --git a/dataforge-meta-io/build.gradle b/dataforge-meta-io/build.gradle index 353f93e9..6a2bf3d6 100644 --- a/dataforge-meta-io/build.gradle +++ b/dataforge-meta-io/build.gradle @@ -1,5 +1,4 @@ plugins { - id 'kotlin-multiplatform' id "org.jetbrains.kotlin.multiplatform" } diff --git a/settings.gradle b/settings.gradle index 63658cc3..e75c3158 100644 --- a/settings.gradle +++ b/settings.gradle @@ -22,7 +22,7 @@ pluginManagement { } } -enableFeaturePreview('GRADLE_METADATA') +//enableFeaturePreview('GRADLE_METADATA') rootProject.name = 'dataforge-core' From 139a78beca1907853f92ba1d104437c2b9175240 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 11 Dec 2018 18:49:08 +0300 Subject: [PATCH 13/37] Build update --- build.gradle | 32 -------------- build.gradle.kts | 43 +++++++++++++++++++ dataforge-meta-io/build.gradle | 16 +++---- .../hep/dataforge/meta/io/MetaFormatTest.kt | 2 +- dataforge-meta/build.gradle | 4 -- settings.gradle | 39 ----------------- settings.gradle.kts | 15 +++++++ 7 files changed, 65 insertions(+), 86 deletions(-) delete mode 100644 build.gradle create mode 100644 build.gradle.kts delete mode 100644 settings.gradle create mode 100644 settings.gradle.kts diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 8c698eaf..00000000 --- a/build.gradle +++ /dev/null @@ -1,32 +0,0 @@ -buildscript { - ext.kotlin_version = '1.3.10' - ext.serialization_version = '0.9.1' - ext.kotlinx_io_version = '0.1.0' - - repositories { - jcenter() - } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4+" - } -} - -allprojects { - apply plugin: 'maven-publish' - apply plugin: "com.jfrog.artifactory" - - repositories { - jcenter() - maven { url = "https://kotlin.bintray.com/kotlinx" } - } - - group = 'hep.dataforge' - version = '0.1.1-SNAPSHOT' -} - -if(file('aritfactory.gradle').exists()) { - apply from: 'aritfactory.gradle' -} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 00000000..692660e9 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,43 @@ +buildscript { + extra["kotlinVersion"] = "1.3.11" + extra["ioVersion"] = "0.1.2-dev-2" + extra["serializationVersion"] = "0.9.1" + extra["coroutinesVersion"] = "1.0.1" + + val kotlinVersion: String by extra + val ioVersion: String by extra + val coroutinesVersion: String by extra + val serializationVersion: String by extra + + repositories { + jcenter() + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") + classpath("org.jetbrains.kotlin:kotlin-serialization:$kotlinVersion") + classpath("org.jfrog.buildinfo:build-info-extractor-gradle:4+") + } +} + +plugins { + id("com.jfrog.artifactory") version "4.8.1" apply false +// id("org.jetbrains.kotlin.multiplatform") apply false +} + +allprojects { + apply(plugin = "maven-publish") + apply(plugin = "com.jfrog.artifactory") + + repositories { + jcenter() + maven("https://kotlin.bintray.com/kotlinx") + } + + group = "hep.dataforge" + version = "0.1.1-dev-1" +} + +if (file("artifactory.gradle").exists()) { + apply(from = "artifactory.gradle") +} \ No newline at end of file diff --git a/dataforge-meta-io/build.gradle b/dataforge-meta-io/build.gradle index 6a2bf3d6..2b1b3ffa 100644 --- a/dataforge-meta-io/build.gradle +++ b/dataforge-meta-io/build.gradle @@ -4,10 +4,6 @@ plugins { description = "IO for meta" -repositories { - jcenter() -} - kotlin { targets { fromPreset(presets.jvm, 'jvm') @@ -22,8 +18,8 @@ kotlin { dependencies { api project(":dataforge-meta") //implementation 'org.jetbrains.kotlin:kotlin-reflect' - implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version" - implementation "org.jetbrains.kotlinx:kotlinx-io:$kotlinx_io_version" + api "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serializationVersion" + api "org.jetbrains.kotlinx:kotlinx-io:$ioVersion" } } commonTest { @@ -34,8 +30,8 @@ kotlin { } jvmMain { dependencies { - implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version" - implementation "org.jetbrains.kotlinx:kotlinx-io-jvm:$kotlinx_io_version" + api "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serializationVersion" + api "org.jetbrains.kotlinx:kotlinx-io-jvm:$ioVersion" } } jvmTest { @@ -46,8 +42,8 @@ kotlin { } jsMain { dependencies { - implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version" - implementation "org.jetbrains.kotlinx:kotlinx-io-js:$kotlinx_io_version" + api "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serializationVersion" + api "org.jetbrains.kotlinx:kotlinx-io-js:$ioVersion" } } jsTest { diff --git a/dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt b/dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt index c347c4e5..6655a541 100644 --- a/dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt +++ b/dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt @@ -16,7 +16,7 @@ class MetaFormatTest{ } val string = meta.asString(BinaryMetaFormat) val result = BinaryMetaFormat.parse(string) - assertEquals(meta,result) + assertEquals(meta, result) } @Test diff --git a/dataforge-meta/build.gradle b/dataforge-meta/build.gradle index 4dc3ebf4..994ee882 100644 --- a/dataforge-meta/build.gradle +++ b/dataforge-meta/build.gradle @@ -2,10 +2,6 @@ plugins { id "org.jetbrains.kotlin.multiplatform" } -repositories { - jcenter() -} - kotlin { targets { fromPreset(presets.jvm, 'jvm') diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index e75c3158..00000000 --- a/settings.gradle +++ /dev/null @@ -1,39 +0,0 @@ -pluginManagement { - resolutionStrategy { - eachPlugin { - if (requested.id.id == "kotlin-platform-common") { - useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") - } - if (requested.id.id == "kotlin-platform-jvm") { - useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") - } - if (requested.id.id == "kotlin-platform-js") { - useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") - } - if (requested.id.id == "org.jetbrains.kotlin.multiplatform") { - useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") - } - } - } - - repositories { - mavenCentral() - maven { url = 'https://plugins.gradle.org/m2/' } - } -} - -//enableFeaturePreview('GRADLE_METADATA') - -rootProject.name = 'dataforge-core' - -include ":dataforge-meta" -include ":dataforge-meta-io" - -include ":dataforge-context" -include ":dataforge-data" -include ":dataforge-io" - -include ":dataforge-tables" - -include ":dataforge-workspace" -//include ":dataforge-envelope" \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 00000000..cda5e4d9 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,15 @@ +pluginManagement { + repositories { + jcenter() + maven("https://plugins.gradle.org/m2/") + } +} + +//enableFeaturePreview("GRADLE_METADATA") + +rootProject.name = "dataforge-core" +include( + ":dataforge-meta", + ":dataforge-meta-io", + ":dataforge-context" +) From abf222434f1a29831ef4575833a3d2bf61fd8355 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 12 Dec 2018 14:13:12 +0300 Subject: [PATCH 14/37] Documentation adjustment for Context --- .../kotlin/hep/dataforge/context/Context.kt | 15 ++++++++++++++ .../hep/dataforge/context/PluginManager.kt | 20 +++++++++++++++++-- .../hep/dataforge/context/JVMContext.kt | 7 ------- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt index f88e7cf3..ee81df6c 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt @@ -10,6 +10,18 @@ import mu.KLogger import mu.KotlinLogging import kotlin.reflect.KClass +/** + * The local environment for anything being done in DataForge framework. Contexts are organized into tree structure with [Global] at the top. + * Context has [properties] - equivalent for system environment values, but grouped into a tree and inherited from parent context. + * + * The main function of the Context is to provide [PluginManager] which stores the loaded plugins and works as a dependency injection point. + * The normal behaviour of the [PluginManager] is to search for a plugin in parent context if it is not found in a current one. It is possible to have + * different plugins with the same interface in different contexts in the hierarchy. The usual behaviour is to use nearest one, but it could + * be overridden by plugin implementation. + * + * Since plugins could contain mutable state, context has two states: active and inactive. No changes are allowed to active context. + * @author Alexander Nozik + */ interface Context : Named, MetaRepr, Provider { val parent: Context? @@ -24,6 +36,9 @@ interface Context : Named, MetaRepr, Provider { */ val logger: KLogger + /** + * A [PluginManager] for current context + */ val plugins: PluginManager /** diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt index d50893e2..25acfc7a 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt @@ -19,6 +19,9 @@ class PluginManager(override val context: Context) : ContextAware, Iterable() + /** + * A [PluginManager] of parent context if it is present + */ private val parent: PluginManager? = context.parent?.plugins @@ -31,7 +34,9 @@ class PluginManager(override val context: Context) : ContextAware, Iterable Boolean): Plugin? = sequence(recursive).find(predicate) @@ -87,6 +92,9 @@ class PluginManager(override val context: Context) : ContextAware, Iterable load(type: KClass, meta: Meta = EmptyMeta): T { val loaded = get(type, false) @@ -142,4 +151,11 @@ class PluginManager(override val context: Context) : ContextAware, Iterable = plugins.iterator() + /** + * Get a plugin if it exists or load it with given meta if it is not. + */ + inline fun getOrLoad(noinline metaBuilder: MetaBuilder.() -> Unit = {}): T { + return get(true) ?: load(metaBuilder) + } + } \ No newline at end of file diff --git a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt index 942fafb8..b2a3c883 100644 --- a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt +++ b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt @@ -26,13 +26,6 @@ import kotlin.collections.HashSet import kotlin.reflect.KClass import kotlin.reflect.full.cast -/** - * The local environment for anything being done in DataForge framework. Contexts are organized into tree structure with [Global] at the top. - * Each context has a set of named [Value] properties which are taken from parent context in case they are not found in local context. - * Context implements [ValueProvider] interface and therefore could be uses as a value source for substitutions etc. - * Context contains [PluginManager] which could be used any number of configurable named plugins. - * @author Alexander Nozik - */ open class JVMContext( final override val name: String, final override val parent: JVMContext? = Global, From 08c208cc46705e816681973df13bc70a7eedb39c Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 16 Dec 2018 14:37:52 +0300 Subject: [PATCH 15/37] Goal and DataNode definition --- .gitignore | 4 +- dataforge-data/build.gradle | 17 +- .../kotlin/hep/dataforge/data/Data.kt | 187 ++++++++++++++++++ .../kotlin/hep/dataforge/data/Goal.kt | 99 ++++++++++ settings.gradle.kts | 3 +- 5 files changed, 305 insertions(+), 5 deletions(-) create mode 100644 dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt create mode 100644 dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt diff --git a/.gitignore b/.gitignore index 534e4d13..5fa56866 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,9 @@ .idea/ *.iws -out/ +*/out/** .gradle -**/build/ +*/build/** !gradle-wrapper.jar diff --git a/dataforge-data/build.gradle b/dataforge-data/build.gradle index a0011494..10205fb1 100644 --- a/dataforge-data/build.gradle +++ b/dataforge-data/build.gradle @@ -9,7 +9,7 @@ repositories { kotlin { targets { fromPreset(presets.jvm, 'jvm') - //fromPreset(presets.js, 'js') + fromPreset(presets.js, 'js') // For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64 // For Linux, preset should be changed to e.g. presets.linuxX64 // For MacOS, preset should be changed to e.g. presets.macosX64 @@ -18,7 +18,20 @@ kotlin { sourceSets { commonMain { dependencies { - api project(":dataforge-context") + api project(":dataforge-meta") + api "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$coroutinesVersion" + } + } + + jvmMain { + dependencies { + api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion" + } + } + + jsMain { + dependencies { + api "org.jetbrains.kotlinx:kotlinx-coroutines-core-js:$coroutinesVersion" } } } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt new file mode 100644 index 00000000..b4f95f05 --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt @@ -0,0 +1,187 @@ +package hep.dataforge.data + +import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaRepr +import hep.dataforge.names.Name +import hep.dataforge.names.NameToken +import hep.dataforge.names.plus +import hep.dataforge.names.toName +import kotlin.coroutines.CoroutineContext + +/** + * A data element characterized by its meta + */ +interface Data : MetaRepr { + val meta: Meta + val goal: Goal + + override fun toMeta(): Meta = meta + + companion object { + fun of(meta: Meta, goal: Goal): Data = DataImpl(meta, goal) + fun of(name: String, meta: Meta, goal: Goal): Data = NamedData(name, of(meta, goal)) + fun static(context: CoroutineContext, meta: Meta, value: T): Data = DataImpl(meta, Goal.static(context, value)) + } +} + +/** + * Generic Data implementation + */ +private class DataImpl(override val meta: Meta, override val goal: Goal) : Data + +class NamedData(val name: String, data: Data) : Data by data + +/** + * A tree-like data structure grouped into the node. All data inside the node must inherit its type + */ +interface DataNode { + /** + * Get the specific data if it exists + */ + operator fun get(name: Name): Data? + + /** + * Get a subnode with given name if it exists. + */ + fun getNode(name: Name): DataNode? + + /** + * Walk the tree upside down and provide all data nodes with full names + */ + fun asSequence(): Sequence>> + + operator fun iterator(): Iterator>> = asSequence().iterator() + + companion object { + fun build(block: DataTreeBuilder.() -> Unit) = DataTreeBuilder().apply(block).build() + } + +} + +internal sealed class DataTreeItem { + class Node(val tree: DataTree) : DataTreeItem() + class Value(val value: Data) : DataTreeItem() +} + +class DataTree internal constructor(private val items: Map>) : DataNode { + //TODO add node-level meta? + + override fun get(name: Name): Data? = when (name.length) { + 0 -> error("Empty name") + 1 -> (items[name.first()] as? DataTreeItem.Value)?.value + else -> getNode(name.first()!!.toName())?.get(name.cutFirst()) + } + + override fun getNode(name: Name): DataTree? = when (name.length) { + 0 -> this + 1 -> (items[name.first()] as? DataTreeItem.Node)?.tree + else -> getNode(name.first()!!.toName())?.getNode(name.cutFirst()) + } + + override fun asSequence(): Sequence>> { + return kotlin.sequences.sequence { + items.forEach { (head, tree) -> + when (tree) { + is DataTreeItem.Value -> yield(head.toName() to tree.value) + is DataTreeItem.Node -> { + val subSequence = tree.tree.asSequence().map { (name, data) -> (head.toName() + name) to data } + yieldAll(subSequence) + } + } + } + } + } +} + +private sealed class DataTreeBuilderItem { + class Node(val tree: DataTreeBuilder) : DataTreeBuilderItem() + class Value(val value: Data) : DataTreeBuilderItem() +} + +/** + * A builder for a DataTree. + */ +class DataTreeBuilder { + private val map = HashMap>() + + operator fun set(token: NameToken, node: DataTreeBuilder) { + if (map.containsKey(token)) error("Tree entry with name $token is not empty") + map[token] = DataTreeBuilderItem.Node(node) + } + + operator fun set(token: NameToken, data: Data) { + if (map.containsKey(token)) error("Tree entry with name $token is not empty") + map[token] = DataTreeBuilderItem.Value(data) + } + + private fun buildNode(token: NameToken): DataTreeBuilder { + return if (!map.containsKey(token)) { + DataTreeBuilder().also { map.put(token, DataTreeBuilderItem.Node(it)) } + } else { + (map[token] as? DataTreeBuilderItem.Node ?: error("The node with name $token is occupied by leaf")).tree + } + } + + private fun buildNode(name: Name): DataTreeBuilder { + return when (name.length) { + 0 -> this + 1 -> buildNode(name.first()!!) + else -> buildNode(name.first()!!).buildNode(name.cutFirst()) + } + } + + operator fun set(name: Name, data: Data) { + when (name.length) { + 0 -> error("Can't add data with empty name") + 1 -> set(name.first()!!, data) + 2 -> buildNode(name.cutLast())[name.last()!!] = data + } + } + + operator fun set(name: Name, node: DataTreeBuilder) { + when (name.length) { + 0 -> error("Can't add data with empty name") + 1 -> set(name.first()!!, node) + 2 -> buildNode(name.cutLast())[name.last()!!] = node + } + } + + operator fun set(name: Name, node: DataNode) = set(name, node.builder()) + + /** + * Append data to node + */ + infix fun String.to(data: Data) = set(toName(), data) + + /** + * Append node + */ + infix fun String.to(node: DataNode) = set(toName(), node) + + /** + * Build and append node + */ + infix fun String.to(block: DataTreeBuilder.() -> Unit) = set(toName(), DataTreeBuilder().apply(block)) + + fun build(): DataTree { + val resMap = map.mapValues { (_, value) -> + when (value) { + is DataTreeBuilderItem.Value -> DataTreeItem.Value(value.value) + is DataTreeBuilderItem.Node -> DataTreeItem.Node(value.tree.build()) + } + } + return DataTree(resMap) + } +} + +/** + * Generate a mutable builder from this node. Node content is not changed + */ +fun DataNode.builder(): DataTreeBuilder = DataTreeBuilder().apply { + asSequence().forEach { (name, data) -> this[name] = data } +} + +/** + * Start computation for all goals in data node + */ +fun DataNode<*>.startAll() = asSequence().forEach { (_,data)->data.goal.start() } \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt new file mode 100644 index 00000000..5aabbe37 --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt @@ -0,0 +1,99 @@ +package hep.dataforge.data + +import kotlinx.coroutines.* +import kotlin.coroutines.CoroutineContext + +/** + * A special deferred with explicit dependencies and some additional information like progress and unique id + */ +interface Goal : Deferred, CoroutineScope { + val dependencies: Collection> + + val status: String + + val totalWork: Double + val workDone: Double + + val progress: Double get() = workDone / totalWork + + companion object { + /** + * Create goal wrapping static value. This goal is always completed + */ + fun static(context: CoroutineContext, value: T): Goal = StaticGoalImpl(context, CompletableDeferred(value)) + } +} + +/** + * A monitor of goal state that could be accessed only form inside the goal + */ +class GoalMonitor { + var totalWork: Double = 1.0 + var workDone: Double = 0.0 + var status: String = "" + + /** + * Mark the goal as started + */ + fun start() { + + } + + /** + * Mark the goal as completed + */ + fun finish() { + workDone = totalWork + } +} + +private class GoalImpl( + override val dependencies: Collection>, + val monitor: GoalMonitor, + deferred: Deferred) : Goal, Deferred by deferred { + override val coroutineContext: CoroutineContext get() = this + override val totalWork: Double get() = dependencies.sumByDouble { totalWork } + monitor.totalWork + override val workDone: Double get() = dependencies.sumByDouble { workDone } + monitor.workDone + override val status: String get() = monitor.status +} + +private class StaticGoalImpl(val context: CoroutineContext, deferred: CompletableDeferred) : Goal, Deferred by deferred { + override val dependencies: Collection> get() = emptyList() + override val status: String get() = "" + override val totalWork: Double get() = 0.0 + override val workDone: Double get() = 0.0 + override val coroutineContext: CoroutineContext get() = context + +} + + +/** + * Create a new [Goal] with given [dependencies] and execution [block]. The block takes monitor as parameter. + * The goal block runs in a supervised scope, meaning that when it fails, it won't affect external scope. + */ +fun CoroutineScope.createGoal(dependencies: Collection>, block: suspend GoalMonitor.() -> R): Goal { + val monitor = GoalMonitor() + val deferred = async(start = CoroutineStart.LAZY) { + dependencies.forEach { it.start() } + monitor.start() + return@async supervisorScope { monitor.block() } + }.also { + monitor.finish() + } + + return GoalImpl(dependencies, monitor, deferred) +} + +/** + * Create a one-to-one goal based on existing goal + */ +fun Goal.pipe(block: suspend GoalMonitor.(T) -> R): Goal = createGoal(listOf(this)) { block(await()) } + +/** + * Create a joining goal. + * @param scope the scope for resulting goal. By default use first goal in list + */ +fun Collection>.join(scope: CoroutineScope = first(), block: suspend GoalMonitor.(Collection) -> R): Goal = + scope.createGoal(this) { + block(map { it.await() }) + } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index cda5e4d9..4a91be73 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -11,5 +11,6 @@ rootProject.name = "dataforge-core" include( ":dataforge-meta", ":dataforge-meta-io", - ":dataforge-context" + ":dataforge-context", + ":dataforge-data" ) From 81d7fbcffa76479ed1e919f41e1a9af11459c1c1 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 16 Dec 2018 19:37:11 +0300 Subject: [PATCH 16/37] Actions --- .../kotlin/hep/dataforge/data/Action.kt | 59 ++++++++++++++++++ .../kotlin/hep/dataforge/data/Data.kt | 61 +++++++++++++------ 2 files changed, 101 insertions(+), 19 deletions(-) create mode 100644 dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt new file mode 100644 index 00000000..e4b35e39 --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt @@ -0,0 +1,59 @@ +package hep.dataforge.data + +import hep.dataforge.meta.Meta +import hep.dataforge.names.Name + +/** + * A simple data transformation on a data node + */ +interface Action { + /** + * Transform the data in the node, producing a new node. By default it is assumed that all calculations are lazy + * so not actual computation is started at this moment + */ + operator fun invoke(node: DataNode, meta: Meta): DataNode + + /** + * Terminal action is the one that could not be invoked lazily and requires some kind of blocking computation to invoke + */ + val isTerminal: Boolean get() = false +} + +/** + * Action composition. The result is terminal if one of parts is terminal + */ +infix fun Action.then(action: Action): Action { + return object : Action { + override fun invoke(node: DataNode, meta: Meta): DataNode { + return action(this@then.invoke(node, meta), meta) + } + + override val isTerminal: Boolean + get() = this@then.isTerminal || action.isTerminal + } +} + +/** + * An action that performs the same transformation on each of input data nodes. Null results are ignored. + */ +class PipeAction(val transform: (Name, Data, Meta) -> Data?) : Action { + override fun invoke(node: DataNode, meta: Meta): DataNode = DataNode.build { + node.asSequence().forEach { (name, data) -> + val res = transform(name, data, meta) + if (res != null) { + set(name, res) + } + } + } + + companion object { + /** + * A simple pipe that performs transformation on the data and copies input meta into the output + */ + fun simple(transform: suspend (Name, T, Meta) -> R) = PipeAction { name, data: Data, meta -> + val goal = data.goal.pipe { transform(name, it, meta) } + return@PipeAction Data.of(goal, data.meta) + } + } +} + diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt index b4f95f05..14f98651 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt @@ -7,34 +7,47 @@ import hep.dataforge.names.NameToken import hep.dataforge.names.plus import hep.dataforge.names.toName import kotlin.coroutines.CoroutineContext +import kotlin.reflect.KClass /** * A data element characterized by its meta */ -interface Data : MetaRepr { +interface Data : MetaRepr { + /** + * Type marker for the data. The type is known before the calculation takes place so it could be cheched. + */ + val type: KClass + /** + * Meta for the data + */ val meta: Meta + + /** + * Lazy data value + */ val goal: Goal override fun toMeta(): Meta = meta companion object { - fun of(meta: Meta, goal: Goal): Data = DataImpl(meta, goal) - fun of(name: String, meta: Meta, goal: Goal): Data = NamedData(name, of(meta, goal)) - fun static(context: CoroutineContext, meta: Meta, value: T): Data = DataImpl(meta, Goal.static(context, value)) + fun of(type: KClass, goal: Goal, meta: Meta): Data = DataImpl(type, goal, meta) + inline fun of(goal: Goal, meta: Meta): Data = of(T::class, goal, meta) + fun of(name: String, goal: Goal, meta: Meta): Data = NamedData(name, of(goal, meta)) + fun static(context: CoroutineContext, value: T, meta: Meta): Data = DataImpl(value::class, Goal.static(context, value), meta) } } /** * Generic Data implementation */ -private class DataImpl(override val meta: Meta, override val goal: Goal) : Data +private class DataImpl(override val type: KClass, override val goal: Goal, override val meta: Meta) : Data -class NamedData(val name: String, data: Data) : Data by data +class NamedData(val name: String, data: Data) : Data by data /** * A tree-like data structure grouped into the node. All data inside the node must inherit its type */ -interface DataNode { +interface DataNode { /** * Get the specific data if it exists */ @@ -53,17 +66,17 @@ interface DataNode { operator fun iterator(): Iterator>> = asSequence().iterator() companion object { - fun build(block: DataTreeBuilder.() -> Unit) = DataTreeBuilder().apply(block).build() + fun build(block: DataTreeBuilder.() -> Unit) = DataTreeBuilder().apply(block).build() } } -internal sealed class DataTreeItem { - class Node(val tree: DataTree) : DataTreeItem() - class Value(val value: Data) : DataTreeItem() +internal sealed class DataTreeItem { + class Node(val tree: DataTree) : DataTreeItem() + class Value(val value: Data) : DataTreeItem() } -class DataTree internal constructor(private val items: Map>) : DataNode { +class DataTree internal constructor(private val items: Map>) : DataNode { //TODO add node-level meta? override fun get(name: Name): Data? = when (name.length) { @@ -93,15 +106,15 @@ class DataTree internal constructor(private val items: Map { - class Node(val tree: DataTreeBuilder) : DataTreeBuilderItem() - class Value(val value: Data) : DataTreeBuilderItem() +private sealed class DataTreeBuilderItem { + class Node(val tree: DataTreeBuilder) : DataTreeBuilderItem() + class Value(val value: Data) : DataTreeBuilderItem() } /** * A builder for a DataTree. */ -class DataTreeBuilder { +class DataTreeBuilder { private val map = HashMap>() operator fun set(token: NameToken, node: DataTreeBuilder) { @@ -116,7 +129,7 @@ class DataTreeBuilder { private fun buildNode(token: NameToken): DataTreeBuilder { return if (!map.containsKey(token)) { - DataTreeBuilder().also { map.put(token, DataTreeBuilderItem.Node(it)) } + DataTreeBuilder().also { map[token] = DataTreeBuilderItem.Node(it) } } else { (map[token] as? DataTreeBuilderItem.Node ?: error("The node with name $token is occupied by leaf")).tree } @@ -177,11 +190,21 @@ class DataTreeBuilder { /** * Generate a mutable builder from this node. Node content is not changed */ -fun DataNode.builder(): DataTreeBuilder = DataTreeBuilder().apply { +fun DataNode.builder(): DataTreeBuilder = DataTreeBuilder().apply { asSequence().forEach { (name, data) -> this[name] = data } } /** * Start computation for all goals in data node */ -fun DataNode<*>.startAll() = asSequence().forEach { (_,data)->data.goal.start() } \ No newline at end of file +fun DataNode<*>.startAll() = asSequence().forEach { (_, data) -> data.goal.start() } + +fun DataNode.filter(predicate: (Name, Data) -> Boolean): DataNode = DataNode.build { + asSequence().forEach {(name, data)-> + if(predicate(name,data)){ + this[name] = data + } + } +} + +//fun DataNode.filterIsInstance(type: KClass): DataNode = filter{_,data -> type.} \ No newline at end of file From 83a56fa0cdd42a112829a75c6273afa84d7e1a90 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 20 Dec 2018 10:40:32 +0300 Subject: [PATCH 17/37] Working on Output --- .../kotlin/hep/dataforge/data/Data.kt | 2 +- dataforge-io/build.gradle | 17 +++++++++++++++-- .../kotlin/hep/dataforge/io/Output.kt | 12 ++++++++++++ settings.gradle.kts | 3 ++- 4 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Output.kt diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt index 14f98651..e8cd20e9 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt @@ -14,7 +14,7 @@ import kotlin.reflect.KClass */ interface Data : MetaRepr { /** - * Type marker for the data. The type is known before the calculation takes place so it could be cheched. + * Type marker for the data. The type is known before the calculation takes place so it could be checked. */ val type: KClass /** diff --git a/dataforge-io/build.gradle b/dataforge-io/build.gradle index a0011494..56384c09 100644 --- a/dataforge-io/build.gradle +++ b/dataforge-io/build.gradle @@ -1,5 +1,5 @@ plugins { - id "org.jetbrains.kotlin.multiplatform" + id("org.jetbrains.kotlin.multiplatform") } repositories { @@ -9,7 +9,7 @@ repositories { kotlin { targets { fromPreset(presets.jvm, 'jvm') - //fromPreset(presets.js, 'js') + fromPreset(presets.js, 'js') // For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64 // For Linux, preset should be changed to e.g. presets.linuxX64 // For MacOS, preset should be changed to e.g. presets.macosX64 @@ -19,6 +19,19 @@ kotlin { commonMain { dependencies { api project(":dataforge-context") + api project(":dataforge-meta-io") + } + } + + jvmMain { + dependencies { + + } + } + + jsMain { + dependencies { + } } } diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Output.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Output.kt new file mode 100644 index 00000000..0f45726d --- /dev/null +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Output.kt @@ -0,0 +1,12 @@ +package hep.dataforge.io + +import hep.dataforge.context.ContextAware +import hep.dataforge.meta.EmptyMeta +import hep.dataforge.meta.Meta + +/** + * A generic way to render any object in the output. + */ +interface Output : ContextAware { + fun render(obj: T, meta: Meta = EmptyMeta) +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 4a91be73..774ee96a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,5 +12,6 @@ include( ":dataforge-meta", ":dataforge-meta-io", ":dataforge-context", - ":dataforge-data" + ":dataforge-data", + ":dataforge-io" ) From ec833dd13cbe84935a49030005d45dc18756c30f Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 11 Jan 2019 07:36:02 +0300 Subject: [PATCH 18/37] Generalized Contexts for multiplatform --- build.gradle.kts | 4 +- dataforge-context/build.gradle | 10 ++- .../hep/dataforge/context/AbstractPlugin.kt | 25 ++++++ .../kotlin/hep/dataforge/context/Context.kt | 27 ++++-- .../kotlin/hep/dataforge/context/Plugin.kt | 2 +- .../hep/dataforge/context/PluginRepository.kt | 17 ++-- .../kotlin/hep/dataforge/context/JSContext.kt | 83 +++++++++++++++++++ .../hep/dataforge/context/PluginRepository.kt | 11 +++ .../kotlin/hep/dataforge/context/Global.kt | 16 +--- .../hep/dataforge/context/JVMContext.kt | 17 +--- .../hep/dataforge/context/PluginRepository.kt | 9 ++ dataforge-io/build.gradle | 8 +- .../kotlin/hep/dataforge/io/Output.kt | 19 ++++- .../kotlin/hep/dataforge/io/OutputManager.kt | 33 ++++++++ .../kotlin/hep/dataforge/io/TextOutput.kt | 16 ++++ .../kotlin/hep/dataforge/names/Name.kt | 4 +- 16 files changed, 242 insertions(+), 59 deletions(-) create mode 100644 dataforge-context/src/commonMain/kotlin/hep/dataforge/context/AbstractPlugin.kt create mode 100644 dataforge-context/src/jsMain/kotlin/hep/dataforge/context/JSContext.kt create mode 100644 dataforge-context/src/jsMain/kotlin/hep/dataforge/context/PluginRepository.kt create mode 100644 dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/PluginRepository.kt create mode 100644 dataforge-io/src/commonMain/kotlin/hep/dataforge/io/OutputManager.kt create mode 100644 dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/TextOutput.kt diff --git a/build.gradle.kts b/build.gradle.kts index 692660e9..cbc2877c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,8 +1,8 @@ buildscript { extra["kotlinVersion"] = "1.3.11" - extra["ioVersion"] = "0.1.2-dev-2" + extra["ioVersion"] = "0.1.2" extra["serializationVersion"] = "0.9.1" - extra["coroutinesVersion"] = "1.0.1" + extra["coroutinesVersion"] = "1.1.0" val kotlinVersion: String by extra val ioVersion: String by extra diff --git a/dataforge-context/build.gradle b/dataforge-context/build.gradle index fed0f11f..9e727ae2 100644 --- a/dataforge-context/build.gradle +++ b/dataforge-context/build.gradle @@ -9,7 +9,7 @@ repositories { kotlin { targets { fromPreset(presets.jvm, 'jvm') - //fromPreset(presets.js, 'js') + fromPreset(presets.js, 'js') // For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64 // For Linux, preset should be changed to e.g. presets.linuxX64 // For MacOS, preset should be changed to e.g. presets.macosX64 @@ -21,11 +21,19 @@ kotlin { api project(":dataforge-meta") api "org.jetbrains.kotlin:kotlin-reflect" api "io.github.microutils:kotlin-logging-common:1.6.10" + api "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$coroutinesVersion" } } jvmMain{ dependencies{ api "io.github.microutils:kotlin-logging:1.6.10" + api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion" + } + } + jsMain{ + dependencies{ + api "io.github.microutils:kotlin-logging-js:1.6.10" + api "org.jetbrains.kotlinx:kotlinx-coroutines-core-js:$coroutinesVersion" } } } diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/AbstractPlugin.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/AbstractPlugin.kt new file mode 100644 index 00000000..ac726c1f --- /dev/null +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/AbstractPlugin.kt @@ -0,0 +1,25 @@ +package hep.dataforge.context + +import hep.dataforge.meta.EmptyMeta +import hep.dataforge.meta.Meta +import hep.dataforge.names.Name + +abstract class AbstractPlugin(override val meta: Meta = EmptyMeta) : Plugin { + + private var _context: Context? = null + + override val context: Context + get() = _context ?: error("Plugin $tag is not attached") + + override fun attach(context: Context) { + this._context = context + } + + override fun detach() { + this._context = null + } + + override fun provideTop(target: String, name: Name): Any? = null + + override fun listTop(target: String): Sequence = emptySequence() +} \ No newline at end of file diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt index ee81df6c..53cbd440 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt @@ -6,8 +6,11 @@ import hep.dataforge.names.toName import hep.dataforge.provider.Provider import hep.dataforge.provider.provideAll import hep.dataforge.values.Value +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import mu.KLogger import mu.KotlinLogging +import kotlin.coroutines.CoroutineContext import kotlin.reflect.KClass /** @@ -22,12 +25,12 @@ import kotlin.reflect.KClass * Since plugins could contain mutable state, context has two states: active and inactive. No changes are allowed to active context. * @author Alexander Nozik */ -interface Context : Named, MetaRepr, Provider { +interface Context : Named, MetaRepr, Provider, CoroutineScope { val parent: Context? /** - * Context properties. Working as substitutes for environment variables + * Context properties. Working as substitute for environment variables */ val properties: Meta @@ -46,11 +49,6 @@ interface Context : Named, MetaRepr, Provider { */ val isActive: Boolean - /** - * Provide services for given type - */ - fun services(type: KClass): Sequence - override val defaultTarget: String get() = Plugin.PLUGIN_TARGET override fun provideTop(target: String, name: Name): Any? { @@ -79,10 +77,19 @@ interface Context : Named, MetaRepr, Provider { */ fun deactivate(activator: Any) + override val coroutineContext: CoroutineContext + get() = Dispatchers.Default + /** * Detach all plugins and terminate context */ fun close() + + override fun toMeta(): Meta = buildMeta { + "parent" to parent?.name + "properties" to properties.seal() + "plugins" to plugins.map { it.toMeta() } + } } /** @@ -95,7 +102,11 @@ inline fun Context.list(target: String): Sequence { /** * A global root context */ -expect object Global : Context +expect object Global : Context{ + fun getContext(name: String): Context +} + + /** * The interface for something that encapsulated in context diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt index 56b2a63d..ccd979b3 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt @@ -46,7 +46,7 @@ interface Plugin : Named, Metoid, ContextAware, Provider, MetaRepr { * * @return */ - fun dependsOn(): List + fun dependsOn(): List = emptyList() /** * Start this plugin and attach registration info to the context. This method diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginRepository.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginRepository.kt index 3ac99da4..a069ff17 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginRepository.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginRepository.kt @@ -10,16 +10,17 @@ interface PluginFactory { } -object PluginRepository { +expect object PluginRepository { /** * List plugins available in the repository */ - fun list(): Sequence = Global.services(PluginFactory::class) + fun list(): Sequence - /** - * Fetch specific plugin and instantiate it with given meta - */ - fun fetch(tag: PluginTag, meta: Meta): Plugin = PluginRepository.list().find { it.tag.matches(tag) }?.build(meta) - ?: error("Plugin with tag $tag not found in the repository") -} \ No newline at end of file +} + +/** + * Fetch specific plugin and instantiate it with given meta + */ +fun PluginRepository.fetch(tag: PluginTag, meta: Meta): Plugin = PluginRepository.list().find { it.tag.matches(tag) }?.build(meta) + ?: error("Plugin with tag $tag not found in the repository") \ No newline at end of file diff --git a/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/JSContext.kt b/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/JSContext.kt new file mode 100644 index 00000000..8c803777 --- /dev/null +++ b/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/JSContext.kt @@ -0,0 +1,83 @@ +package hep.dataforge.context + +import hep.dataforge.meta.* +import mu.KLogger +import mu.KotlinLogging +import kotlin.jvm.Synchronized +import kotlin.reflect.KClass + +actual object Global: Context, JSContext("GLOBAL", null){ + /** + * Closing all contexts + * + * @throws Exception + */ + override fun close() { + logger.info{"Shutting down GLOBAL"} + for (ctx in contextRegistry.values) { + ctx.close() + } + super.close() + } + + private val contextRegistry = HashMap() + + /** + * Get previously builder context o builder a new one + * + * @param name + * @return + */ + @Synchronized + actual fun getContext(name: String): Context { + return contextRegistry.getOrPut(name) { JSContext(name) } + } +} + +open class JSContext( + final override val name: String, + final override val parent: JSContext? = Global, + properties: Meta = EmptyMeta +): Context { + + private val _properties = Config().apply { update(properties) } + override val properties: Meta + get() = if (parent == null) { + _properties + } else { + Laminate(_properties, parent.properties) + } + + override val plugins: PluginManager by lazy { PluginManager(this) } + override val logger: KLogger = KotlinLogging.logger(name) + + /** + * A property showing that dispatch thread is started in the context + */ + private var started = false + + override fun services(type: KClass): Sequence = TODO("Not implemented") + + /** + * Free up resources associated with this context + * + * @throws Exception + */ + override fun close() { + if (isActive) error("Can't close active context") + //detach all plugins + plugins.forEach { it.detach() } + } + + private val activators = HashSet() + + override val isActive: Boolean = !activators.isEmpty() + + override fun activate(activator: Any) { + activators.add(activator) + } + + override fun deactivate(activator: Any) { + activators.clear() + } +} \ No newline at end of file diff --git a/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/PluginRepository.kt b/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/PluginRepository.kt new file mode 100644 index 00000000..e72fc8e4 --- /dev/null +++ b/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/PluginRepository.kt @@ -0,0 +1,11 @@ +package hep.dataforge.context + +actual object PluginRepository { + /** + * List plugins available in the repository + */ + actual fun list(): Sequence { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} \ No newline at end of file diff --git a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/Global.kt b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/Global.kt index d560ddd2..4c987afc 100644 --- a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/Global.kt +++ b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/Global.kt @@ -42,7 +42,6 @@ actual object Global : Context, JVMContext("GLOBAL", null, Thread.currentThread( * * @throws Exception */ - @Throws(Exception::class) override fun close() { logger.info("Shutting down GLOBAL") for (ctx in contextRegistry.values) { @@ -60,20 +59,7 @@ actual object Global : Context, JVMContext("GLOBAL", null, Thread.currentThread( * @return */ @Synchronized - fun getContext(name: String): Context { + actual fun getContext(name: String): Context { return contextRegistry.getOrPut(name) { JVMContext(name) } } - - /** - * Close all contexts and terminate framework - */ - @JvmStatic - fun terminate() { - try { - close() - } catch (e: Exception) { - logger.error("Exception while terminating DataForge framework", e) - } - - } } diff --git a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt index b2a3c883..96371db8 100644 --- a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt +++ b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt @@ -16,6 +16,7 @@ package hep.dataforge.context import hep.dataforge.meta.* +import kotlinx.coroutines.Dispatchers import mu.KLogger import mu.KotlinLogging import java.lang.ref.WeakReference @@ -23,6 +24,7 @@ import java.util.* import java.util.concurrent.ExecutorService import java.util.concurrent.Executors import kotlin.collections.HashSet +import kotlin.coroutines.CoroutineContext import kotlin.reflect.KClass import kotlin.reflect.full.cast @@ -72,23 +74,10 @@ open class JVMContext( private val serviceCache: MutableMap, ServiceLoader<*>> = HashMap() - override fun services(type: KClass): Sequence { + fun services(type: KClass): Sequence { return serviceCache.getOrPut(type.java) { ServiceLoader.load(type.java, classLoader) }.asSequence().map { type.cast(it) } } - /** - * Get identity for this context - * - * @return - */ - override fun toMeta(): Meta { - return buildMeta { - "parent" to parent?.name - "properties" to properties.seal() - "plugins" to plugins.map { it.toMeta() } - } - } - /** * Free up resources associated with this context * diff --git a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/PluginRepository.kt b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/PluginRepository.kt new file mode 100644 index 00000000..9f84a905 --- /dev/null +++ b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/PluginRepository.kt @@ -0,0 +1,9 @@ +package hep.dataforge.context + +actual object PluginRepository { + /** + * List plugins available in the repository + */ + actual fun list(): Sequence = Global.services(PluginFactory::class) + +} \ No newline at end of file diff --git a/dataforge-io/build.gradle b/dataforge-io/build.gradle index 56384c09..7ee283bd 100644 --- a/dataforge-io/build.gradle +++ b/dataforge-io/build.gradle @@ -9,7 +9,7 @@ repositories { kotlin { targets { fromPreset(presets.jvm, 'jvm') - fromPreset(presets.js, 'js') + //fromPreset(presets.js, 'js') // For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64 // For Linux, preset should be changed to e.g. presets.linuxX64 // For MacOS, preset should be changed to e.g. presets.macosX64 @@ -28,11 +28,5 @@ kotlin { } } - - jsMain { - dependencies { - - } - } } } \ No newline at end of file diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Output.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Output.kt index 0f45726d..8c84e6d1 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Output.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Output.kt @@ -1,12 +1,27 @@ package hep.dataforge.io +import hep.dataforge.context.Context import hep.dataforge.context.ContextAware import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.Meta +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlin.reflect.KClass /** * A generic way to render any object in the output. + * + * An object could be rendered either in append or overlay mode. The mode is decided by the [Output] + * based on its configuration and provided meta + * */ -interface Output : ContextAware { +interface Output : ContextAware { + /** + * Render specific object with configuration. + * + * By convention actual render is called in asynchronous mode, so this method should never + * block execution + */ fun render(obj: T, meta: Meta = EmptyMeta) -} \ No newline at end of file +} diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/OutputManager.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/OutputManager.kt new file mode 100644 index 00000000..aa648dfa --- /dev/null +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/OutputManager.kt @@ -0,0 +1,33 @@ +package hep.dataforge.io + +import hep.dataforge.context.Plugin +import hep.dataforge.meta.EmptyMeta +import hep.dataforge.meta.Meta +import hep.dataforge.names.EmptyName +import hep.dataforge.names.Name +import kotlin.reflect.KClass + +/** + * A manager for outputs + */ +interface OutputManager : Plugin { + /** + * Provide an output for given name and stage. + * + * @param stage represents the node or directory for the output. Empty means root node. + * @param name represents the name inside the node. + * @param meta configuration for [Output] (not for rendered object) + * + */ + operator fun get(name: Name, stage: Name = EmptyName, meta: Meta = EmptyMeta): Output + + /** + * Get an output specialized for giver ntype + */ + fun typed(type: KClass, name: Name, stage: Name = EmptyName, meta: Meta = EmptyMeta): Output + +} + +inline fun OutputManager.typed(name: Name, stage: Name = EmptyName, meta: Meta = EmptyMeta): Output { + return typed(T::class, name, stage, meta) +} diff --git a/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/TextOutput.kt b/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/TextOutput.kt new file mode 100644 index 00000000..e5ca55aa --- /dev/null +++ b/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/TextOutput.kt @@ -0,0 +1,16 @@ +package hep.dataforge.io + +import hep.dataforge.context.Context +import hep.dataforge.meta.Meta +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + + +class TextOutput(override val context: Context, private val output: kotlinx.io.core.Output) : Output { + override fun render(obj: Any, meta: Meta) { + context.launch(Dispatchers.IO) { + output.append(obj.toString()) + output.append('\n') + } + } +} \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt index 57395b1b..b162e5f3 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt @@ -101,4 +101,6 @@ operator fun Name.plus(other: Name): Name = Name(this.tokens + other.tokens) operator fun Name.plus(other: String): Name = this + other.toName() -fun NameToken.toName() = Name(listOf(this)) \ No newline at end of file +fun NameToken.toName() = Name(listOf(this)) + +val EmptyName = Name(emptyList()) \ No newline at end of file From 25248f6cca3d9caf47f88775a2f79cdf236773fe Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 27 Jan 2019 15:06:10 +0300 Subject: [PATCH 19/37] Multiplatform plugin repository. --- .../hep/dataforge/context/PluginRepository.kt | 4 ++- .../hep/dataforge/context/PluginRepository.kt | 30 ++++++++++++++++--- .../hep/dataforge/context/PluginRepository.kt | 10 ++++++- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginRepository.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginRepository.kt index a069ff17..eca2bf05 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginRepository.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginRepository.kt @@ -5,13 +5,15 @@ import kotlin.reflect.KClass interface PluginFactory { val tag: PluginTag - val type: KClass + val type: KClass fun build(meta: Meta): Plugin } expect object PluginRepository { + fun register(factory: PluginFactory) + /** * List plugins available in the repository */ diff --git a/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/PluginRepository.kt b/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/PluginRepository.kt index e72fc8e4..f94928f5 100644 --- a/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/PluginRepository.kt +++ b/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/PluginRepository.kt @@ -1,11 +1,33 @@ package hep.dataforge.context +import hep.dataforge.meta.Meta +import kotlin.reflect.KClass + + actual object PluginRepository { + + private val factories: MutableSet = HashSet() + + actual fun register(factory: PluginFactory) { + factories.add(factory) + } + + fun register(tag: PluginTag, type: KClass, constructor: (Meta) -> T) { + val factory = object : PluginFactory { + override val tag: PluginTag = tag + override val type: KClass = type + + override fun build(meta: Meta): Plugin = constructor(meta) + + } + register(factory) + } + + inline fun register(tag: PluginTag, noinline constructor: (Meta) -> T) = + register(tag, T::class, constructor) + /** * List plugins available in the repository */ - actual fun list(): Sequence { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - + actual fun list(): Sequence = factories.asSequence() } \ No newline at end of file diff --git a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/PluginRepository.kt b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/PluginRepository.kt index 9f84a905..34e8ba77 100644 --- a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/PluginRepository.kt +++ b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/PluginRepository.kt @@ -1,9 +1,17 @@ package hep.dataforge.context actual object PluginRepository { + + private val factories: MutableSet = HashSet() + + actual fun register(factory: PluginFactory) { + factories.add(factory) + } + /** * List plugins available in the repository */ - actual fun list(): Sequence = Global.services(PluginFactory::class) + actual fun list(): Sequence = + factories.asSequence() + Global.services(PluginFactory::class) } \ No newline at end of file From 64a23545f709e3a51a905a8531175e96418fb187 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 27 Jan 2019 15:54:13 +0300 Subject: [PATCH 20/37] kotlin to 1.3.20 --- build.gradle.kts | 6 +++--- .../src/commonMain/kotlin/hep/dataforge/data/Goal.kt | 3 ++- .../src/commonMain/kotlin/hep/dataforge/io/Output.kt | 5 ----- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index cbc2877c..92044203 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,8 +1,8 @@ buildscript { - extra["kotlinVersion"] = "1.3.11" + extra["kotlinVersion"] = "1.3.20" extra["ioVersion"] = "0.1.2" extra["serializationVersion"] = "0.9.1" - extra["coroutinesVersion"] = "1.1.0" + extra["coroutinesVersion"] = "1.1.1" val kotlinVersion: String by extra val ioVersion: String by extra @@ -35,7 +35,7 @@ allprojects { } group = "hep.dataforge" - version = "0.1.1-dev-1" + version = "0.1.1-dev-2" } if (file("artifactory.gradle").exists()) { diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt index 5aabbe37..707e2074 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt @@ -63,13 +63,14 @@ private class StaticGoalImpl(val context: CoroutineContext, deferred: Complet override val totalWork: Double get() = 0.0 override val workDone: Double get() = 0.0 override val coroutineContext: CoroutineContext get() = context - } /** * Create a new [Goal] with given [dependencies] and execution [block]. The block takes monitor as parameter. * The goal block runs in a supervised scope, meaning that when it fails, it won't affect external scope. + * + * **Important:** Unlike regular deferred, the [Goal] is started lazily, so the actual calculation is called only when result is requested. */ fun CoroutineScope.createGoal(dependencies: Collection>, block: suspend GoalMonitor.() -> R): Goal { val monitor = GoalMonitor() diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Output.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Output.kt index 8c84e6d1..e9d1800c 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Output.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Output.kt @@ -1,13 +1,8 @@ package hep.dataforge.io -import hep.dataforge.context.Context import hep.dataforge.context.ContextAware import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.Meta -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import kotlin.reflect.KClass /** * A generic way to render any object in the output. From 5dd40f02264b2f70acdbf38af030daf6051bfd4c Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 27 Jan 2019 17:12:46 +0300 Subject: [PATCH 21/37] Build update and minor fixes --- build.gradle.kts | 30 +++++++---- dataforge-context/build.gradle | 40 -------------- dataforge-context/build.gradle.kts | 35 ++++++++++++ .../kotlin/hep/dataforge/context/Plugin.kt | 2 +- .../kotlin/hep/dataforge/context/JSContext.kt | 8 --- .../kotlin/hep/dataforge/data/Action.kt | 2 +- .../kotlin/hep/dataforge/data/Data.kt | 20 +++++-- dataforge-io/build.gradle | 2 +- .../kotlin/hep/dataforge/io/OutputManager.kt | 3 ++ dataforge-meta/build.gradle | 53 ------------------- dataforge-meta/build.gradle.kts | 50 +++++++++++++++++ 11 files changed, 127 insertions(+), 118 deletions(-) delete mode 100644 dataforge-context/build.gradle create mode 100644 dataforge-context/build.gradle.kts delete mode 100644 dataforge-meta/build.gradle create mode 100644 dataforge-meta/build.gradle.kts diff --git a/build.gradle.kts b/build.gradle.kts index 92044203..3705ffd4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,13 +1,10 @@ -buildscript { - extra["kotlinVersion"] = "1.3.20" - extra["ioVersion"] = "0.1.2" - extra["serializationVersion"] = "0.9.1" - extra["coroutinesVersion"] = "1.1.1" +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension - val kotlinVersion: String by extra - val ioVersion: String by extra - val coroutinesVersion: String by extra - val serializationVersion: String by extra +buildscript { + val kotlinVersion: String by rootProject.extra("1.3.20") + val ioVersion: String by rootProject.extra("0.1.2") + val coroutinesVersion: String by rootProject.extra("1.1.1") + val serializationVersion: String by rootProject.extra("0.9.1") repositories { jcenter() @@ -36,6 +33,21 @@ allprojects { group = "hep.dataforge" version = "0.1.1-dev-2" + + extensions.findByType()?.apply { + jvm { + compilations.all { + kotlinOptions { + jvmTarget = "1.8" + } + } + } + targets.all { + sourceSets.all { + languageSettings.progressiveMode = true + } + } + } } if (file("artifactory.gradle").exists()) { diff --git a/dataforge-context/build.gradle b/dataforge-context/build.gradle deleted file mode 100644 index 9e727ae2..00000000 --- a/dataforge-context/build.gradle +++ /dev/null @@ -1,40 +0,0 @@ -plugins { - id "org.jetbrains.kotlin.multiplatform" -} - -repositories { - jcenter() -} - -kotlin { - targets { - fromPreset(presets.jvm, 'jvm') - fromPreset(presets.js, 'js') - // For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64 - // For Linux, preset should be changed to e.g. presets.linuxX64 - // For MacOS, preset should be changed to e.g. presets.macosX64 - //fromPreset(presets.iosX64, 'ios') - } - sourceSets { - commonMain { - dependencies { - api project(":dataforge-meta") - api "org.jetbrains.kotlin:kotlin-reflect" - api "io.github.microutils:kotlin-logging-common:1.6.10" - api "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$coroutinesVersion" - } - } - jvmMain{ - dependencies{ - api "io.github.microutils:kotlin-logging:1.6.10" - api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion" - } - } - jsMain{ - dependencies{ - api "io.github.microutils:kotlin-logging-js:1.6.10" - api "org.jetbrains.kotlinx:kotlinx-coroutines-core-js:$coroutinesVersion" - } - } - } -} \ No newline at end of file diff --git a/dataforge-context/build.gradle.kts b/dataforge-context/build.gradle.kts new file mode 100644 index 00000000..5b33659e --- /dev/null +++ b/dataforge-context/build.gradle.kts @@ -0,0 +1,35 @@ +plugins { + kotlin("multiplatform") +} + +description = "Context and provider definitions" + +val coroutinesVersion: String by rootProject.extra + +kotlin { + jvm () + js() + + sourceSets { + val commonMain by getting { + dependencies { + api(project(":dataforge-meta")) + api(kotlin("reflect")) + api("io.github.microutils:kotlin-logging-common:1.6.10") + api("org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$coroutinesVersion") + } + } + val jvmMain by getting { + dependencies { + api("io.github.microutils:kotlin-logging:1.6.10") + api("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion") + } + } + val jsMain by getting { + dependencies { + api("io.github.microutils:kotlin-logging-js:1.6.10") + api("org.jetbrains.kotlinx:kotlinx-coroutines-core-js:$coroutinesVersion") + } + } + } +} \ No newline at end of file diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt index ccd979b3..780855a6 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt @@ -65,7 +65,7 @@ interface Plugin : Named, Metoid, ContextAware, Provider, MetaRepr { override fun toMeta(): Meta = buildMeta { "context" to context.name - "type" to this::class.qualifiedName + "type" to this::class.simpleName "tag" to tag "meta" to meta } diff --git a/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/JSContext.kt b/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/JSContext.kt index 8c803777..95ae8aea 100644 --- a/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/JSContext.kt +++ b/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/JSContext.kt @@ -4,7 +4,6 @@ import hep.dataforge.meta.* import mu.KLogger import mu.KotlinLogging import kotlin.jvm.Synchronized -import kotlin.reflect.KClass actual object Global: Context, JSContext("GLOBAL", null){ /** @@ -51,13 +50,6 @@ open class JSContext( override val plugins: PluginManager by lazy { PluginManager(this) } override val logger: KLogger = KotlinLogging.logger(name) - /** - * A property showing that dispatch thread is started in the context - */ - private var started = false - - override fun services(type: KClass): Sequence = TODO("Not implemented") - /** * Free up resources associated with this context * diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt index e4b35e39..895cc764 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt @@ -50,7 +50,7 @@ class PipeAction(val transform: (Name, Data, Meta) - /** * A simple pipe that performs transformation on the data and copies input meta into the output */ - fun simple(transform: suspend (Name, T, Meta) -> R) = PipeAction { name, data: Data, meta -> + inline fun simple(noinline transform: suspend (Name, T, Meta) -> R) = PipeAction { name, data: Data, meta -> val goal = data.goal.pipe { transform(name, it, meta) } return@PipeAction Data.of(goal, data.meta) } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt index e8cd20e9..6d74a788 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt @@ -32,15 +32,25 @@ interface Data : MetaRepr { companion object { fun of(type: KClass, goal: Goal, meta: Meta): Data = DataImpl(type, goal, meta) inline fun of(goal: Goal, meta: Meta): Data = of(T::class, goal, meta) - fun of(name: String, goal: Goal, meta: Meta): Data = NamedData(name, of(goal, meta)) - fun static(context: CoroutineContext, value: T, meta: Meta): Data = DataImpl(value::class, Goal.static(context, value), meta) + fun of(name: String, type: KClass, goal: Goal, meta: Meta): Data = + NamedData(name, of(type, goal, meta)) + + inline fun of(name: String, goal: Goal, meta: Meta): Data = + of(name, T::class, goal, meta) + + fun static(context: CoroutineContext, value: T, meta: Meta): Data = + DataImpl(value::class, Goal.static(context, value), meta) } } /** * Generic Data implementation */ -private class DataImpl(override val type: KClass, override val goal: Goal, override val meta: Meta) : Data +private class DataImpl( + override val type: KClass, + override val goal: Goal, + override val meta: Meta +) : Data class NamedData(val name: String, data: Data) : Data by data @@ -200,8 +210,8 @@ fun DataNode.builder(): DataTreeBuilder = DataTreeBuilder().a fun DataNode<*>.startAll() = asSequence().forEach { (_, data) -> data.goal.start() } fun DataNode.filter(predicate: (Name, Data) -> Boolean): DataNode = DataNode.build { - asSequence().forEach {(name, data)-> - if(predicate(name,data)){ + asSequence().forEach { (name, data) -> + if (predicate(name, data)) { this[name] = data } } diff --git a/dataforge-io/build.gradle b/dataforge-io/build.gradle index 7ee283bd..570e9d28 100644 --- a/dataforge-io/build.gradle +++ b/dataforge-io/build.gradle @@ -9,7 +9,7 @@ repositories { kotlin { targets { fromPreset(presets.jvm, 'jvm') - //fromPreset(presets.js, 'js') + fromPreset(presets.js, 'js') // For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64 // For Linux, preset should be changed to e.g. presets.linuxX64 // For MacOS, preset should be changed to e.g. presets.macosX64 diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/OutputManager.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/OutputManager.kt index aa648dfa..7a8c1eee 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/OutputManager.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/OutputManager.kt @@ -28,6 +28,9 @@ interface OutputManager : Plugin { } +/** + * Get an output with given [name], [stage] and reified content type + */ inline fun OutputManager.typed(name: Name, stage: Name = EmptyName, meta: Meta = EmptyMeta): Output { return typed(T::class, name, stage, meta) } diff --git a/dataforge-meta/build.gradle b/dataforge-meta/build.gradle deleted file mode 100644 index 994ee882..00000000 --- a/dataforge-meta/build.gradle +++ /dev/null @@ -1,53 +0,0 @@ -plugins { - id "org.jetbrains.kotlin.multiplatform" -} - -kotlin { - targets { - fromPreset(presets.jvm, 'jvm') - fromPreset(presets.js, 'js') - // For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64 - // For Linux, preset should be changed to e.g. presets.linuxX64 - // For MacOS, preset should be changed to e.g. presets.macosX64 - //fromPreset(presets.iosX64, 'ios') - } - sourceSets { - commonMain { - dependencies { - api "org.jetbrains.kotlin:kotlin-stdlib-common" - - } - } - commonTest { - dependencies { - implementation 'org.jetbrains.kotlin:kotlin-test-common' - implementation 'org.jetbrains.kotlin:kotlin-test-annotations-common' - } - } - jvmMain { - dependencies { - api "org.jetbrains.kotlin:kotlin-stdlib-jdk8" - } - } - jvmTest { - dependencies { - implementation 'org.jetbrains.kotlin:kotlin-test' - implementation 'org.jetbrains.kotlin:kotlin-test-junit' - } - } - jsMain { - dependencies { - api 'org.jetbrains.kotlin:kotlin-stdlib-js' - } - } - jsTest { - dependencies { - implementation 'org.jetbrains.kotlin:kotlin-test-js' - } - } -// iosMain { -// } -// iosTest { -// } - } -} \ No newline at end of file diff --git a/dataforge-meta/build.gradle.kts b/dataforge-meta/build.gradle.kts new file mode 100644 index 00000000..c2d39d6c --- /dev/null +++ b/dataforge-meta/build.gradle.kts @@ -0,0 +1,50 @@ +plugins { + kotlin("multiplatform") +} + +description = "Meta definition and basic operations on meta" + +kotlin { + jvm() + js() + + sourceSets { + + val commonMain by getting { + dependencies { + api(kotlin("stdlib")) + } + } + val commonTest by getting { + dependencies { + implementation(kotlin("test-common")) + implementation(kotlin("test-annotations-common")) + } + } + val jvmMain by getting { + dependencies { + api(kotlin("stdlib-jdk8")) + } + } + val jvmTest by getting { + dependencies { + implementation(kotlin("test")) + implementation(kotlin("test-junit")) + } + } + val jsMain by getting { + dependencies { + api(kotlin("stdlib-js")) + } + } + val jsTest by getting { + dependencies { + implementation(kotlin("test-js")) + } + } +// mingwMain { +// } +// mingwTest { +// } + } +} \ No newline at end of file From 4cafac1f1b94937c6b7e6b578b1e8a7740517da8 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 27 Jan 2019 17:16:40 +0300 Subject: [PATCH 22/37] Code cleanup --- README.md | 0 dataforge-context/build.gradle.kts | 2 +- .../kotlin/hep/dataforge/context/Context.kt | 4 +- .../hep/dataforge/context/PluginManager.kt | 3 +- .../hep/dataforge/context/PluginRepository.kt | 3 +- .../kotlin/hep/dataforge/context/PluginTag.kt | 6 +- .../kotlin/hep/dataforge/context/JSContext.kt | 12 +-- .../hep/dataforge/context/PluginRepository.kt | 2 +- .../hep/dataforge/context/JVMContext.kt | 13 ++-- .../hep/dataforge/context/PluginRepository.kt | 2 +- .../kotlin/hep/dataforge/data/Action.kt | 9 ++- .../kotlin/hep/dataforge/data/Goal.kt | 24 +++--- .../kotlin/hep/dataforge/io/OutputManager.kt | 6 +- .../hep/dataforge/meta/io/BinaryMetaFormat.kt | 2 +- .../hep/dataforge/meta/io/JsonMetaFormat.kt | 2 +- .../hep/dataforge/meta/io/MetaFormat.kt | 6 +- .../hep/dataforge/meta/io/MetaFormatTest.kt | 10 +-- .../kotlin/hep/dataforge/meta/Delegates.kt | 73 +++++++++++++------ .../kotlin/hep/dataforge/meta/Laminate.kt | 6 +- .../kotlin/hep/dataforge/meta/Meta.kt | 10 +-- .../hep/dataforge/meta/MutableMetaNode.kt | 23 ++++-- .../hep/dataforge/meta/Specification.kt | 21 ++++-- .../kotlin/hep/dataforge/meta/Styleable.kt | 2 +- .../hep/dataforge/meta/MetaBuilderTest.kt | 6 +- .../kotlin/hep/dataforge/names/NameTest.kt | 8 +- settings.gradle.kts | 10 +-- 26 files changed, 159 insertions(+), 106 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..e69de29b diff --git a/dataforge-context/build.gradle.kts b/dataforge-context/build.gradle.kts index 5b33659e..58da79a7 100644 --- a/dataforge-context/build.gradle.kts +++ b/dataforge-context/build.gradle.kts @@ -7,7 +7,7 @@ description = "Context and provider definitions" val coroutinesVersion: String by rootProject.extra kotlin { - jvm () + jvm() js() sourceSets { diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt index 53cbd440..51f632ae 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt @@ -11,7 +11,6 @@ import kotlinx.coroutines.Dispatchers import mu.KLogger import mu.KotlinLogging import kotlin.coroutines.CoroutineContext -import kotlin.reflect.KClass /** * The local environment for anything being done in DataForge framework. Contexts are organized into tree structure with [Global] at the top. @@ -102,12 +101,11 @@ inline fun Context.list(target: String): Sequence { /** * A global root context */ -expect object Global : Context{ +expect object Global : Context { fun getContext(name: String): Context } - /** * The interface for something that encapsulated in context * diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt index 25acfc7a..73b42bd4 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt @@ -59,7 +59,8 @@ class PluginManager(override val context: Context) : ContextAware, Iterable get(type: KClass, recursive: Boolean = true): T? = get(recursive) { type.isInstance(it) } as T? + operator fun get(type: KClass, recursive: Boolean = true): T? = + get(recursive) { type.isInstance(it) } as T? inline fun get(recursive: Boolean = true): T? = get(T::class, recursive) diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginRepository.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginRepository.kt index eca2bf05..60cdc3f0 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginRepository.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginRepository.kt @@ -24,5 +24,6 @@ expect object PluginRepository { /** * Fetch specific plugin and instantiate it with given meta */ -fun PluginRepository.fetch(tag: PluginTag, meta: Meta): Plugin = PluginRepository.list().find { it.tag.matches(tag) }?.build(meta) +fun PluginRepository.fetch(tag: PluginTag, meta: Meta): Plugin = + PluginRepository.list().find { it.tag.matches(tag) }?.build(meta) ?: error("Plugin with tag $tag not found in the repository") \ No newline at end of file diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginTag.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginTag.kt index 6196a8cb..a45b66ab 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginTag.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginTag.kt @@ -11,9 +11,9 @@ import hep.dataforge.meta.buildMeta * @author Alexander Nozik */ data class PluginTag( - val name: String, - val group: String = "", - val version: String = "" + val name: String, + val group: String = "", + val version: String = "" ) : MetaRepr { /** diff --git a/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/JSContext.kt b/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/JSContext.kt index 95ae8aea..908f4f16 100644 --- a/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/JSContext.kt +++ b/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/JSContext.kt @@ -5,14 +5,14 @@ import mu.KLogger import mu.KotlinLogging import kotlin.jvm.Synchronized -actual object Global: Context, JSContext("GLOBAL", null){ +actual object Global : Context, JSContext("GLOBAL", null) { /** * Closing all contexts * * @throws Exception */ override fun close() { - logger.info{"Shutting down GLOBAL"} + logger.info { "Shutting down GLOBAL" } for (ctx in contextRegistry.values) { ctx.close() } @@ -34,10 +34,10 @@ actual object Global: Context, JSContext("GLOBAL", null){ } open class JSContext( - final override val name: String, - final override val parent: JSContext? = Global, - properties: Meta = EmptyMeta -): Context { + final override val name: String, + final override val parent: JSContext? = Global, + properties: Meta = EmptyMeta +) : Context { private val _properties = Config().apply { update(properties) } override val properties: Meta diff --git a/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/PluginRepository.kt b/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/PluginRepository.kt index f94928f5..830cd233 100644 --- a/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/PluginRepository.kt +++ b/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/PluginRepository.kt @@ -24,7 +24,7 @@ actual object PluginRepository { } inline fun register(tag: PluginTag, noinline constructor: (Meta) -> T) = - register(tag, T::class, constructor) + register(tag, T::class, constructor) /** * List plugins available in the repository diff --git a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt index 96371db8..223c7d67 100644 --- a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt +++ b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt @@ -16,7 +16,6 @@ package hep.dataforge.context import hep.dataforge.meta.* -import kotlinx.coroutines.Dispatchers import mu.KLogger import mu.KotlinLogging import java.lang.ref.WeakReference @@ -24,15 +23,14 @@ import java.util.* import java.util.concurrent.ExecutorService import java.util.concurrent.Executors import kotlin.collections.HashSet -import kotlin.coroutines.CoroutineContext import kotlin.reflect.KClass import kotlin.reflect.full.cast open class JVMContext( - final override val name: String, - final override val parent: JVMContext? = Global, - classLoader: ClassLoader? = null, - properties: Meta = EmptyMeta + final override val name: String, + final override val parent: JVMContext? = Global, + classLoader: ClassLoader? = null, + properties: Meta = EmptyMeta ) : Context, AutoCloseable { private val _properties = Config().apply { update(properties) } @@ -75,7 +73,8 @@ open class JVMContext( private val serviceCache: MutableMap, ServiceLoader<*>> = HashMap() fun services(type: KClass): Sequence { - return serviceCache.getOrPut(type.java) { ServiceLoader.load(type.java, classLoader) }.asSequence().map { type.cast(it) } + return serviceCache.getOrPut(type.java) { ServiceLoader.load(type.java, classLoader) }.asSequence() + .map { type.cast(it) } } /** diff --git a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/PluginRepository.kt b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/PluginRepository.kt index 34e8ba77..e6008c34 100644 --- a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/PluginRepository.kt +++ b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/PluginRepository.kt @@ -12,6 +12,6 @@ actual object PluginRepository { * List plugins available in the repository */ actual fun list(): Sequence = - factories.asSequence() + Global.services(PluginFactory::class) + factories.asSequence() + Global.services(PluginFactory::class) } \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt index 895cc764..2a8e2fb6 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt @@ -50,10 +50,11 @@ class PipeAction(val transform: (Name, Data, Meta) - /** * A simple pipe that performs transformation on the data and copies input meta into the output */ - inline fun simple(noinline transform: suspend (Name, T, Meta) -> R) = PipeAction { name, data: Data, meta -> - val goal = data.goal.pipe { transform(name, it, meta) } - return@PipeAction Data.of(goal, data.meta) - } + inline fun simple(noinline transform: suspend (Name, T, Meta) -> R) = + PipeAction { name, data: Data, meta -> + val goal = data.goal.pipe { transform(name, it, meta) } + return@PipeAction Data.of(goal, data.meta) + } } } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt index 707e2074..104a9037 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt @@ -20,7 +20,8 @@ interface Goal : Deferred, CoroutineScope { /** * Create goal wrapping static value. This goal is always completed */ - fun static(context: CoroutineContext, value: T): Goal = StaticGoalImpl(context, CompletableDeferred(value)) + fun static(context: CoroutineContext, value: T): Goal = + StaticGoalImpl(context, CompletableDeferred(value)) } } @@ -48,16 +49,18 @@ class GoalMonitor { } private class GoalImpl( - override val dependencies: Collection>, - val monitor: GoalMonitor, - deferred: Deferred) : Goal, Deferred by deferred { + override val dependencies: Collection>, + val monitor: GoalMonitor, + deferred: Deferred +) : Goal, Deferred by deferred { override val coroutineContext: CoroutineContext get() = this override val totalWork: Double get() = dependencies.sumByDouble { totalWork } + monitor.totalWork override val workDone: Double get() = dependencies.sumByDouble { workDone } + monitor.workDone override val status: String get() = monitor.status } -private class StaticGoalImpl(val context: CoroutineContext, deferred: CompletableDeferred) : Goal, Deferred by deferred { +private class StaticGoalImpl(val context: CoroutineContext, deferred: CompletableDeferred) : Goal, + Deferred by deferred { override val dependencies: Collection> get() = emptyList() override val status: String get() = "" override val totalWork: Double get() = 0.0 @@ -94,7 +97,10 @@ fun Goal.pipe(block: suspend GoalMonitor.(T) -> R): Goal = createGo * Create a joining goal. * @param scope the scope for resulting goal. By default use first goal in list */ -fun Collection>.join(scope: CoroutineScope = first(), block: suspend GoalMonitor.(Collection) -> R): Goal = - scope.createGoal(this) { - block(map { it.await() }) - } \ No newline at end of file +fun Collection>.join( + scope: CoroutineScope = first(), + block: suspend GoalMonitor.(Collection) -> R +): Goal = + scope.createGoal(this) { + block(map { it.await() }) + } \ No newline at end of file diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/OutputManager.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/OutputManager.kt index 7a8c1eee..3b454391 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/OutputManager.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/OutputManager.kt @@ -31,6 +31,10 @@ interface OutputManager : Plugin { /** * Get an output with given [name], [stage] and reified content type */ -inline fun OutputManager.typed(name: Name, stage: Name = EmptyName, meta: Meta = EmptyMeta): Output { +inline fun OutputManager.typed( + name: Name, + stage: Name = EmptyName, + meta: Meta = EmptyMeta +): Output { return typed(T::class, name, stage, meta) } diff --git a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/BinaryMetaFormat.kt b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/BinaryMetaFormat.kt index c2148ba4..176435da 100644 --- a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/BinaryMetaFormat.kt +++ b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/BinaryMetaFormat.kt @@ -124,7 +124,7 @@ object BinaryMetaFormat : MetaFormat { } } -class BinaryMetaFormatFactory: MetaFormatFactory{ +class BinaryMetaFormatFactory : MetaFormatFactory { override val name: String = "bin" override val key: Short = 0x4249//BI diff --git a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/JsonMetaFormat.kt b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/JsonMetaFormat.kt index cde54c25..c9c67201 100644 --- a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/JsonMetaFormat.kt +++ b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/JsonMetaFormat.kt @@ -74,7 +74,7 @@ class JsonMeta(val json: JsonObject) : Meta { } } -class JsonMetaFormatFactory: MetaFormatFactory{ +class JsonMetaFormatFactory : MetaFormatFactory { override val name: String = "json" override val key: Short = 0x4a53//"JS" diff --git a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt index 2f619adf..2d7de871 100644 --- a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt +++ b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/MetaFormat.kt @@ -21,13 +21,13 @@ interface MetaFormatFactory { fun build(): MetaFormat } -fun Meta.asString(format: MetaFormat = JsonMetaFormat): String{ +fun Meta.asString(format: MetaFormat = JsonMetaFormat): String { val builder = BytePacketBuilder() - format.write(this,builder) + format.write(this, builder) return builder.build().readText() } -fun MetaFormat.parse(str: String): Meta{ +fun MetaFormat.parse(str: String): Meta { return read(ByteReadPacket(str.toByteArray())) } diff --git a/dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt b/dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt index 6655a541..78966dc8 100644 --- a/dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt +++ b/dataforge-meta-io/src/commonTest/kotlin/hep/dataforge/meta/io/MetaFormatTest.kt @@ -4,9 +4,9 @@ import hep.dataforge.meta.buildMeta import kotlin.test.Test import kotlin.test.assertEquals -class MetaFormatTest{ +class MetaFormatTest { @Test - fun testBinaryMetaFormat(){ + fun testBinaryMetaFormat() { val meta = buildMeta { "a" to 22 "node" to { @@ -14,13 +14,13 @@ class MetaFormatTest{ "c" to 11.1 } } - val string = meta.asString(BinaryMetaFormat) + val string = meta.asString(BinaryMetaFormat) val result = BinaryMetaFormat.parse(string) assertEquals(meta, result) } @Test - fun testJsonMetaFormat(){ + fun testJsonMetaFormat() { val meta = buildMeta { "a" to 22 "node" to { @@ -30,7 +30,7 @@ class MetaFormatTest{ } val string = meta.asString(JsonMetaFormat) val result = JsonMetaFormat.parse(string) - assertEquals(meta,result) + assertEquals(meta, result) } } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Delegates.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Delegates.kt index f63a271b..fadfc673 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Delegates.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Delegates.kt @@ -11,25 +11,29 @@ import kotlin.reflect.KProperty //TODO add caching for sealed nodes -class ValueDelegate(private val key: String? = null, private val default: Value? = null) : ReadOnlyProperty { +class ValueDelegate(private val key: String? = null, private val default: Value? = null) : + ReadOnlyProperty { override fun getValue(thisRef: Metoid, property: KProperty<*>): Value? { return thisRef.meta[key ?: property.name]?.value ?: default } } -class StringDelegate(private val key: String? = null, private val default: String? = null) : ReadOnlyProperty { +class StringDelegate(private val key: String? = null, private val default: String? = null) : + ReadOnlyProperty { override fun getValue(thisRef: Metoid, property: KProperty<*>): String? { return thisRef.meta[key ?: property.name]?.string ?: default } } -class BooleanDelegate(private val key: String? = null, private val default: Boolean? = null) : ReadOnlyProperty { +class BooleanDelegate(private val key: String? = null, private val default: Boolean? = null) : + ReadOnlyProperty { override fun getValue(thisRef: Metoid, property: KProperty<*>): Boolean? { return thisRef.meta[key ?: property.name]?.boolean ?: default } } -class NumberDelegate(private val key: String? = null, private val default: Number? = null) : ReadOnlyProperty { +class NumberDelegate(private val key: String? = null, private val default: Number? = null) : + ReadOnlyProperty { override fun getValue(thisRef: Metoid, property: KProperty<*>): Number? { return thisRef.meta[key ?: property.name]?.number ?: default } @@ -37,25 +41,32 @@ class NumberDelegate(private val key: String? = null, private val default: Numbe //Delegates with non-null values -class SafeStringDelegate(private val key: String? = null, private val default: String) : ReadOnlyProperty { +class SafeStringDelegate(private val key: String? = null, private val default: String) : + ReadOnlyProperty { override fun getValue(thisRef: Metoid, property: KProperty<*>): String { return thisRef.meta[key ?: property.name]?.string ?: default } } -class SafeBooleanDelegate(private val key: String? = null, private val default: Boolean) : ReadOnlyProperty { +class SafeBooleanDelegate(private val key: String? = null, private val default: Boolean) : + ReadOnlyProperty { override fun getValue(thisRef: Metoid, property: KProperty<*>): Boolean { return thisRef.meta[key ?: property.name]?.boolean ?: default } } -class SafeNumberDelegate(private val key: String? = null, private val default: Number) : ReadOnlyProperty { +class SafeNumberDelegate(private val key: String? = null, private val default: Number) : + ReadOnlyProperty { override fun getValue(thisRef: Metoid, property: KProperty<*>): Number { return thisRef.meta[key ?: property.name]?.number ?: default } } -class SafeEnumDelegate>(private val key: String? = null, private val default: E, private val resolver: (String) -> E) : ReadOnlyProperty { +class SafeEnumDelegate>( + private val key: String? = null, + private val default: E, + private val resolver: (String) -> E +) : ReadOnlyProperty { override fun getValue(thisRef: Metoid, property: KProperty<*>): E { return (thisRef.meta[key ?: property.name]?.string)?.let { resolver(it) } ?: default } @@ -63,9 +74,10 @@ class SafeEnumDelegate>(private val key: String? = null, private val //Child node delegate -class ChildDelegate(private val key: String? = null, private val converter: (Meta) -> T) : ReadOnlyProperty { +class ChildDelegate(private val key: String? = null, private val converter: (Meta) -> T) : + ReadOnlyProperty { override fun getValue(thisRef: Metoid, property: KProperty<*>): T? { - return thisRef.meta[key ?: property.name]?.node?.let { converter(it)} + return thisRef.meta[key ?: property.name]?.node?.let { converter(it) } } } @@ -95,18 +107,20 @@ fun Metoid.boolean(default: Boolean, key: String? = null) = SafeBooleanDelegate( @JvmName("safeNumber") fun Metoid.number(default: Number, key: String? = null) = SafeNumberDelegate(key, default) -inline fun > Metoid.enum(default: E, key: String? = null) = SafeEnumDelegate(key, default) { enumValueOf(it) } +inline fun > Metoid.enum(default: E, key: String? = null) = + SafeEnumDelegate(key, default) { enumValueOf(it) } /* Config delegates */ -class ValueConfigDelegate(private val key: String? = null, private val default: Value? = null) : ReadWriteProperty { +class ValueConfigDelegate(private val key: String? = null, private val default: Value? = null) : + ReadWriteProperty { override fun getValue(thisRef: Configurable, property: KProperty<*>): Value? { return thisRef.config[key ?: property.name]?.value ?: default } override fun setValue(thisRef: Configurable, property: KProperty<*>, value: Value?) { val name = key ?: property.name - if(value == null){ + if (value == null) { thisRef.config.remove(name) } else { thisRef.config[name] = value @@ -114,7 +128,8 @@ class ValueConfigDelegate(private val key: String? = null, private val default: } } -class StringConfigDelegate(private val key: String? = null, private val default: String? = null) : ReadWriteProperty { +class StringConfigDelegate(private val key: String? = null, private val default: String? = null) : + ReadWriteProperty { override fun getValue(thisRef: Configurable, property: KProperty<*>): String? { return thisRef.config[key ?: property.name]?.string ?: default } @@ -124,7 +139,8 @@ class StringConfigDelegate(private val key: String? = null, private val default: } } -class BooleanConfigDelegate(private val key: String? = null, private val default: Boolean? = null) : ReadWriteProperty { +class BooleanConfigDelegate(private val key: String? = null, private val default: Boolean? = null) : + ReadWriteProperty { override fun getValue(thisRef: Configurable, property: KProperty<*>): Boolean? { return thisRef.config[key ?: property.name]?.boolean ?: default } @@ -134,7 +150,8 @@ class BooleanConfigDelegate(private val key: String? = null, private val default } } -class NumberConfigDelegate(private val key: String? = null, private val default: Number? = null) : ReadWriteProperty { +class NumberConfigDelegate(private val key: String? = null, private val default: Number? = null) : + ReadWriteProperty { override fun getValue(thisRef: Configurable, property: KProperty<*>): Number? { return thisRef.config[key ?: property.name]?.number ?: default } @@ -146,7 +163,8 @@ class NumberConfigDelegate(private val key: String? = null, private val default: //Delegates with non-null values -class SafeStringConfigDelegate(private val key: String? = null, private val default: String) : ReadWriteProperty { +class SafeStringConfigDelegate(private val key: String? = null, private val default: String) : + ReadWriteProperty { override fun getValue(thisRef: Configurable, property: KProperty<*>): String { return thisRef.config[key ?: property.name]?.string ?: default } @@ -156,7 +174,8 @@ class SafeStringConfigDelegate(private val key: String? = null, private val defa } } -class SafeBooleanConfigDelegate(private val key: String? = null, private val default: Boolean) : ReadWriteProperty { +class SafeBooleanConfigDelegate(private val key: String? = null, private val default: Boolean) : + ReadWriteProperty { override fun getValue(thisRef: Configurable, property: KProperty<*>): Boolean { return thisRef.config[key ?: property.name]?.boolean ?: default } @@ -166,7 +185,8 @@ class SafeBooleanConfigDelegate(private val key: String? = null, private val def } } -class SafeNumberConfigDelegate(private val key: String? = null, private val default: Number) : ReadWriteProperty { +class SafeNumberConfigDelegate(private val key: String? = null, private val default: Number) : + ReadWriteProperty { override fun getValue(thisRef: Configurable, property: KProperty<*>): Number { return thisRef.config[key ?: property.name]?.number ?: default } @@ -176,7 +196,11 @@ class SafeNumberConfigDelegate(private val key: String? = null, private val defa } } -class SafeEnumvConfigDelegate>(private val key: String? = null, private val default: E, private val resolver: (String) -> E) : ReadWriteProperty { +class SafeEnumvConfigDelegate>( + private val key: String? = null, + private val default: E, + private val resolver: (String) -> E +) : ReadWriteProperty { override fun getValue(thisRef: Configurable, property: KProperty<*>): E { return (thisRef.config[key ?: property.name]?.string)?.let { resolver(it) } ?: default } @@ -188,7 +212,8 @@ class SafeEnumvConfigDelegate>(private val key: String? = null, priv //Child node delegate -class ChildConfigDelegate(private val key: String? = null, private val converter: (Config) -> T) : ReadWriteProperty { +class ChildConfigDelegate(private val key: String? = null, private val converter: (Config) -> T) : + ReadWriteProperty { override fun getValue(thisRef: Configurable, property: KProperty<*>): T { return converter(thisRef.config[key ?: property.name]?.node ?: Config()) } @@ -214,7 +239,8 @@ fun Configurable.number(default: Number? = null, key: String? = null) = NumberCo fun Configurable.child(key: String? = null) = ChildConfigDelegate(key) { SimpleConfigurable(it) } -fun Configurable.child(key: String? = null, converter: (Config) -> T) = ChildConfigDelegate(key, converter) +fun Configurable.child(key: String? = null, converter: (Config) -> T) = + ChildConfigDelegate(key, converter) //fun Configurable.spec(spec: Specification, key: String? = null) = ChildConfigDelegate(key) { spec.wrap(this) } @@ -227,4 +253,5 @@ fun Configurable.boolean(default: Boolean, key: String? = null) = SafeBooleanCon @JvmName("safeNumber") fun Configurable.number(default: Number, key: String? = null) = SafeNumberConfigDelegate(key, default) -inline fun > Configurable.enum(default: E, key: String? = null) = SafeEnumvConfigDelegate(key, default) { enumValueOf(it) } \ No newline at end of file +inline fun > Configurable.enum(default: E, key: String? = null) = + SafeEnumvConfigDelegate(key, default) { enumValueOf(it) } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt index 5d8f13c5..094380c8 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt @@ -10,14 +10,14 @@ import hep.dataforge.names.NameToken class Laminate(layers: List) : Meta { val layers: List = layers.flatMap { - if(it is Laminate){ + if (it is Laminate) { it.layers - } else{ + } else { listOf(it) } } - constructor(vararg layers: Meta): this(layers.asList()) + constructor(vararg layers: Meta) : this(layers.asList()) override val items: Map> get() = layers.map { it.items.keys }.flatten().associateWith { key -> diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt index 12dc3e2f..56590dbd 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -77,9 +77,9 @@ fun Meta.getAll(name: Name): Map> { val (body, query) = name.last()!! val regex = query.toRegex() return (this[name.cutLast()] as? NodeItem<*>)?.node?.items - ?.filter { it.key.body == body && (query.isEmpty() || regex.matches(it.key.query)) } - ?.mapKeys { it.key.query } - ?: emptyMap() + ?.filter { it.key.body == body && (query.isEmpty() || regex.matches(it.key.query)) } + ?.mapKeys { it.key.query } + ?: emptyMap() } @@ -155,8 +155,8 @@ object EmptyMeta : Meta { val MetaItem<*>.value get() = (this as? MetaItem.ValueItem)?.value - ?: (this.node[VALUE_KEY] as? MetaItem.ValueItem)?.value - ?: error("Trying to interpret node meta item as value item") + ?: (this.node[VALUE_KEY] as? MetaItem.ValueItem)?.value + ?: error("Trying to interpret node meta item as value item") val MetaItem<*>.string get() = value.string val MetaItem<*>.boolean get() = value.boolean val MetaItem<*>.number get() = value.number diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt index 48729efb..fe48a299 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt @@ -6,7 +6,10 @@ import hep.dataforge.names.plus import hep.dataforge.names.toName import hep.dataforge.values.Value -class MetaListener(val owner: Any? = null, val action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit) { +class MetaListener( + val owner: Any? = null, + val action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit +) { operator fun invoke(name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) = action(name, oldItem, newItem) } @@ -87,7 +90,7 @@ abstract class MutableMetaNode> : MetaNode(), MutableM val token = name.first()!! //get existing or create new node. Query is ignored for new node val child = this.items[token]?.node - ?: empty().also { this[token.body.toName()] = MetaItem.NodeItem(it) } + ?: empty().also { this[token.body.toName()] = MetaItem.NodeItem(it) } child[name.cutFirst()] = item } } @@ -130,16 +133,20 @@ fun > M.update(meta: Meta) { meta.items.forEach { entry -> val value = entry.value when (value) { - is MetaItem.ValueItem -> setValue(entry.key.toName(),value.value) + is MetaItem.ValueItem -> setValue(entry.key.toName(), value.value) is MetaItem.NodeItem -> (this[entry.key.toName()] as? MetaItem.NodeItem)?.node?.update(value.node) - ?: run { setNode(entry.key.toName(),value.node)} + ?: run { setNode(entry.key.toName(), value.node) } } } } /* Same name siblings generation */ -fun > M.setIndexed(name: Name, items: Iterable>, queryFactory: (Int) -> String = { it.toString() }) { +fun > M.setIndexed( + name: Name, + items: Iterable>, + queryFactory: (Int) -> String = { it.toString() } +) { val tokens = name.tokens.toMutableList() val last = tokens.last() items.forEachIndexed { index, meta -> @@ -149,7 +156,11 @@ fun > M.setIndexed(name: Name, items: Iterable>, } } -fun > M.setIndexed(name: Name, metas: Iterable, queryFactory: (Int) -> String = { it.toString() }) { +fun > M.setIndexed( + name: Name, + metas: Iterable, + queryFactory: (Int) -> String = { it.toString() } +) { setIndexed(name, metas.map { wrap(name, it) }, queryFactory) } 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 132fba06..bdd11429 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt @@ -3,7 +3,7 @@ package hep.dataforge.meta /** * Marker interface for specifications */ -interface Specification: Configurable{ +interface Specification : Configurable { operator fun get(name: String): MetaItem? = config[name] } @@ -26,24 +26,29 @@ interface SpecificationBuilder { fun wrap(meta: Meta): T = wrap(meta.toConfig()) } -fun specification(wrapper: (Config) -> T): SpecificationBuilder = object : SpecificationBuilder { - override fun wrap(config: Config): T = wrapper(config) -} +fun specification(wrapper: (Config) -> T): SpecificationBuilder = + object : SpecificationBuilder { + override fun wrap(config: Config): T = wrapper(config) + } /** * Apply specified configuration to configurable */ -fun > T.configure(spec: S, action: C.() -> Unit) = apply { spec.update(config, action) } +fun > T.configure(spec: S, action: C.() -> Unit) = + apply { spec.update(config, action) } /** * Update configuration using given specification */ -fun > Specification.update(spec: S, action: C.() -> Unit) = apply { spec.update(config, action) } +fun > Specification.update(spec: S, action: C.() -> Unit) = + apply { spec.update(config, action) } /** * Create a style based on given specification */ -fun > S.createStyle(action: C.() -> Unit): Meta = Config().also { update(it, action) } +fun > S.createStyle(action: C.() -> Unit): Meta = + Config().also { update(it, action) } -fun Specification.spec(spec: SpecificationBuilder, key: String? = null) = ChildConfigDelegate(key) { spec.wrap(config) } \ No newline at end of file +fun Specification.spec(spec: SpecificationBuilder, key: String? = null) = + ChildConfigDelegate(key) { spec.wrap(config) } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styleable.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styleable.kt index 5b4af6d2..d8aaf24e 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styleable.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styleable.kt @@ -44,7 +44,7 @@ class StyledConfig(val config: Config, style: Meta = EmptyMeta) : Config() { } is MetaItem.ValueItem -> MetaItem.ValueItem(value.value) is MetaItem.NodeItem -> MetaItem.NodeItem( - StyledConfig(value.node, styleValue?.node ?: EmptyMeta) + StyledConfig(value.node, styleValue?.node ?: EmptyMeta) ) } key to item diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaBuilderTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaBuilderTest.kt index 8cda9003..3f34882c 100644 --- a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaBuilderTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaBuilderTest.kt @@ -5,12 +5,12 @@ import kotlin.test.Test import kotlin.test.assertEquals -class MetaBuilderTest{ +class MetaBuilderTest { @Test - fun testBuilder(){ + fun testBuilder() { val meta = buildMeta { "a" to 22 - "b" to listOf(1,2,3) + "b" to listOf(1, 2, 3) this["c"] = "myValue".asValue() "node" to { "e" to 12.2 diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/names/NameTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/names/NameTest.kt index 52fb478d..1302777e 100644 --- a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/names/NameTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/names/NameTest.kt @@ -3,17 +3,17 @@ package hep.dataforge.names import kotlin.test.Test import kotlin.test.assertEquals -class NameTest{ +class NameTest { @Test - fun simpleName(){ + fun simpleName() { val name = "token1.token2.token3".toName() assertEquals("token2", name[1].toString()) } @Test - fun equalityTest(){ + fun equalityTest() { val name1 = "token1.token2[2].token3".toName() val name2 = "token1".toName() + "token2[2].token3" - assertEquals(name1,name2) + assertEquals(name1, name2) } } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 774ee96a..216dd001 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -9,9 +9,9 @@ pluginManagement { rootProject.name = "dataforge-core" include( - ":dataforge-meta", - ":dataforge-meta-io", - ":dataforge-context", - ":dataforge-data", - ":dataforge-io" + ":dataforge-meta", + ":dataforge-meta-io", + ":dataforge-context", + ":dataforge-data", + ":dataforge-io" ) From 2d8810110ecd139515b8ae2f2bc8898a7bc68eee Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 27 Jan 2019 20:54:28 +0300 Subject: [PATCH 23/37] IO update --- build.gradle.kts | 2 +- .../kotlin/hep/dataforge/context/Context.kt | 5 +- .../kotlin/hep/dataforge/context/PluginTag.kt | 2 + .../kotlin/hep/dataforge/provider/Provider.kt | 10 +++- .../kotlin/hep/dataforge/provider/Types.kt | 26 ++++++++ dataforge-io/build.gradle | 32 ---------- dataforge-io/build.gradle.kts | 20 +++++++ .../kotlin/hep/dataforge/io/OutputManager.kt | 23 ++++++++ .../kotlin/hep/dataforge/io/TextOutput.kt | 59 +++++++++++++++++++ .../kotlin/hep/dataforge/io/ConsoleOutput.kt | 22 +++++++ .../kotlin/hep/dataforge/io/ConsoleOutput.kt | 13 ++++ .../kotlin/hep/dataforge/io/TextOutput.kt | 16 ----- 12 files changed, 178 insertions(+), 52 deletions(-) create mode 100644 dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Types.kt delete mode 100644 dataforge-io/build.gradle create mode 100644 dataforge-io/build.gradle.kts create mode 100644 dataforge-io/src/commonMain/kotlin/hep/dataforge/io/TextOutput.kt create mode 100644 dataforge-io/src/jsMain/kotlin/hep/dataforge/io/ConsoleOutput.kt create mode 100644 dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/ConsoleOutput.kt delete mode 100644 dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/TextOutput.kt diff --git a/build.gradle.kts b/build.gradle.kts index 3705ffd4..2daffdb1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension buildscript { val kotlinVersion: String by rootProject.extra("1.3.20") - val ioVersion: String by rootProject.extra("0.1.2") + val ioVersion: String by rootProject.extra("0.1.4") val coroutinesVersion: String by rootProject.extra("1.1.1") val serializationVersion: String by rootProject.extra("0.9.1") diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt index 51f632ae..a9366c35 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt @@ -4,6 +4,7 @@ import hep.dataforge.meta.* import hep.dataforge.names.Name import hep.dataforge.names.toName import hep.dataforge.provider.Provider +import hep.dataforge.provider.Types import hep.dataforge.provider.provideAll import hep.dataforge.values.Value import kotlinx.coroutines.CoroutineScope @@ -94,8 +95,8 @@ interface Context : Named, MetaRepr, Provider, CoroutineScope { /** * A sequences of all objects provided by plugins with given target and type */ -inline fun Context.list(target: String): Sequence { - return plugins.asSequence().flatMap { provideAll(target) }.mapNotNull { it as? T } +inline fun Context.provideAll(target: String = Types[T::class]): Sequence { + return plugins.asSequence().flatMap { it.provideAll(target) }.filterIsInstance() } /** diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginTag.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginTag.kt index a45b66ab..25164c72 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginTag.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginTag.kt @@ -44,6 +44,8 @@ data class PluginTag( companion object { + const val DATAFORGE_GROUP = "hep.dataforge" + /** * Build new PluginTag from standard string representation * diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Provider.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Provider.kt index 7d37d609..967ee43b 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Provider.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Provider.kt @@ -85,5 +85,13 @@ inline fun Provider.provide(target: String, name: String): T? * [Sequence] of all elements with given target */ fun Provider.provideAll(target: String): Sequence { - return listTop(target).map { it -> provideTop(target, it) ?: error("The element $it is declared but not provided") } + return listTop(target).map { provideTop(target, it) ?: error("The element $it is declared but not provided") } +} + +/** + * Provide an object with given name inferring target from its type using [Type] annotation + */ +inline fun Provider.provideByType(name: String): T? { + val target = Types[T::class] + return provide(target, name) } diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Types.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Types.kt new file mode 100644 index 00000000..97f22aa6 --- /dev/null +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Types.kt @@ -0,0 +1,26 @@ +package hep.dataforge.provider + +import kotlin.reflect.KClass + +/** + * A text label for internal DataForge type classification. Alternative for mime container type. + * + * The DataForge type notation presumes that type `A.B.C` is the subtype of `A.B` + */ +@MustBeDocumented +@Target(AnnotationTarget.CLASS) +annotation class Type(val id: String) + +/** + * Utils to get type of classes and objects + */ +object Types { + operator fun get(cl: KClass<*>): String { + return cl.annotations.filterIsInstance().firstOrNull()?.id ?: cl.simpleName ?: "" + } + + operator fun get(obj: Any): String { + return get(obj::class) + } +} + diff --git a/dataforge-io/build.gradle b/dataforge-io/build.gradle deleted file mode 100644 index 570e9d28..00000000 --- a/dataforge-io/build.gradle +++ /dev/null @@ -1,32 +0,0 @@ -plugins { - id("org.jetbrains.kotlin.multiplatform") -} - -repositories { - jcenter() -} - -kotlin { - targets { - fromPreset(presets.jvm, 'jvm') - fromPreset(presets.js, 'js') - // For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64 - // For Linux, preset should be changed to e.g. presets.linuxX64 - // For MacOS, preset should be changed to e.g. presets.macosX64 - //fromPreset(presets.iosX64, 'ios') - } - sourceSets { - commonMain { - dependencies { - api project(":dataforge-context") - api project(":dataforge-meta-io") - } - } - - jvmMain { - dependencies { - - } - } - } -} \ No newline at end of file diff --git a/dataforge-io/build.gradle.kts b/dataforge-io/build.gradle.kts new file mode 100644 index 00000000..e0ae01c6 --- /dev/null +++ b/dataforge-io/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + kotlin("multiplatform") +} + +repositories { + jcenter() +} + +kotlin { + jvm() + js() + sourceSets { + val commonMain by getting{ + dependencies { + api(project(":dataforge-context")) + api(project(":dataforge-meta-io")) + } + } + } +} \ No newline at end of file diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/OutputManager.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/OutputManager.kt index 3b454391..3a96c458 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/OutputManager.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/OutputManager.kt @@ -1,10 +1,14 @@ package hep.dataforge.io +import hep.dataforge.context.AbstractPlugin import hep.dataforge.context.Plugin +import hep.dataforge.context.PluginTag +import hep.dataforge.context.PluginTag.Companion.DATAFORGE_GROUP import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.Meta import hep.dataforge.names.EmptyName import hep.dataforge.names.Name +import kotlinx.coroutines.CoroutineDispatcher import kotlin.reflect.KClass /** @@ -38,3 +42,22 @@ inline fun OutputManager.typed( ): Output { return typed(T::class, name, stage, meta) } + +/** + * System console output. + * The [ConsoleOutput] is used when no other [OutputManager] is provided. + */ +expect val ConsoleOutput: Output + +object ConsoleOutputManager : AbstractPlugin(), OutputManager { + override val tag: PluginTag = PluginTag("output.console", group = DATAFORGE_GROUP) + + override fun get(name: Name, stage: Name, meta: Meta): Output = ConsoleOutput + + override fun typed(type: KClass, name: Name, stage: Name, meta: Meta): Output = ConsoleOutput +} + +/** + * A dispatcher for output tasks. + */ +expect val OutputDispatcher : CoroutineDispatcher \ No newline at end of file diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/TextOutput.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/TextOutput.kt new file mode 100644 index 00000000..f094eff4 --- /dev/null +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/TextOutput.kt @@ -0,0 +1,59 @@ +package hep.dataforge.io + +import hep.dataforge.context.Context +import hep.dataforge.context.provideAll +import hep.dataforge.meta.Meta +import kotlinx.coroutines.launch +import kotlin.reflect.KClass + +class TextOutput(override val context: Context, private val output: kotlinx.io.core.Output) : Output { + private val cache = HashMap, TextRenderer>() + + /** + * Find the first [TextRenderer] matching the given object type. + */ + override fun render(obj: Any, meta: Meta) { + val renderer: TextRenderer = if (obj is CharSequence) { + DefaultTextRenderer + } else { + val value = cache[obj::class] + if (value == null) { + val answer = context.provideAll().filter { it.type.isInstance(obj) }.firstOrNull() + if (answer != null) { + cache[obj::class] = answer + answer + } else { + DefaultTextRenderer + } + } else { + value + } + } + context.launch(OutputDispatcher) { + renderer.run { output.render(obj) } + } + } +} + +interface TextRenderer { + /** + * The priority of this renderer compared to other renderers + */ + val priority: Int + /** + * The type of the content served by this renderer + */ + val type: KClass<*> + + suspend fun kotlinx.io.core.Output.render(obj: Any) +} + +object DefaultTextRenderer : TextRenderer { + override val priority: Int = Int.MAX_VALUE + override val type: KClass<*> = Any::class + + override suspend fun kotlinx.io.core.Output.render(obj: Any) { + append(obj.toString()) + append('\n') + } +} \ No newline at end of file diff --git a/dataforge-io/src/jsMain/kotlin/hep/dataforge/io/ConsoleOutput.kt b/dataforge-io/src/jsMain/kotlin/hep/dataforge/io/ConsoleOutput.kt new file mode 100644 index 00000000..3aed2000 --- /dev/null +++ b/dataforge-io/src/jsMain/kotlin/hep/dataforge/io/ConsoleOutput.kt @@ -0,0 +1,22 @@ +package hep.dataforge.io + +import hep.dataforge.context.Context +import hep.dataforge.context.Global +import hep.dataforge.meta.Meta +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers + +/** + * System console output. + * The [ConsoleOutput] is used when no other [OutputManager] is provided. + */ +actual val ConsoleOutput: Output = object : Output { + override fun render(obj: Any, meta: Meta) { + println(obj) + } + + override val context: Context get() = Global + +} + +actual val OutputDispatcher: CoroutineDispatcher = Dispatchers.Default \ No newline at end of file diff --git a/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/ConsoleOutput.kt b/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/ConsoleOutput.kt new file mode 100644 index 00000000..b4f28887 --- /dev/null +++ b/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/ConsoleOutput.kt @@ -0,0 +1,13 @@ +package hep.dataforge.io + +import hep.dataforge.context.Global +import kotlinx.coroutines.Dispatchers +import kotlinx.io.streams.asOutput + +/** + * System console output. + * The [ConsoleOutput] is used when no other [OutputManager] is provided. + */ +actual val ConsoleOutput: Output = TextOutput(Global, System.out.asOutput()) + +actual val OutputDispatcher = Dispatchers.IO \ No newline at end of file diff --git a/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/TextOutput.kt b/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/TextOutput.kt deleted file mode 100644 index e5ca55aa..00000000 --- a/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/TextOutput.kt +++ /dev/null @@ -1,16 +0,0 @@ -package hep.dataforge.io - -import hep.dataforge.context.Context -import hep.dataforge.meta.Meta -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch - - -class TextOutput(override val context: Context, private val output: kotlinx.io.core.Output) : Output { - override fun render(obj: Any, meta: Meta) { - context.launch(Dispatchers.IO) { - output.append(obj.toString()) - output.append('\n') - } - } -} \ No newline at end of file From 012ee93ab2090b2160e5fa843c507c677f8cc614 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 30 Jan 2019 18:57:14 +0300 Subject: [PATCH 24/37] Basic design for workspaces --- .../kotlin/hep/dataforge/context/Context.kt | 14 +- .../kotlin/hep/dataforge/provider/Provider.kt | 8 +- .../kotlin/hep/dataforge/provider/Type.kt | 10 + .../kotlin/hep/dataforge/provider/Types.kt | 26 --- .../kotlin/hep/dataforge/provider/Types.kt | 37 ++++ dataforge-data/build.gradle | 38 ---- dataforge-data/build.gradle.kts | 30 +++ .../kotlin/hep/dataforge/data/Action.kt | 2 +- .../kotlin/hep/dataforge/data/Data.kt | 172 +--------------- .../kotlin/hep/dataforge/data/DataFilter.kt | 46 +++++ .../kotlin/hep/dataforge/data/DataNode.kt | 192 ++++++++++++++++++ .../kotlin/hep/dataforge/data/_Data.kt | 8 + .../kotlin/hep/dataforge/io/TextOutput.kt | 12 +- .../kotlin/hep/dataforge/meta/Meta.kt | 1 + .../hep/dataforge/meta/MutableMetaNode.kt | 3 +- .../hep/dataforge/meta/Specification.kt | 25 ++- .../kotlin/hep/dataforge/names/Name.kt | 4 +- .../kotlin/hep/dataforge/values/Value.kt | 2 +- dataforge-workspace/build.gradle | 25 --- dataforge-workspace/build.gradle.kts | 16 ++ .../hep/dataforge/workspace/Dependency.kt | 51 +++++ .../kotlin/hep/dataforge/workspace/Task.kt | 52 +++++ .../hep/dataforge/workspace/TaskModel.kt | 97 +++++++++ .../hep/dataforge/workspace/Workspace.kt | 55 +++++ settings.gradle.kts | 3 +- 25 files changed, 637 insertions(+), 292 deletions(-) create mode 100644 dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Type.kt delete mode 100644 dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Types.kt create mode 100644 dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/Types.kt delete mode 100644 dataforge-data/build.gradle create mode 100644 dataforge-data/build.gradle.kts create mode 100644 dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataFilter.kt create mode 100644 dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt create mode 100644 dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/_Data.kt delete mode 100644 dataforge-workspace/build.gradle create mode 100644 dataforge-workspace/build.gradle.kts create mode 100644 dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt create mode 100644 dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt create mode 100644 dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt create mode 100644 dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt index a9366c35..63483f91 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt @@ -4,8 +4,6 @@ import hep.dataforge.meta.* import hep.dataforge.names.Name import hep.dataforge.names.toName import hep.dataforge.provider.Provider -import hep.dataforge.provider.Types -import hep.dataforge.provider.provideAll import hep.dataforge.values.Value import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -54,7 +52,7 @@ interface Context : Named, MetaRepr, Provider, CoroutineScope { override fun provideTop(target: String, name: Name): Any? { return when (target) { Plugin.PLUGIN_TARGET -> plugins[PluginTag.fromString(name.toString())] - Value.VALUE_TARGET -> properties[name]?.value + Value.TYPE -> properties[name]?.value else -> null } } @@ -62,7 +60,7 @@ interface Context : Named, MetaRepr, Provider, CoroutineScope { override fun listTop(target: String): Sequence { return when (target) { Plugin.PLUGIN_TARGET -> plugins.asSequence().map { it.name.toName() } - Value.VALUE_TARGET -> properties.asValueSequence().map { it.first } + Value.TYPE -> properties.asValueSequence().map { it.first } else -> emptySequence() } } @@ -92,15 +90,9 @@ interface Context : Named, MetaRepr, Provider, CoroutineScope { } } -/** - * A sequences of all objects provided by plugins with given target and type - */ -inline fun Context.provideAll(target: String = Types[T::class]): Sequence { - return plugins.asSequence().flatMap { it.provideAll(target) }.filterIsInstance() -} /** - * A global root context + * A global root context. Closing [Global] terminates the framework. */ expect object Global : Context { fun getContext(name: String): Context diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Provider.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Provider.kt index 967ee43b..79b94dce 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Provider.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Provider.kt @@ -88,10 +88,4 @@ fun Provider.provideAll(target: String): Sequence { return listTop(target).map { provideTop(target, it) ?: error("The element $it is declared but not provided") } } -/** - * Provide an object with given name inferring target from its type using [Type] annotation - */ -inline fun Provider.provideByType(name: String): T? { - val target = Types[T::class] - return provide(target, name) -} + diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Type.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Type.kt new file mode 100644 index 00000000..a31f1fdb --- /dev/null +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Type.kt @@ -0,0 +1,10 @@ +package hep.dataforge.provider + +/** + * A text label for internal DataForge type classification. Alternative for mime container type. + * + * The DataForge type notation presumes that type `A.B.C` is the subtype of `A.B` + */ +@MustBeDocumented +@Target(AnnotationTarget.CLASS) +annotation class Type(val id: String) diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Types.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Types.kt deleted file mode 100644 index 97f22aa6..00000000 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Types.kt +++ /dev/null @@ -1,26 +0,0 @@ -package hep.dataforge.provider - -import kotlin.reflect.KClass - -/** - * A text label for internal DataForge type classification. Alternative for mime container type. - * - * The DataForge type notation presumes that type `A.B.C` is the subtype of `A.B` - */ -@MustBeDocumented -@Target(AnnotationTarget.CLASS) -annotation class Type(val id: String) - -/** - * Utils to get type of classes and objects - */ -object Types { - operator fun get(cl: KClass<*>): String { - return cl.annotations.filterIsInstance().firstOrNull()?.id ?: cl.simpleName ?: "" - } - - operator fun get(obj: Any): String { - return get(obj::class) - } -} - diff --git a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/Types.kt b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/Types.kt new file mode 100644 index 00000000..df283776 --- /dev/null +++ b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/Types.kt @@ -0,0 +1,37 @@ +package hep.dataforge.provider + +import hep.dataforge.context.Context +import kotlin.reflect.KClass +import kotlin.reflect.full.findAnnotation + + +object Types { + operator fun get(cl: KClass<*>): String { + return cl.findAnnotation()?.id ?: cl.simpleName ?: "" + } + + operator fun get(obj: Any): String { + return get(obj::class) + } +} + +/** + * Provide an object with given name inferring target from its type using [Type] annotation + */ +inline fun Provider.provideByType(name: String): T? { + val target = Types[T::class] + return provide(target, name) +} + +inline fun Provider.provideAllByType(): Sequence { + val target = Types[T::class] + return provideAll(target).filterIsInstance() +} + +/** + * A sequences of all objects provided by plugins with given target and type + */ +inline fun Context.components(): Sequence { + return plugins.asSequence().flatMap { it.provideAll(Types[T::class]) }.filterIsInstance() +} + diff --git a/dataforge-data/build.gradle b/dataforge-data/build.gradle deleted file mode 100644 index 10205fb1..00000000 --- a/dataforge-data/build.gradle +++ /dev/null @@ -1,38 +0,0 @@ -plugins { - id "org.jetbrains.kotlin.multiplatform" -} - -repositories { - jcenter() -} - -kotlin { - targets { - fromPreset(presets.jvm, 'jvm') - fromPreset(presets.js, 'js') - // For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64 - // For Linux, preset should be changed to e.g. presets.linuxX64 - // For MacOS, preset should be changed to e.g. presets.macosX64 - //fromPreset(presets.iosX64, 'ios') - } - sourceSets { - commonMain { - dependencies { - api project(":dataforge-meta") - api "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$coroutinesVersion" - } - } - - jvmMain { - dependencies { - api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion" - } - } - - jsMain { - dependencies { - api "org.jetbrains.kotlinx:kotlinx-coroutines-core-js:$coroutinesVersion" - } - } - } -} \ No newline at end of file diff --git a/dataforge-data/build.gradle.kts b/dataforge-data/build.gradle.kts new file mode 100644 index 00000000..eb6b3669 --- /dev/null +++ b/dataforge-data/build.gradle.kts @@ -0,0 +1,30 @@ +plugins { + kotlin("multiplatform") +} + +val coroutinesVersion: String by rootProject.extra + +kotlin { + jvm() + js() + sourceSets { + val commonMain by getting{ + dependencies { + api(project(":dataforge-meta")) + api("org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$coroutinesVersion") + } + } + + val jvmMain by getting{ + dependencies { + api("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion") + } + } + + val jsMain by getting{ + dependencies { + api("org.jetbrains.kotlinx:kotlinx-coroutines-core-js:$coroutinesVersion") + } + } + } +} \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt index 2a8e2fb6..599101c0 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt @@ -38,7 +38,7 @@ infix fun Action.then(action: Action): A */ class PipeAction(val transform: (Name, Data, Meta) -> Data?) : Action { override fun invoke(node: DataNode, meta: Meta): DataNode = DataNode.build { - node.asSequence().forEach { (name, data) -> + node.dataSequence().forEach { (name, data) -> val res = transform(name, data, meta) if (res != null) { set(name, res) diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt index 6d74a788..6096a799 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt @@ -2,10 +2,6 @@ package hep.dataforge.data import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaRepr -import hep.dataforge.names.Name -import hep.dataforge.names.NameToken -import hep.dataforge.names.plus -import hep.dataforge.names.toName import kotlin.coroutines.CoroutineContext import kotlin.reflect.KClass @@ -30,6 +26,8 @@ interface Data : MetaRepr { override fun toMeta(): Meta = meta companion object { + const val TYPE = "data" + fun of(type: KClass, goal: Goal, meta: Meta): Data = DataImpl(type, goal, meta) inline fun of(goal: Goal, meta: Meta): Data = of(T::class, goal, meta) fun of(name: String, type: KClass, goal: Goal, meta: Meta): Data = @@ -43,6 +41,8 @@ interface Data : MetaRepr { } } +suspend fun Data.await(): T = goal.await() + /** * Generic Data implementation */ @@ -54,167 +54,3 @@ private class DataImpl( class NamedData(val name: String, data: Data) : Data by data -/** - * A tree-like data structure grouped into the node. All data inside the node must inherit its type - */ -interface DataNode { - /** - * Get the specific data if it exists - */ - operator fun get(name: Name): Data? - - /** - * Get a subnode with given name if it exists. - */ - fun getNode(name: Name): DataNode? - - /** - * Walk the tree upside down and provide all data nodes with full names - */ - fun asSequence(): Sequence>> - - operator fun iterator(): Iterator>> = asSequence().iterator() - - companion object { - fun build(block: DataTreeBuilder.() -> Unit) = DataTreeBuilder().apply(block).build() - } - -} - -internal sealed class DataTreeItem { - class Node(val tree: DataTree) : DataTreeItem() - class Value(val value: Data) : DataTreeItem() -} - -class DataTree internal constructor(private val items: Map>) : DataNode { - //TODO add node-level meta? - - override fun get(name: Name): Data? = when (name.length) { - 0 -> error("Empty name") - 1 -> (items[name.first()] as? DataTreeItem.Value)?.value - else -> getNode(name.first()!!.toName())?.get(name.cutFirst()) - } - - override fun getNode(name: Name): DataTree? = when (name.length) { - 0 -> this - 1 -> (items[name.first()] as? DataTreeItem.Node)?.tree - else -> getNode(name.first()!!.toName())?.getNode(name.cutFirst()) - } - - override fun asSequence(): Sequence>> { - return kotlin.sequences.sequence { - items.forEach { (head, tree) -> - when (tree) { - is DataTreeItem.Value -> yield(head.toName() to tree.value) - is DataTreeItem.Node -> { - val subSequence = tree.tree.asSequence().map { (name, data) -> (head.toName() + name) to data } - yieldAll(subSequence) - } - } - } - } - } -} - -private sealed class DataTreeBuilderItem { - class Node(val tree: DataTreeBuilder) : DataTreeBuilderItem() - class Value(val value: Data) : DataTreeBuilderItem() -} - -/** - * A builder for a DataTree. - */ -class DataTreeBuilder { - private val map = HashMap>() - - operator fun set(token: NameToken, node: DataTreeBuilder) { - if (map.containsKey(token)) error("Tree entry with name $token is not empty") - map[token] = DataTreeBuilderItem.Node(node) - } - - operator fun set(token: NameToken, data: Data) { - if (map.containsKey(token)) error("Tree entry with name $token is not empty") - map[token] = DataTreeBuilderItem.Value(data) - } - - private fun buildNode(token: NameToken): DataTreeBuilder { - return if (!map.containsKey(token)) { - DataTreeBuilder().also { map[token] = DataTreeBuilderItem.Node(it) } - } else { - (map[token] as? DataTreeBuilderItem.Node ?: error("The node with name $token is occupied by leaf")).tree - } - } - - private fun buildNode(name: Name): DataTreeBuilder { - return when (name.length) { - 0 -> this - 1 -> buildNode(name.first()!!) - else -> buildNode(name.first()!!).buildNode(name.cutFirst()) - } - } - - operator fun set(name: Name, data: Data) { - when (name.length) { - 0 -> error("Can't add data with empty name") - 1 -> set(name.first()!!, data) - 2 -> buildNode(name.cutLast())[name.last()!!] = data - } - } - - operator fun set(name: Name, node: DataTreeBuilder) { - when (name.length) { - 0 -> error("Can't add data with empty name") - 1 -> set(name.first()!!, node) - 2 -> buildNode(name.cutLast())[name.last()!!] = node - } - } - - operator fun set(name: Name, node: DataNode) = set(name, node.builder()) - - /** - * Append data to node - */ - infix fun String.to(data: Data) = set(toName(), data) - - /** - * Append node - */ - infix fun String.to(node: DataNode) = set(toName(), node) - - /** - * Build and append node - */ - infix fun String.to(block: DataTreeBuilder.() -> Unit) = set(toName(), DataTreeBuilder().apply(block)) - - fun build(): DataTree { - val resMap = map.mapValues { (_, value) -> - when (value) { - is DataTreeBuilderItem.Value -> DataTreeItem.Value(value.value) - is DataTreeBuilderItem.Node -> DataTreeItem.Node(value.tree.build()) - } - } - return DataTree(resMap) - } -} - -/** - * Generate a mutable builder from this node. Node content is not changed - */ -fun DataNode.builder(): DataTreeBuilder = DataTreeBuilder().apply { - asSequence().forEach { (name, data) -> this[name] = data } -} - -/** - * Start computation for all goals in data node - */ -fun DataNode<*>.startAll() = asSequence().forEach { (_, data) -> data.goal.start() } - -fun DataNode.filter(predicate: (Name, Data) -> Boolean): DataNode = DataNode.build { - asSequence().forEach { (name, data) -> - if (predicate(name, data)) { - this[name] = data - } - } -} - -//fun DataNode.filterIsInstance(type: KClass): DataNode = filter{_,data -> type.} \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataFilter.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataFilter.kt new file mode 100644 index 00000000..6f230c0f --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataFilter.kt @@ -0,0 +1,46 @@ +package hep.dataforge.data + +import hep.dataforge.meta.* +import hep.dataforge.names.toName + + +class DataFilter(override val config: Config) : Specification { + var from by string() + var to by string() + var pattern by string("*.") +// val prefix by string() +// val suffix by string() + + companion object : SpecificationCompanion { + override fun wrap(config: Config): DataFilter = DataFilter(config) + } +} + +/** + * Apply meta-based filter to given data node + */ +fun DataNode.filter(filter: DataFilter): DataNode { + val sourceNode = filter.from?.let { getNode(it.toName()) } ?: this@filter + val regex = filter.pattern.toRegex() + val targetNode = DataTreeBuilder().apply { + sourceNode.dataSequence().forEach { (name, data) -> + if (name.toString().matches(regex)) { + this[name] = data + } + } + } + return filter.to?.let { + DataTreeBuilder().apply { this[it.toName()] = targetNode }.build() + } ?: targetNode.build() +} + +/** + * Filter data using [DataFilter] specification + */ +fun DataNode.filter(filter: Meta): DataNode = filter(DataFilter.wrap(filter)) + +/** + * Filter data using [DataFilter] builder + */ +fun DataNode.filter(filterBuilder: DataFilter.() -> Unit): DataNode = + filter(DataFilter.build(filterBuilder)) \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt new file mode 100644 index 00000000..bf8f6f81 --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt @@ -0,0 +1,192 @@ +package hep.dataforge.data + +import hep.dataforge.names.Name +import hep.dataforge.names.NameToken +import hep.dataforge.names.plus +import hep.dataforge.names.toName + +/** + * A tree-like data structure grouped into the node. All data inside the node must inherit its type + */ +interface DataNode { + /** + * Get the specific data if it exists + */ + operator fun get(name: Name): Data? + + /** + * Get a subnode with given name if it exists. + */ + fun getNode(name: Name): DataNode? + + /** + * Walk the tree upside down and provide all data nodes with full names + */ + fun dataSequence(): Sequence>> + + /** + * A sequence of all nodes in the tree walking upside down, excluding self + */ + fun nodeSequence(): Sequence>> + + operator fun iterator(): Iterator>> = dataSequence().iterator() + + companion object { + const val TYPE = "dataNode" + + fun build(block: DataTreeBuilder.() -> Unit) = DataTreeBuilder().apply(block).build() + } + +} + +internal sealed class DataTreeItem { + class Node(val tree: DataTree) : DataTreeItem() + class Value(val value: Data) : DataTreeItem() +} + +class DataTree internal constructor(private val items: Map>) : DataNode { + //TODO add node-level meta? + + override fun get(name: Name): Data? = when (name.length) { + 0 -> error("Empty name") + 1 -> (items[name.first()] as? DataTreeItem.Value)?.value + else -> getNode(name.first()!!.toName())?.get(name.cutFirst()) + } + + override fun getNode(name: Name): DataTree? = when (name.length) { + 0 -> this + 1 -> (items[name.first()] as? DataTreeItem.Node)?.tree + else -> getNode(name.first()!!.toName())?.getNode(name.cutFirst()) + } + + override fun dataSequence(): Sequence>> { + return sequence { + items.forEach { (head, tree) -> + when (tree) { + is DataTreeItem.Value -> yield(head.toName() to tree.value) + is DataTreeItem.Node -> { + val subSequence = + tree.tree.dataSequence().map { (name, data) -> (head.toName() + name) to data } + yieldAll(subSequence) + } + } + } + } + } + + override fun nodeSequence(): Sequence>> { + return sequence { + items.forEach { (head, tree) -> + if (tree is DataTreeItem.Node) { + yield(head.toName() to tree.tree) + val subSequence = + tree.tree.nodeSequence().map { (name, node) -> (head.toName() + name) to node } + yieldAll(subSequence) + } + } + } + } +} + +private sealed class DataTreeBuilderItem { + class Node(val tree: DataTreeBuilder) : DataTreeBuilderItem() + class Value(val value: Data) : DataTreeBuilderItem() +} + +/** + * A builder for a DataTree. + */ +class DataTreeBuilder { + private val map = HashMap>() + + operator fun set(token: NameToken, node: DataTreeBuilder) { + if (map.containsKey(token)) error("Tree entry with name $token is not empty") + map[token] = DataTreeBuilderItem.Node(node) + } + + operator fun set(token: NameToken, data: Data) { + if (map.containsKey(token)) error("Tree entry with name $token is not empty") + map[token] = DataTreeBuilderItem.Value(data) + } + + private fun buildNode(token: NameToken): DataTreeBuilder { + return if (!map.containsKey(token)) { + DataTreeBuilder().also { map[token] = DataTreeBuilderItem.Node(it) } + } else { + (map[token] as? DataTreeBuilderItem.Node ?: error("The node with name $token is occupied by leaf")).tree + } + } + + private fun buildNode(name: Name): DataTreeBuilder { + return when (name.length) { + 0 -> this + 1 -> buildNode(name.first()!!) + else -> buildNode(name.first()!!).buildNode(name.cutFirst()) + } + } + + operator fun set(name: Name, data: Data) { + when (name.length) { + 0 -> error("Can't add data with empty name") + 1 -> set(name.first()!!, data) + 2 -> buildNode(name.cutLast())[name.last()!!] = data + } + } + + operator fun set(name: Name, node: DataTreeBuilder) { + when (name.length) { + 0 -> error("Can't add data with empty name") + 1 -> set(name.first()!!, node) + 2 -> buildNode(name.cutLast())[name.last()!!] = node + } + } + + operator fun set(name: Name, node: DataNode) = set(name, node.builder()) + + /** + * Append data to node + */ + infix fun String.to(data: Data) = set(toName(), data) + + /** + * Append node + */ + infix fun String.to(node: DataNode) = set(toName(), node) + + /** + * Build and append node + */ + infix fun String.to(block: DataTreeBuilder.() -> Unit) = set(toName(), DataTreeBuilder().apply(block)) + + fun build(): DataTree { + val resMap = map.mapValues { (_, value) -> + when (value) { + is DataTreeBuilderItem.Value -> DataTreeItem.Value(value.value) + is DataTreeBuilderItem.Node -> DataTreeItem.Node(value.tree.build()) + } + } + return DataTree(resMap) + } +} + +/** + * Generate a mutable builder from this node. Node content is not changed + */ +fun DataNode.builder(): DataTreeBuilder = DataTreeBuilder().apply { + dataSequence().forEach { (name, data) -> this[name] = data } +} + +/** + * Start computation for all goals in data node + */ +fun DataNode<*>.startAll() = dataSequence().forEach { (_, data) -> data.goal.start() } + +fun DataNode.filter(predicate: (Name, Data) -> Boolean): DataNode = DataNode.build { + dataSequence().forEach { (name, data) -> + if (predicate(name, data)) { + this[name] = data + } + } +} + +//fun DataNode.filterIsInstance(type: KClass): DataNode = filter{_,data -> type.} \ No newline at end of file diff --git a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/_Data.kt b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/_Data.kt new file mode 100644 index 00000000..00c9e656 --- /dev/null +++ b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/_Data.kt @@ -0,0 +1,8 @@ +package hep.dataforge.data + +import kotlinx.coroutines.runBlocking + +/** + * Block the thread and get data content + */ +fun Data.get(): T = runBlocking { await() } \ No newline at end of file diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/TextOutput.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/TextOutput.kt index f094eff4..446eed31 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/TextOutput.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/TextOutput.kt @@ -1,8 +1,10 @@ package hep.dataforge.io import hep.dataforge.context.Context -import hep.dataforge.context.provideAll +import hep.dataforge.io.TextRenderer.Companion.TEXT_RENDERER_TYPE import hep.dataforge.meta.Meta +import hep.dataforge.provider.Type +import hep.dataforge.provider.provideAll import kotlinx.coroutines.launch import kotlin.reflect.KClass @@ -18,7 +20,8 @@ class TextOutput(override val context: Context, private val output: kotlinx.io.c } else { val value = cache[obj::class] if (value == null) { - val answer = context.provideAll().filter { it.type.isInstance(obj) }.firstOrNull() + val answer = context.provideAll(TEXT_RENDERER_TYPE).filterIsInstance() + .filter { it.type.isInstance(obj) }.firstOrNull() if (answer != null) { cache[obj::class] = answer answer @@ -35,6 +38,7 @@ class TextOutput(override val context: Context, private val output: kotlinx.io.c } } +@Type(TEXT_RENDERER_TYPE) interface TextRenderer { /** * The priority of this renderer compared to other renderers @@ -46,6 +50,10 @@ interface TextRenderer { val type: KClass<*> suspend fun kotlinx.io.core.Output.render(obj: Any) + + companion object { + const val TEXT_RENDERER_TYPE = "dataforge.textRenderer" + } } object DefaultTextRenderer : TextRenderer { diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt index 56590dbd..bcc777c1 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -42,6 +42,7 @@ interface Meta : MetaRepr { override fun toMeta(): Meta = this companion object { + const val TYPE = "meta" /** * A key for single value node */ diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt index fe48a299..31998b46 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt @@ -161,7 +161,8 @@ fun > M.setIndexed( metas: Iterable, queryFactory: (Int) -> String = { it.toString() } ) { - setIndexed(name, metas.map { wrap(name, it) }, queryFactory) + setIndexed(name, metas.map { MetaItem.NodeItem(wrap(name, it)) }, queryFactory) } operator fun > M.set(name: Name, metas: Iterable) = setIndexed(name, metas) +operator fun > M.set(name: String, metas: Iterable) = setIndexed(name.toName(), metas) 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 bdd11429..fb3e909f 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt @@ -8,47 +8,52 @@ interface Specification : Configurable { } /** - * Specification allows to apply custom configuration in a type safe way to simple untyped configuration + * Allows to apply custom configuration in a type safe way to simple untyped configuration. + * By convention [Specification] companion should inherit this class + * */ -interface SpecificationBuilder { +interface SpecificationCompanion { /** * Update given configuration using given type as a builder */ - fun update(config: Config, action: T.() -> Unit) { - wrap(config).apply(action) + fun update(config: Config, action: T.() -> Unit): T { + return wrap(config).apply(action) } + fun build(action: T.() -> Unit) = update(Config(), action) + /** * Wrap generic configuration producing instance of desired type */ fun wrap(config: Config): T fun wrap(meta: Meta): T = wrap(meta.toConfig()) + } -fun specification(wrapper: (Config) -> T): SpecificationBuilder = - object : SpecificationBuilder { +fun specification(wrapper: (Config) -> T): SpecificationCompanion = + object : SpecificationCompanion { override fun wrap(config: Config): T = wrapper(config) } /** * Apply specified configuration to configurable */ -fun > T.configure(spec: S, action: C.() -> Unit) = +fun > T.configure(spec: S, action: C.() -> Unit) = apply { spec.update(config, action) } /** * Update configuration using given specification */ -fun > Specification.update(spec: S, action: C.() -> Unit) = +fun > Specification.update(spec: S, action: C.() -> Unit) = apply { spec.update(config, action) } /** * Create a style based on given specification */ -fun > S.createStyle(action: C.() -> Unit): Meta = +fun > S.createStyle(action: C.() -> Unit): Meta = Config().also { update(it, action) } -fun Specification.spec(spec: SpecificationBuilder, key: String? = null) = +fun Specification.spec(spec: SpecificationCompanion, key: String? = null) = ChildConfigDelegate(key) { spec.wrap(config) } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt index b162e5f3..78dba871 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt @@ -103,4 +103,6 @@ operator fun Name.plus(other: String): Name = this + other.toName() fun NameToken.toName() = Name(listOf(this)) -val EmptyName = Name(emptyList()) \ No newline at end of file +val EmptyName = Name(emptyList()) + +fun Name.isEmpty(): Boolean = this.length == 0 \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt index 53cc1996..2f92bd60 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt @@ -44,7 +44,7 @@ interface Value { get() = listOf(this) companion object { - const val VALUE_TARGET = "value" + const val TYPE = "value" /** * Convert object to value diff --git a/dataforge-workspace/build.gradle b/dataforge-workspace/build.gradle deleted file mode 100644 index a0011494..00000000 --- a/dataforge-workspace/build.gradle +++ /dev/null @@ -1,25 +0,0 @@ -plugins { - id "org.jetbrains.kotlin.multiplatform" -} - -repositories { - jcenter() -} - -kotlin { - targets { - fromPreset(presets.jvm, 'jvm') - //fromPreset(presets.js, 'js') - // For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64 - // For Linux, preset should be changed to e.g. presets.linuxX64 - // For MacOS, preset should be changed to e.g. presets.macosX64 - //fromPreset(presets.iosX64, 'ios') - } - sourceSets { - commonMain { - dependencies { - api project(":dataforge-context") - } - } - } -} \ No newline at end of file diff --git a/dataforge-workspace/build.gradle.kts b/dataforge-workspace/build.gradle.kts new file mode 100644 index 00000000..cb2ce82c --- /dev/null +++ b/dataforge-workspace/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + kotlin("multiplatform") +} + +kotlin { + jvm() + js() + sourceSets { + val commonMain by getting{ + dependencies { + api(project(":dataforge-context")) + api(project(":dataforge-data")) + } + } + } +} \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt new file mode 100644 index 00000000..0fedc3e9 --- /dev/null +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt @@ -0,0 +1,51 @@ +package hep.dataforge.workspace + +import hep.dataforge.data.DataFilter +import hep.dataforge.data.DataNode +import hep.dataforge.data.DataTreeBuilder +import hep.dataforge.data.filter +import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaRepr +import hep.dataforge.meta.buildMeta +import hep.dataforge.names.EmptyName +import hep.dataforge.names.Name +import hep.dataforge.names.isEmpty + +/** + * A dependency of the task which allows to lazily create a data tree for single dependency + */ +sealed class Dependency : MetaRepr { + abstract fun apply(workspace: Workspace): DataNode +} + +class DataDependency(val filter: DataFilter) : Dependency() { + override fun apply(workspace: Workspace): DataNode = + workspace.data.filter(filter) + + override fun toMeta(): Meta = filter.config + + companion object { + val all: DataDependency = DataDependency(DataFilter.build { }) + } +} + +class TaskModelDependency(val name: String, val meta: Meta, val placement: Name = EmptyName) : Dependency() { + override fun apply(workspace: Workspace): DataNode { + val model = + workspace.tasks[name]?.build(workspace, meta) ?: error("Task with name $name not found in $workspace") + + val task = workspace.tasks[model.name] ?: error("Task with name ${model.name} is not found in the workspace") + if (task.isTerminal) TODO("Support terminal task") + val result = task.run(model) + return if (placement.isEmpty()) { + result + } else { + DataTreeBuilder().apply { this[placement] = result }.build() + } + } + + override fun toMeta(): Meta = buildMeta { + "name" to name + "meta" to meta + } +} \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt new file mode 100644 index 00000000..bb6d33f3 --- /dev/null +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt @@ -0,0 +1,52 @@ +package hep.dataforge.workspace + +import hep.dataforge.context.Named +import hep.dataforge.data.DataNode +import hep.dataforge.meta.Meta +import hep.dataforge.provider.Type +import hep.dataforge.workspace.Task.Companion.TYPE +import kotlin.reflect.KClass + +@Type(TYPE) +interface Task : Named { + /** + * Terminal task is the one that could not build model lazily + */ + val isTerminal: Boolean get() = false + + /** + * The explicit type of the node returned by the task + */ + val type: KClass + + /** + * Build a model for this task + * + * @param workspace + * @param taskConfig + * @return + */ + fun build(workspace: Workspace, taskConfig: Meta): TaskModel + + /** + * Check if the model is valid and is acceptable by the task. Throw exception if not. + * + * @param model + */ + fun validate(model: TaskModel) { + if(this.name != model.name) error("The task $name could not be run with model from task ${model.name}") + } + + /** + * Run given task model. Type check expected to be performed before actual + * calculation. + * + * @param model + * @return + */ + fun run(model: TaskModel): DataNode + + companion object { + const val TYPE = "task" + } +} \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt new file mode 100644 index 00000000..8d0de0c0 --- /dev/null +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt @@ -0,0 +1,97 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package hep.dataforge.workspace + +import hep.dataforge.data.DataFilter +import hep.dataforge.data.DataTree +import hep.dataforge.data.DataTreeBuilder +import hep.dataforge.meta.* +import hep.dataforge.names.EmptyName +import hep.dataforge.names.Name +import hep.dataforge.names.toName + + +/** + * A model for task execution + * @param name the name of the task + * @param meta the meta for the task (not for the whole configuration) + * @param dependencies a list of direct dependencies for this task + */ +data class TaskModel( + val name: String, + val meta: Meta, + val dependencies: Collection +) : MetaRepr { + //TODO provide a way to get task descriptor + //TODO add pre-run check of task result type? + + override fun toMeta(): Meta = buildMeta { + "name" to name + "meta" to meta + "dependsOn" to { + val dataDependencies = dependencies.filterIsInstance() + val taskDependencies = dependencies.filterIsInstance() + setIndexed("data".toName(), dataDependencies.map { it.toMeta() }) + setIndexed("task".toName(), taskDependencies.map { it.toMeta() }) { taskDependencies[it].name } + //TODO ensure all dependencies are listed + } + } +} + +/** + * Build input for the task + */ +fun TaskModel.buildInput(workspace: Workspace): DataTree { + return DataTreeBuilder().apply { + dependencies.asSequence().flatMap { it.apply(workspace).dataSequence() }.forEach { (name, data) -> + //TODO add concise error on replacement + this[name] = data + } + }.build() +} + +/** + * A builder for [TaskModel] + */ +class TaskModelBuilder(val name: String, meta: Meta = EmptyMeta) { + /** + * Meta for current task. By default uses the whole input meta + */ + var meta: MetaBuilder = meta.builder() + val dependencies = HashSet() + + /** + * Add dependency for + */ + fun dependsOn(name: String, meta: Meta, placement: Name = EmptyName) { + dependencies.add(TaskModelDependency(name, meta, placement)) + } + + /** + * Add custom data dependency + */ + fun data(action: DataFilter.() -> Unit) { + dependencies.add(DataDependency(DataFilter.build(action))) + } + + /** + * User-friendly way to add data dependency + */ + fun data(pattern: String? = null, from: String? = null, to: String? = null) = data { + pattern?.let { this.pattern = it } + from?.let { this.from = it } + to?.let { this.to = it } + } + + /** + * Add all data as root node + */ + fun allData() { + dependencies.add(DataDependency.all) + } + + fun build(): TaskModel = TaskModel(name, meta.seal(), dependencies) +} diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt new file mode 100644 index 00000000..a7fbfea7 --- /dev/null +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt @@ -0,0 +1,55 @@ +package hep.dataforge.workspace + +import hep.dataforge.context.ContextAware +import hep.dataforge.context.Named +import hep.dataforge.data.Data +import hep.dataforge.data.DataNode +import hep.dataforge.meta.Meta +import hep.dataforge.names.Name +import hep.dataforge.names.toName +import hep.dataforge.provider.Provider +import hep.dataforge.provider.Type + + +@Type(Workspace.TYPE) +interface Workspace : ContextAware, Named, Provider { + /** + * The whole data node for current workspace + */ + val data: DataNode + + /** + * All targets associated with the workspace + */ + val targets: Map + + /** + * All tasks associated with the workspace + */ + val tasks: Map> + + override fun provideTop(target: String, name: Name): Any? { + return when (target) { + "target", Meta.TYPE -> targets[name.toString()] + Task.TYPE -> tasks[name.toString()] + Data.TYPE -> data[name] + DataNode.TYPE -> data.getNode(name) + else -> null + } + } + + override fun listTop(target: String): Sequence { + return when (target) { + "target", Meta.TYPE -> targets.keys.asSequence().map { it.toName() } + Task.TYPE -> tasks.keys.asSequence().map { it.toName() } + Data.TYPE -> data.dataSequence().map { it.first } + DataNode.TYPE -> data.nodeSequence().map { it.first } + else -> emptySequence() + } + } + + companion object { + const val TYPE = "workspace" + } +} + diff --git a/settings.gradle.kts b/settings.gradle.kts index 216dd001..670d7438 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,5 +13,6 @@ include( ":dataforge-meta-io", ":dataforge-context", ":dataforge-data", - ":dataforge-io" + ":dataforge-io", + ":dataforge-workspace" ) From a4a7163e0d38894d69b383588d6d3fe938e03613 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 31 Jan 2019 21:21:02 +0300 Subject: [PATCH 25/37] Basic design for workspaces --- .../hep/dataforge/context/AbstractPlugin.kt | 10 +- .../kotlin/hep/dataforge/context/Context.kt | 84 +++++++++++-- .../hep/dataforge/context/ContextBuilder.kt | 37 ++++++ .../kotlin/hep/dataforge/context/Plugin.kt | 6 +- .../hep/dataforge/context/PluginManager.kt | 11 +- .../hep/dataforge/context/PluginRepository.kt | 27 +++- .../kotlin/hep/dataforge/context/JSContext.kt | 75 ----------- .../hep/dataforge/context/PluginRepository.kt | 17 --- .../dataforge/context/ClassLoaderPlugin.kt | 118 ++++++++++++++++++ .../kotlin/hep/dataforge/context/Global.kt | 65 ---------- .../hep/dataforge/context/JVMContext.kt | 107 ---------------- .../hep/dataforge/context/PluginRepository.kt | 2 +- .../kotlin/hep/dataforge/provider/Types.kt | 5 +- .../kotlin/hep/dataforge/meta/Meta.kt | 4 +- .../hep/dataforge/workspace/Dependency.kt | 7 +- .../hep/dataforge/workspace/Workspace.kt | 37 +++++- .../dataforge/workspace/WorkspaceBuilder.kt | 40 ++++++ 17 files changed, 345 insertions(+), 307 deletions(-) create mode 100644 dataforge-context/src/commonMain/kotlin/hep/dataforge/context/ContextBuilder.kt delete mode 100644 dataforge-context/src/jsMain/kotlin/hep/dataforge/context/JSContext.kt create mode 100644 dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/ClassLoaderPlugin.kt delete mode 100644 dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/Global.kt delete mode 100644 dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt create mode 100644 dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/AbstractPlugin.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/AbstractPlugin.kt index ac726c1f..8864fae1 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/AbstractPlugin.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/AbstractPlugin.kt @@ -1,16 +1,16 @@ package hep.dataforge.context -import hep.dataforge.meta.EmptyMeta -import hep.dataforge.meta.Meta +import hep.dataforge.meta.Config import hep.dataforge.names.Name -abstract class AbstractPlugin(override val meta: Meta = EmptyMeta) : Plugin { - +abstract class AbstractPlugin : Plugin { private var _context: Context? = null override val context: Context get() = _context ?: error("Plugin $tag is not attached") + override val config = Config() + override fun attach(context: Context) { this._context = context } @@ -19,6 +19,8 @@ abstract class AbstractPlugin(override val meta: Meta = EmptyMeta) : Plugin { this._context = null } + //TODO make configuration activation-safe + override fun provideTop(target: String, name: Name): Any? = null override fun listTop(target: String): Sequence = emptySequence() diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt index 63483f91..d1ff801d 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt @@ -4,12 +4,14 @@ import hep.dataforge.meta.* import hep.dataforge.names.Name import hep.dataforge.names.toName import hep.dataforge.provider.Provider +import hep.dataforge.provider.provideAll import hep.dataforge.values.Value import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers import mu.KLogger import mu.KotlinLogging import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext +import kotlin.jvm.JvmName /** * The local environment for anything being done in DataForge framework. Contexts are organized into tree structure with [Global] at the top. @@ -23,29 +25,36 @@ import kotlin.coroutines.CoroutineContext * Since plugins could contain mutable state, context has two states: active and inactive. No changes are allowed to active context. * @author Alexander Nozik */ -interface Context : Named, MetaRepr, Provider, CoroutineScope { +open class Context(final override val name: String, val parent: Context? = Global) : Named, MetaRepr, Provider, + CoroutineScope { - val parent: Context? + private val config = Config() /** * Context properties. Working as substitute for environment variables */ - val properties: Meta + val properties: Meta = if (parent == null) { + config + } else { + Laminate(config, parent.properties) + } /** * Context logger */ - val logger: KLogger + val logger: KLogger = KotlinLogging.logger(name) /** * A [PluginManager] for current context */ - val plugins: PluginManager + val plugins: PluginManager by lazy { PluginManager(this) } + + private val activators = HashSet() /** * Defines if context is used in any kind of active computations. Active context properties and plugins could not be changed */ - val isActive: Boolean + val isActive: Boolean = activators.isNotEmpty() override val defaultTarget: String get() = Plugin.PLUGIN_TARGET @@ -68,20 +77,36 @@ interface Context : Named, MetaRepr, Provider, CoroutineScope { /** * Mark context as active and used by [activator] */ - fun activate(activator: Any) + fun activate(activator: Any) { + activators.add(activator) + } /** * Mark context unused by [activator] */ - fun deactivate(activator: Any) + fun deactivate(activator: Any) { + activators.remove(activator) + } + + /** + * Change the properties of the context. If active, throw an exception + */ + fun configure(action: Config.() -> Unit) { + if (isActive) error("Can't configure active context") + config.action() + } override val coroutineContext: CoroutineContext - get() = Dispatchers.Default + get() = EmptyCoroutineContext /** * Detach all plugins and terminate context */ - fun close() + open fun close() { + if (isActive) error("Can't close active context") + //detach all plugins + plugins.forEach { it.detach() } + } override fun toMeta(): Meta = buildMeta { "parent" to parent?.name @@ -90,12 +115,45 @@ interface Context : Named, MetaRepr, Provider, CoroutineScope { } } +/** + * A sequences of all objects provided by plugins with given target and type + */ +fun Context.members(target: String): Sequence = + plugins.asSequence().flatMap { it.provideAll(target) } + +@JvmName("typedMembers") +inline fun Context.members(target: String) = + members(target).filterIsInstance() + /** * A global root context. Closing [Global] terminates the framework. */ -expect object Global : Context { - fun getContext(name: String): Context +object Global : Context("GLOBAL", null) { + /** + * Closing all contexts + * + * @throws Exception + */ + override fun close() { + logger.info { "Shutting down GLOBAL" } + for (ctx in contextRegistry.values) { + ctx.close() + } + super.close() + } + + private val contextRegistry = HashMap() + + /** + * Get previously builder context o builder a new one + * + * @param name + * @return + */ + fun getContext(name: String): Context { + return contextRegistry.getOrPut(name) { Context(name) } + } } diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/ContextBuilder.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/ContextBuilder.kt new file mode 100644 index 00000000..52079854 --- /dev/null +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/ContextBuilder.kt @@ -0,0 +1,37 @@ +package hep.dataforge.context + +import hep.dataforge.meta.Config +import hep.dataforge.meta.MetaBuilder +import hep.dataforge.meta.configure + +/** + * A convenience builder for context + */ +class ContextBuilder(var name: String = "@anonimous", val parent: Context = Global) { + private val plugins = ArrayList() + private var meta = MetaBuilder() + + fun properties(action: MetaBuilder.() -> Unit) { + meta.action() + } + + fun plugin(plugin: Plugin) { + plugins.add(plugin) + } + + fun plugin(tag: PluginTag, action: Config.() -> Unit) { + plugins.add(PluginRepository.fetch(tag).configure(action)) + } + + fun plugin(name: String, group: String = "", version: String = "", action: Config.() -> Unit) { + plugin(PluginTag(name, group, version), action) + } + + fun build(): Context { + return Context(name, parent).apply { + this@ContextBuilder.plugins.forEach { + plugins.load(it) + } + } + } +} \ No newline at end of file diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt index 780855a6..1de7f6ca 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt @@ -1,8 +1,8 @@ package hep.dataforge.context +import hep.dataforge.meta.Configurable import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaRepr -import hep.dataforge.meta.Metoid import hep.dataforge.meta.buildMeta import hep.dataforge.provider.Provider @@ -22,7 +22,7 @@ import hep.dataforge.provider.Provider * * @author Alexander Nozik */ -interface Plugin : Named, Metoid, ContextAware, Provider, MetaRepr { +interface Plugin : Named, ContextAware, Provider, MetaRepr, Configurable { /** * Get tag for this plugin @@ -67,7 +67,7 @@ interface Plugin : Named, Metoid, ContextAware, Provider, MetaRepr { "context" to context.name "type" to this::class.simpleName "tag" to tag - "meta" to meta + "meta" to config } companion object { diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt index 73b42bd4..1f905093 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt @@ -1,9 +1,6 @@ package hep.dataforge.context -import hep.dataforge.meta.EmptyMeta -import hep.dataforge.meta.Meta -import hep.dataforge.meta.MetaBuilder -import hep.dataforge.meta.buildMeta +import hep.dataforge.meta.* import kotlin.reflect.KClass /** @@ -115,8 +112,8 @@ class PluginManager(override val context: Context) : ContextAware, Iterable load(PluginRepository.fetch(tag, meta)) - loaded.meta == meta -> loaded // if meta is the same, return existing plugin + loaded == null -> load(PluginRepository.fetch(tag)).configure(meta) + loaded.config == meta -> loaded // if meta is the same, return existing plugin else -> throw RuntimeException("Can't load plugin with tag $tag. Plugin with this tag and different configuration already exists in context.") } } @@ -137,7 +134,7 @@ class PluginManager(override val context: Context) : ContextAware, Iterable loaded // if meta is the same, return existing plugin + loaded.config == meta -> loaded // if meta is the same, return existing plugin else -> throw RuntimeException("Can't load plugin with type $type. Plugin with this type and different configuration already exists in context.") } } diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginRepository.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginRepository.kt index 60cdc3f0..43e51a07 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginRepository.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginRepository.kt @@ -1,14 +1,17 @@ package hep.dataforge.context import hep.dataforge.meta.Meta +import hep.dataforge.meta.configure import kotlin.reflect.KClass interface PluginFactory { val tag: PluginTag val type: KClass - fun build(meta: Meta): Plugin + fun build(): Plugin } +fun PluginFactory.build(meta: Meta) = build().configure(meta) + expect object PluginRepository { @@ -24,6 +27,22 @@ expect object PluginRepository { /** * Fetch specific plugin and instantiate it with given meta */ -fun PluginRepository.fetch(tag: PluginTag, meta: Meta): Plugin = - PluginRepository.list().find { it.tag.matches(tag) }?.build(meta) - ?: error("Plugin with tag $tag not found in the repository") \ No newline at end of file +fun PluginRepository.fetch(tag: PluginTag): Plugin = + PluginRepository.list().find { it.tag.matches(tag) }?.build() + ?: error("Plugin with tag $tag not found in the repository") + +fun PluginRepository.register(tag: PluginTag, type: KClass, constructor: () -> Plugin) { + val factory = object : PluginFactory { + override val tag: PluginTag = tag + override val type: KClass = type + + override fun build(): Plugin = constructor() + + } + PluginRepository.register(factory) +} + +inline fun PluginRepository.register(tag: PluginTag, noinline constructor: () -> T) = + register(tag, T::class, constructor) + +fun PluginRepository.register(plugin: Plugin) = register(plugin.tag, plugin::class) { plugin } \ No newline at end of file diff --git a/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/JSContext.kt b/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/JSContext.kt deleted file mode 100644 index 908f4f16..00000000 --- a/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/JSContext.kt +++ /dev/null @@ -1,75 +0,0 @@ -package hep.dataforge.context - -import hep.dataforge.meta.* -import mu.KLogger -import mu.KotlinLogging -import kotlin.jvm.Synchronized - -actual object Global : Context, JSContext("GLOBAL", null) { - /** - * Closing all contexts - * - * @throws Exception - */ - override fun close() { - logger.info { "Shutting down GLOBAL" } - for (ctx in contextRegistry.values) { - ctx.close() - } - super.close() - } - - private val contextRegistry = HashMap() - - /** - * Get previously builder context o builder a new one - * - * @param name - * @return - */ - @Synchronized - actual fun getContext(name: String): Context { - return contextRegistry.getOrPut(name) { JSContext(name) } - } -} - -open class JSContext( - final override val name: String, - final override val parent: JSContext? = Global, - properties: Meta = EmptyMeta -) : Context { - - private val _properties = Config().apply { update(properties) } - override val properties: Meta - get() = if (parent == null) { - _properties - } else { - Laminate(_properties, parent.properties) - } - - override val plugins: PluginManager by lazy { PluginManager(this) } - override val logger: KLogger = KotlinLogging.logger(name) - - /** - * Free up resources associated with this context - * - * @throws Exception - */ - override fun close() { - if (isActive) error("Can't close active context") - //detach all plugins - plugins.forEach { it.detach() } - } - - private val activators = HashSet() - - override val isActive: Boolean = !activators.isEmpty() - - override fun activate(activator: Any) { - activators.add(activator) - } - - override fun deactivate(activator: Any) { - activators.clear() - } -} \ No newline at end of file diff --git a/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/PluginRepository.kt b/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/PluginRepository.kt index 830cd233..4cbeb0d1 100644 --- a/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/PluginRepository.kt +++ b/dataforge-context/src/jsMain/kotlin/hep/dataforge/context/PluginRepository.kt @@ -1,8 +1,5 @@ package hep.dataforge.context -import hep.dataforge.meta.Meta -import kotlin.reflect.KClass - actual object PluginRepository { @@ -12,20 +9,6 @@ actual object PluginRepository { factories.add(factory) } - fun register(tag: PluginTag, type: KClass, constructor: (Meta) -> T) { - val factory = object : PluginFactory { - override val tag: PluginTag = tag - override val type: KClass = type - - override fun build(meta: Meta): Plugin = constructor(meta) - - } - register(factory) - } - - inline fun register(tag: PluginTag, noinline constructor: (Meta) -> T) = - register(tag, T::class, constructor) - /** * List plugins available in the repository */ diff --git a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/ClassLoaderPlugin.kt b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/ClassLoaderPlugin.kt new file mode 100644 index 00000000..6d401613 --- /dev/null +++ b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/ClassLoaderPlugin.kt @@ -0,0 +1,118 @@ +/* + * 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 hep.dataforge.context + +import java.util.* +import kotlin.reflect.KClass +import kotlin.reflect.full.cast + +class ClassLoaderPlugin(val classLoader: ClassLoader) : AbstractPlugin() { + override val tag: PluginTag = PluginTag("classLoader", PluginTag.DATAFORGE_GROUP) + + private val serviceCache: MutableMap, ServiceLoader<*>> = HashMap() + + fun services(type: KClass): Sequence { + return serviceCache.getOrPut(type.java) { ServiceLoader.load(type.java, classLoader) }.asSequence() + .map { type.cast(it) } + } + + companion object { + val DEFAULT = ClassLoaderPlugin(Global::class.java.classLoader) + } +} + +val Context.classLoaderPlugin get() = this.plugins.get() ?: ClassLoaderPlugin.DEFAULT + +inline fun Context.services() = classLoaderPlugin.services(T::class) + + +//open class JVMContext( +// final override val name: String, +// final override val parent: JVMContext? = Global, +// classLoader: ClassLoader? = null, +// properties: Meta = EmptyMeta +//) : Context, AutoCloseable { +// +// override val properties: Meta = if (parent == null) { +// properties +// } else { +// Laminate(properties, parent.properties) +// } +// +// override val plugins: PluginManager by lazy { PluginManager(this) } +// override val logger: KLogger = KotlinLogging.logger(name) +// +// /** +// * A class loader for this context. Parent class loader is used by default +// */ +// open val classLoader: ClassLoader = classLoader ?: parent?.classLoader ?: Global.classLoader +// +// /** +// * A property showing that dispatch thread is started in the context +// */ +// private var started = false +// +// /** +// * A dispatch thread executor for current context +// * +// * @return +// */ +// val dispatcher: ExecutorService by lazy { +// logger.info("Initializing dispatch thread executor in {}", name) +// Executors.newSingleThreadExecutor { r -> +// Thread(r).apply { +// priority = 8 // slightly higher priority +// isDaemon = true +// name = this@JVMContext.name + "_dispatch" +// }.also { started = true } +// } +// } +// +// private val serviceCache: MutableMap, ServiceLoader<*>> = HashMap() +// +// fun services(type: KClass): Sequence { +// return serviceCache.getOrPut(type.java) { ServiceLoader.load(type.java, classLoader) }.asSequence() +// .map { type.cast(it) } +// } +// +// /** +// * Free up resources associated with this context +// * +// * @throws Exception +// */ +// override fun close() { +// if (isActive) error("Can't close active context") +// //detach all plugins +// plugins.forEach { it.detach() } +// +// if (started) { +// dispatcher.shutdown() +// } +// } +// +// private val activators = HashSet>() +// +// override val isActive: Boolean = activators.all { it.get() == null } +// +// override fun activate(activator: Any) { +// activators.add(WeakReference(activator)) +// } +// +// override fun deactivate(activator: Any) { +// activators.removeAll { it.get() == activator } +// } +//} +// diff --git a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/Global.kt b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/Global.kt deleted file mode 100644 index 4c987afc..00000000 --- a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/Global.kt +++ /dev/null @@ -1,65 +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 hep.dataforge.context - -import hep.dataforge.meta.Meta -import hep.dataforge.meta.buildMeta -import hep.dataforge.names.toName -import java.util.* -import kotlin.collections.HashMap - - -private fun Properties.asMeta(): Meta { - return buildMeta { - this@asMeta.forEach { key, value -> - set(key.toString().toName(), value) - } - } -} - -/** - * A singleton global context. Automatic root for the whole context hierarchy. Also stores the registry for active contexts. - * - * @author Alexander Nozik - */ -actual object Global : Context, JVMContext("GLOBAL", null, Thread.currentThread().contextClassLoader) { - - /** - * Closing all contexts - * - * @throws Exception - */ - override fun close() { - logger.info("Shutting down GLOBAL") - for (ctx in contextRegistry.values) { - ctx.close() - } - super.close() - } - - private val contextRegistry = HashMap() - - /** - * Get previously builder context o builder a new one - * - * @param name - * @return - */ - @Synchronized - actual fun getContext(name: String): Context { - return contextRegistry.getOrPut(name) { JVMContext(name) } - } -} diff --git a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt deleted file mode 100644 index 223c7d67..00000000 --- a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/JVMContext.kt +++ /dev/null @@ -1,107 +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 hep.dataforge.context - -import hep.dataforge.meta.* -import mu.KLogger -import mu.KotlinLogging -import java.lang.ref.WeakReference -import java.util.* -import java.util.concurrent.ExecutorService -import java.util.concurrent.Executors -import kotlin.collections.HashSet -import kotlin.reflect.KClass -import kotlin.reflect.full.cast - -open class JVMContext( - final override val name: String, - final override val parent: JVMContext? = Global, - classLoader: ClassLoader? = null, - properties: Meta = EmptyMeta -) : Context, AutoCloseable { - - private val _properties = Config().apply { update(properties) } - override val properties: Meta - get() = if (parent == null) { - _properties - } else { - Laminate(_properties, parent.properties) - } - - override val plugins: PluginManager by lazy { PluginManager(this) } - override val logger: KLogger = KotlinLogging.logger(name) - - /** - * A class loader for this context. Parent class loader is used by default - */ - open val classLoader: ClassLoader = classLoader ?: parent?.classLoader ?: Global.classLoader - - /** - * A property showing that dispatch thread is started in the context - */ - private var started = false - - /** - * A dispatch thread executor for current context - * - * @return - */ - val dispatcher: ExecutorService by lazy { - logger.info("Initializing dispatch thread executor in {}", name) - Executors.newSingleThreadExecutor { r -> - Thread(r).apply { - priority = 8 // slightly higher priority - isDaemon = true - name = this@JVMContext.name + "_dispatch" - }.also { started = true } - } - } - - private val serviceCache: MutableMap, ServiceLoader<*>> = HashMap() - - fun services(type: KClass): Sequence { - return serviceCache.getOrPut(type.java) { ServiceLoader.load(type.java, classLoader) }.asSequence() - .map { type.cast(it) } - } - - /** - * Free up resources associated with this context - * - * @throws Exception - */ - override fun close() { - if (isActive) error("Can't close active context") - //detach all plugins - plugins.forEach { it.detach() } - - if (started) { - dispatcher.shutdown() - } - } - - private val activators = HashSet>() - - override val isActive: Boolean = activators.all { it.get() == null } - - override fun activate(activator: Any) { - activators.add(WeakReference(activator)) - } - - override fun deactivate(activator: Any) { - activators.removeAll { it.get() == activator } - } -} - diff --git a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/PluginRepository.kt b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/PluginRepository.kt index e6008c34..05b65ad9 100644 --- a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/PluginRepository.kt +++ b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/context/PluginRepository.kt @@ -12,6 +12,6 @@ actual object PluginRepository { * List plugins available in the repository */ actual fun list(): Sequence = - factories.asSequence() + Global.services(PluginFactory::class) + factories.asSequence() + Global.services() } \ No newline at end of file diff --git a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/Types.kt b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/Types.kt index df283776..15cfe2d1 100644 --- a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/Types.kt +++ b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/Types.kt @@ -1,6 +1,7 @@ package hep.dataforge.provider import hep.dataforge.context.Context +import hep.dataforge.context.members import kotlin.reflect.KClass import kotlin.reflect.full.findAnnotation @@ -31,7 +32,5 @@ inline fun Provider.provideAllByType(): Sequence { /** * A sequences of all objects provided by plugins with given target and type */ -inline fun Context.components(): Sequence { - return plugins.asSequence().flatMap { it.provideAll(Types[T::class]) }.filterIsInstance() -} +inline fun Context.members(): Sequence = members(Types[T::class]) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt index bcc777c1..6d00d319 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -179,4 +179,6 @@ interface Metoid { val meta: Meta } -fun Value.toMeta() = buildMeta { Meta.VALUE_KEY to this } \ No newline at end of file +fun Value.toMeta() = buildMeta { Meta.VALUE_KEY to this } + +fun Meta.isEmpty() = this === EmptyMeta || this.items.isEmpty() \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt index 0fedc3e9..1f0f64f4 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt @@ -31,12 +31,9 @@ class DataDependency(val filter: DataFilter) : Dependency() { class TaskModelDependency(val name: String, val meta: Meta, val placement: Name = EmptyName) : Dependency() { override fun apply(workspace: Workspace): DataNode { - val model = - workspace.tasks[name]?.build(workspace, meta) ?: error("Task with name $name not found in $workspace") - - val task = workspace.tasks[model.name] ?: error("Task with name ${model.name} is not found in the workspace") + val task = workspace.tasks[name] ?: error("Task with name ${name} is not found in the workspace") if (task.isTerminal) TODO("Support terminal task") - val result = task.run(model) + val result = with(workspace) { task(meta) } return if (placement.isEmpty()) { result } else { diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt index a7fbfea7..b0b1cf4d 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt @@ -1,7 +1,8 @@ package hep.dataforge.workspace +import hep.dataforge.context.Context import hep.dataforge.context.ContextAware -import hep.dataforge.context.Named +import hep.dataforge.context.members import hep.dataforge.data.Data import hep.dataforge.data.DataNode import hep.dataforge.meta.Meta @@ -12,7 +13,7 @@ import hep.dataforge.provider.Type @Type(Workspace.TYPE) -interface Workspace : ContextAware, Named, Provider { +interface Workspace : ContextAware, Provider { /** * The whole data node for current workspace */ @@ -48,8 +49,40 @@ interface Workspace : ContextAware, Named, Provider { } } + operator fun Task.invoke(config: Meta): DataNode { + context.activate(this) + try { + val model = build(this@Workspace, config) + validate(model) + return run(model) + } finally { + context.deactivate(this) + } + } + + /** + * Invoke a task in the workspace utilizing caching if possible + */ + operator fun Task.invoke(targetName: String): DataNode { + val target = targets[targetName] ?: error("A target with name $targetName not found in ${this@Workspace}") + return invoke(target) + } + companion object { const val TYPE = "workspace" } } +class SimpleWorkspace( + override val context: Context, + override val data: DataNode, + override val targets: Map, + tasks: Collection> +) : Workspace { + + override val tasks: Map> by lazy { + (context.members>(Task.TYPE) + tasks).associate { it.name to it } + } + +} + diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt new file mode 100644 index 00000000..9aebdcd0 --- /dev/null +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt @@ -0,0 +1,40 @@ +package hep.dataforge.workspace + +import hep.dataforge.context.Context +import hep.dataforge.context.ContextBuilder +import hep.dataforge.data.DataTreeBuilder +import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaBuilder +import hep.dataforge.meta.buildMeta + +/** + * A builder for a workspace + */ +class WorkspaceBuilder(var context: Context) { + val data = DataTreeBuilder() + val targets = HashMap() + val tasks = HashSet>() + + fun context(action: ContextBuilder.() -> Unit) { + this.context = ContextBuilder().apply(action).build() + } + + fun data(action: DataTreeBuilder.() -> Unit) = data.apply(action) + + fun target(name: String, meta: Meta) { + targets[name] = meta + } + + fun target(name: String, action: MetaBuilder.() -> Unit) = target(name, buildMeta(action)) + + fun task(task: Task<*>) { + tasks.add(task) + } + + fun build(): Workspace = SimpleWorkspace( + context, + data.build(), + targets, + tasks + ) +} \ No newline at end of file From 128f74755832ea43caa0f9dd2cdd9ed0bed5a222 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 1 Feb 2019 16:38:11 +0300 Subject: [PATCH 26/37] Starting scripting support --- dataforge-scripting/build.gradle.kts | 20 +++++++++++++++++++ .../hep/dataforge/scripting/Builders.kt | 7 +++++++ settings.gradle.kts | 3 ++- 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 dataforge-scripting/build.gradle.kts create mode 100644 dataforge-scripting/src/jvmMain/kotlin/hep/dataforge/scripting/Builders.kt diff --git a/dataforge-scripting/build.gradle.kts b/dataforge-scripting/build.gradle.kts new file mode 100644 index 00000000..9c37715d --- /dev/null +++ b/dataforge-scripting/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + kotlin("multiplatform") +} + +kotlin { + jvm() + sourceSets { + val commonMain by getting { + dependencies { + api(project(":dataforge-workspace")) + implementation("org.jetbrains.kotlin:kotlin-scripting-common") + } + } + val jvmMain by getting { + dependencies { + implementation("org.jetbrains.kotlin:kotlin-scripting-jvm-host") + } + } + } +} \ No newline at end of file diff --git a/dataforge-scripting/src/jvmMain/kotlin/hep/dataforge/scripting/Builders.kt b/dataforge-scripting/src/jvmMain/kotlin/hep/dataforge/scripting/Builders.kt new file mode 100644 index 00000000..ffe5569a --- /dev/null +++ b/dataforge-scripting/src/jvmMain/kotlin/hep/dataforge/scripting/Builders.kt @@ -0,0 +1,7 @@ +package hep.dataforge.scripting + +import java.io.File + +fun buildWorkspace(file: File) { + +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 670d7438..75af1911 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,5 +14,6 @@ include( ":dataforge-context", ":dataforge-data", ":dataforge-io", - ":dataforge-workspace" + ":dataforge-workspace", + ":dataforge-scripting" ) From c06244465ca00864cd30d2c5bbef030b9f31844c Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 5 Feb 2019 16:49:35 +0300 Subject: [PATCH 27/37] Scripting builders (does not work yet) --- dataforge-scripting/build.gradle.kts | 12 +++++-- .../hep/dataforge/scripting/Builders.kt | 36 ++++++++++++++++++- .../hep/dataforge/scripting/BuildersKtTest.kt | 28 +++++++++++++++ 3 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 dataforge-scripting/src/jvmTest/kotlin/hep/dataforge/scripting/BuildersKtTest.kt diff --git a/dataforge-scripting/build.gradle.kts b/dataforge-scripting/build.gradle.kts index 9c37715d..674ec25f 100644 --- a/dataforge-scripting/build.gradle.kts +++ b/dataforge-scripting/build.gradle.kts @@ -8,12 +8,20 @@ kotlin { val commonMain by getting { dependencies { api(project(":dataforge-workspace")) - implementation("org.jetbrains.kotlin:kotlin-scripting-common") + implementation(kotlin("scripting-common")) } } val jvmMain by getting { dependencies { - implementation("org.jetbrains.kotlin:kotlin-scripting-jvm-host") + implementation(kotlin("scripting-jvm-host-embeddable")) + implementation(kotlin("scripting-jvm")) + } + } + val jvmTest by getting { + dependencies { + implementation(kotlin("test")) + implementation(kotlin("test-junit")) + implementation("ch.qos.logback:logback-classic:1.2.3") } } } diff --git a/dataforge-scripting/src/jvmMain/kotlin/hep/dataforge/scripting/Builders.kt b/dataforge-scripting/src/jvmMain/kotlin/hep/dataforge/scripting/Builders.kt index ffe5569a..452b3fe2 100644 --- a/dataforge-scripting/src/jvmMain/kotlin/hep/dataforge/scripting/Builders.kt +++ b/dataforge-scripting/src/jvmMain/kotlin/hep/dataforge/scripting/Builders.kt @@ -1,7 +1,41 @@ package hep.dataforge.scripting +import hep.dataforge.context.Global +import hep.dataforge.workspace.Workspace +import hep.dataforge.workspace.WorkspaceBuilder import java.io.File +import kotlin.script.experimental.api.* +import kotlin.script.experimental.host.toScriptSource +import kotlin.script.experimental.jvm.dependenciesFromCurrentContext +import kotlin.script.experimental.jvm.jvm +import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost -fun buildWorkspace(file: File) { +object Builders { + fun buildWorkspace(source: SourceCode): Workspace { + val builder = WorkspaceBuilder(Global) + + val workspaceScriptConfiguration = ScriptCompilationConfiguration { + baseClass(Any::class) + implicitReceivers(WorkspaceBuilder::class) + jvm{ + dependenciesFromCurrentContext() + } + } + + val evaluationConfiguration = ScriptEvaluationConfiguration { + implicitReceivers(builder) + } + + val res = BasicJvmScriptingHost().eval(source, workspaceScriptConfiguration, evaluationConfiguration) + res.reports.forEach{ scriptDiagnostic -> + scriptDiagnostic.exception?.let { throw it } + } + + return builder.build() + } + + fun buildWorkspace(file: File): Workspace = buildWorkspace(file.toScriptSource()) + + fun buildWorkspace(string: String): Workspace = buildWorkspace(string.toScriptSource()) } \ No newline at end of file diff --git a/dataforge-scripting/src/jvmTest/kotlin/hep/dataforge/scripting/BuildersKtTest.kt b/dataforge-scripting/src/jvmTest/kotlin/hep/dataforge/scripting/BuildersKtTest.kt new file mode 100644 index 00000000..ef58de78 --- /dev/null +++ b/dataforge-scripting/src/jvmTest/kotlin/hep/dataforge/scripting/BuildersKtTest.kt @@ -0,0 +1,28 @@ +package hep.dataforge.scripting + +import hep.dataforge.meta.get +import hep.dataforge.meta.int +import org.junit.Test +import kotlin.test.assertEquals + + +class BuildersKtTest { + @Test + fun testWorkspaceBuilder() { + val script = """ + println("I am working") + + context{ + name = "test" + } + + target("testTarget"){ + "a" to 12 + } + """.trimIndent() + val workspace = Builders.buildWorkspace(script) + + val target = workspace.targets.getValue("testTarget") + assertEquals(12, target["a"]!!.int) + } +} \ No newline at end of file From 48c45fa51ca72be20979e91f3938d6dc3d9287bd Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 10 Feb 2019 09:49:16 +0300 Subject: [PATCH 28/37] Working workspace builder --- build.gradle.kts | 2 +- .../hep/dataforge/scripting/Builders.kt | 22 +++++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 2daffdb1..6b033c87 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -32,7 +32,7 @@ allprojects { } group = "hep.dataforge" - version = "0.1.1-dev-2" + version = "0.1.1-dev-3" extensions.findByType()?.apply { jvm { diff --git a/dataforge-scripting/src/jvmMain/kotlin/hep/dataforge/scripting/Builders.kt b/dataforge-scripting/src/jvmMain/kotlin/hep/dataforge/scripting/Builders.kt index 452b3fe2..ee1af3df 100644 --- a/dataforge-scripting/src/jvmMain/kotlin/hep/dataforge/scripting/Builders.kt +++ b/dataforge-scripting/src/jvmMain/kotlin/hep/dataforge/scripting/Builders.kt @@ -1,5 +1,6 @@ package hep.dataforge.scripting +import hep.dataforge.context.Context import hep.dataforge.context.Global import hep.dataforge.workspace.Workspace import hep.dataforge.workspace.WorkspaceBuilder @@ -12,14 +13,14 @@ import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost object Builders { - fun buildWorkspace(source: SourceCode): Workspace { - val builder = WorkspaceBuilder(Global) + fun buildWorkspace(source: SourceCode, context: Context = Global): Workspace { + val builder = WorkspaceBuilder(context) val workspaceScriptConfiguration = ScriptCompilationConfiguration { baseClass(Any::class) implicitReceivers(WorkspaceBuilder::class) - jvm{ - dependenciesFromCurrentContext() + jvm { + dependenciesFromCurrentContext(wholeClasspath = true) } } @@ -27,9 +28,16 @@ object Builders { implicitReceivers(builder) } - val res = BasicJvmScriptingHost().eval(source, workspaceScriptConfiguration, evaluationConfiguration) - res.reports.forEach{ scriptDiagnostic -> - scriptDiagnostic.exception?.let { throw it } + BasicJvmScriptingHost().eval(source, workspaceScriptConfiguration, evaluationConfiguration).onFailure { + it.reports.forEach { scriptDiagnostic -> + when (scriptDiagnostic.severity) { + ScriptDiagnostic.Severity.FATAL, ScriptDiagnostic.Severity.ERROR -> + context.logger.error(scriptDiagnostic.exception) { scriptDiagnostic.toString() } + ScriptDiagnostic.Severity.WARNING -> context.logger.warn { scriptDiagnostic.toString() } + ScriptDiagnostic.Severity.INFO -> context.logger.info { scriptDiagnostic.toString() } + ScriptDiagnostic.Severity.DEBUG -> context.logger.debug { scriptDiagnostic.toString() } + } + } } return builder.build() From 54b30e9260c971296a97a2cf8e9e102e0edcdee4 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 1 Mar 2019 22:10:17 +0300 Subject: [PATCH 29/37] Visuliazation package. Multiple fixes to mutable and styled meta. --- .gitignore | 4 +- build.gradle.kts | 85 ++++- dataforge-io/build.gradle.kts | 4 - .../kotlin/hep/dataforge/meta/io/Binary.kt | 7 + .../kotlin/hep/dataforge/meta/io/Envelope.kt | 50 +++ .../kotlin/hep/dataforge/meta/Config.kt | 7 + .../kotlin/hep/dataforge/meta/Delegates.kt | 332 ++++++++++++------ .../hep/dataforge/meta/ExtraMetaDelegates.kt | 36 ++ .../kotlin/hep/dataforge/meta/Laminate.kt | 2 +- .../kotlin/hep/dataforge/meta/Meta.kt | 50 +-- .../{MutableMetaNode.kt => MutableMeta.kt} | 46 +-- .../hep/dataforge/meta/Specification.kt | 7 +- .../kotlin/hep/dataforge/meta/Styleable.kt | 68 ---- .../kotlin/hep/dataforge/meta/Styled.kt | 68 ++++ dataforge-vis/build.gradle.kts | 24 ++ .../dataforge-vis-fx/build.gradle.kts | 23 ++ .../vis/spatial/FXSpatialRenderer.kt | 45 +++ .../dataforge/vis/spatial/RendererDemoApp.kt | 44 +++ .../dataforge-vis-js/build.gradle.kts | 42 +++ .../dataforge-vis-spatial/build.gradle.kts | 26 ++ .../dataforge/vis/spatial/DisplayObject3D.kt | 28 ++ .../kotlin/hep/dataforge/vis/DisplayObject.kt | 143 ++++++++ .../dataforge/vis/DisplayObjectDelegates.kt | 48 +++ .../kotlin/hep/dataforge/vis/NamedObject.kt | 40 +++ gradle.properties | 7 + gradle/artifactory.gradle | 31 ++ gradle/bintray.gradle | 85 +++++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 55190 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 172 +++++++++ gradlew.bat | 84 +++++ settings.gradle.kts | 20 +- 32 files changed, 1387 insertions(+), 246 deletions(-) create mode 100644 dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Binary.kt create mode 100644 dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Envelope.kt create mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ExtraMetaDelegates.kt rename dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/{MutableMetaNode.kt => MutableMeta.kt} (76%) delete mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styleable.kt create mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styled.kt create mode 100644 dataforge-vis/build.gradle.kts create mode 100644 dataforge-vis/dataforge-vis-fx/build.gradle.kts create mode 100644 dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/FXSpatialRenderer.kt create mode 100644 dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt create mode 100644 dataforge-vis/dataforge-vis-js/build.gradle.kts create mode 100644 dataforge-vis/dataforge-vis-spatial/build.gradle.kts create mode 100644 dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/DisplayObject3D.kt create mode 100644 dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObject.kt create mode 100644 dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObjectDelegates.kt create mode 100644 dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/NamedObject.kt create mode 100644 gradle.properties create mode 100644 gradle/artifactory.gradle create mode 100644 gradle/bintray.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat diff --git a/.gitignore b/.gitignore index 5fa56866..7287ad5a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,4 @@ */build/** -!gradle-wrapper.jar - -artifactory.gradle \ No newline at end of file +!gradle-wrapper.jar \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 6b033c87..7b8168f8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,19 +1,25 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension buildscript { - val kotlinVersion: String by rootProject.extra("1.3.20") - val ioVersion: String by rootProject.extra("0.1.4") + val kotlinVersion: String by rootProject.extra("1.3.21") + val ioVersion: String by rootProject.extra("0.1.5") val coroutinesVersion: String by rootProject.extra("1.1.1") - val serializationVersion: String by rootProject.extra("0.9.1") + val atomicfuVersion: String by rootProject.extra("0.12.1") + val dokkaVersion: String by rootProject.extra("0.9.17") + val serializationVersion: String by rootProject.extra("0.10.0") repositories { jcenter() + maven("https://dl.bintray.com/kotlin/kotlin-eap") } dependencies { classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") - classpath("org.jetbrains.kotlin:kotlin-serialization:$kotlinVersion") classpath("org.jfrog.buildinfo:build-info-extractor-gradle:4+") + classpath("com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4") + classpath("org.jetbrains.dokka:dokka-gradle-plugin:$dokkaVersion") + classpath("org.jetbrains.kotlin:kotlin-frontend-plugin:0.0.45") + classpath("org.openjfx:javafx-plugin:0.0.7") } } @@ -23,6 +29,7 @@ plugins { } allprojects { + apply(plugin = "maven") apply(plugin = "maven-publish") apply(plugin = "com.jfrog.artifactory") @@ -32,8 +39,17 @@ allprojects { } group = "hep.dataforge" - version = "0.1.1-dev-3" + version = "0.1.1-dev-4" + // apply bintray configuration + apply(from = "${rootProject.rootDir}/gradle/bintray.gradle") + + //apply artifactory configuration + apply(from = "${rootProject.rootDir}/gradle/artifactory.gradle") + +} + +subprojects { extensions.findByType()?.apply { jvm { compilations.all { @@ -48,8 +64,61 @@ allprojects { } } } + + // dokka { +// outputFormat = "html" +// outputDirectory = javadoc.destinationDir +// } +// +// task dokkaJar (type: Jar, dependsOn: dokka) { +// from javadoc . destinationDir +// classifier = "javadoc" +// } + + + if (!name.startsWith("dataforge")) return@subprojects + + extensions.findByType()?.apply { + publications.filterIsInstance().forEach { publication -> + if (publication.name == "kotlinMultiplatform") { + // for our root metadata publication, set artifactId with a package and project name + publication.artifactId = project.name + } else { + // for targets, set artifactId with a package, project name and target name (e.g. iosX64) + publication.artifactId = "${project.name}-${publication.name}" + } + } + + // Create empty jar for sources classifier to satisfy maven requirements + val stubSources by tasks.registering(Jar::class) { + archiveClassifier.set("sources") + //from(sourceSets.main.get().allSource) + } + + // Create empty jar for javadoc classifier to satisfy maven requirements + val stubJavadoc by tasks.registering(Jar::class) { + archiveClassifier.set("javadoc") + } + + extensions.findByType()?.apply { + + targets.forEach { target -> + val publication = publications.findByName(target.name) as MavenPublication + + // Patch publications with fake javadoc + publication.artifact(stubJavadoc) + + // Remove gradle metadata publishing from all targets which are not native +// if (target.platformType.name != "native") { +// publication.gradleModuleMetadataFile = null +// tasks.matching { it.name == "generateMetadataFileFor${name.capitalize()}Publication" }.all { +// onlyIf { false } +// } +// } + } + } + } + } -if (file("artifactory.gradle").exists()) { - apply(from = "artifactory.gradle") -} \ No newline at end of file + diff --git a/dataforge-io/build.gradle.kts b/dataforge-io/build.gradle.kts index e0ae01c6..1756f10d 100644 --- a/dataforge-io/build.gradle.kts +++ b/dataforge-io/build.gradle.kts @@ -2,10 +2,6 @@ plugins { kotlin("multiplatform") } -repositories { - jcenter() -} - kotlin { jvm() js() diff --git a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Binary.kt b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Binary.kt new file mode 100644 index 00000000..7ab032c2 --- /dev/null +++ b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Binary.kt @@ -0,0 +1,7 @@ +package hep.dataforge.meta.io + +import kotlinx.io.ByteBuffer +import kotlinx.io.core.Input + +//TODO replace by abstraction +typealias Binary = Input \ No newline at end of file diff --git a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Envelope.kt b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Envelope.kt new file mode 100644 index 00000000..ce01d506 --- /dev/null +++ b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Envelope.kt @@ -0,0 +1,50 @@ +package hep.dataforge.meta.io + +import hep.dataforge.meta.Meta +import hep.dataforge.meta.get +import hep.dataforge.meta.string + +interface Envelope { + val meta: Meta + val data: Binary? + + companion object { +// /** +// * Property keys +// */ +// const val TYPE_PROPERTY = "type" +// const val META_TYPE_PROPERTY = "metaType" +// const val META_LENGTH_PROPERTY = "metaLength" +// const val DATA_LENGTH_PROPERTY = "dataLength" + + /** + * meta keys + */ + const val ENVELOPE_NODE = "@envelope" + const val ENVELOPE_TYPE_KEY = "$ENVELOPE_NODE.type" + const val ENVELOPE_DATA_TYPE_KEY = "$ENVELOPE_NODE.dataType" + const val ENVELOPE_DESCRIPTION_KEY = "$ENVELOPE_NODE.description" + //const val ENVELOPE_TIME_KEY = "@envelope.time" + } +} + +/** + * The purpose of the envelope + * + * @return + */ +val Envelope.type: String? get() = meta[Envelope.ENVELOPE_TYPE_KEY].string + +/** + * The type of data encoding + * + * @return + */ +val Envelope.dataType: String? get() = meta[Envelope.ENVELOPE_DATA_TYPE_KEY].string + +/** + * Textual user friendly description + * + * @return + */ +val Envelope.description: String? get() = meta[Envelope.ENVELOPE_DESCRIPTION_KEY].string \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt index 4a5d23fe..79407337 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt @@ -1,6 +1,7 @@ package hep.dataforge.meta import hep.dataforge.names.Name +import hep.dataforge.names.NameToken import hep.dataforge.names.toName //TODO add validator to configuration @@ -16,8 +17,14 @@ open class Config : MutableMetaNode() { override fun wrap(name: Name, meta: Meta): Config = meta.toConfig() override fun empty(): Config = Config() + + companion object { + fun empty(): Config = Config() + } } +operator fun Config.get(token: NameToken): MetaItem? = items[token] + fun Meta.toConfig(): Config = this as? Config ?: Config().also { builder -> this.items.mapValues { entry -> val item = entry.value diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Delegates.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Delegates.kt index fadfc673..5672d2cf 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Delegates.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Delegates.kt @@ -2,6 +2,7 @@ package hep.dataforge.meta import hep.dataforge.values.Null import hep.dataforge.values.Value +import hep.dataforge.values.asValue import kotlin.jvm.JvmName import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadWriteProperty @@ -11,247 +12,348 @@ import kotlin.reflect.KProperty //TODO add caching for sealed nodes -class ValueDelegate(private val key: String? = null, private val default: Value? = null) : - ReadOnlyProperty { - override fun getValue(thisRef: Metoid, property: KProperty<*>): Value? { - return thisRef.meta[key ?: property.name]?.value ?: default +class ValueDelegate(val meta: Meta, private val key: String? = null, private val default: Value? = null) : + ReadOnlyProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): Value? { + return meta[key ?: property.name]?.value ?: default } } -class StringDelegate(private val key: String? = null, private val default: String? = null) : - ReadOnlyProperty { - override fun getValue(thisRef: Metoid, property: KProperty<*>): String? { - return thisRef.meta[key ?: property.name]?.string ?: default +class StringDelegate(val meta: Meta, private val key: String? = null, private val default: String? = null) : + ReadOnlyProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): String? { + return meta[key ?: property.name]?.string ?: default } } -class BooleanDelegate(private val key: String? = null, private val default: Boolean? = null) : +class BooleanDelegate(val meta: Meta, private val key: String? = null, private val default: Boolean? = null) : ReadOnlyProperty { override fun getValue(thisRef: Metoid, property: KProperty<*>): Boolean? { - return thisRef.meta[key ?: property.name]?.boolean ?: default + return meta[key ?: property.name]?.boolean ?: default } } -class NumberDelegate(private val key: String? = null, private val default: Number? = null) : - ReadOnlyProperty { - override fun getValue(thisRef: Metoid, property: KProperty<*>): Number? { - return thisRef.meta[key ?: property.name]?.number ?: default +class NumberDelegate(val meta: Meta, private val key: String? = null, private val default: Number? = null) : + ReadOnlyProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): Number? { + return meta[key ?: property.name]?.number ?: default + } + + //delegates for number transformation + + val double get() = DelegateWrapper(this) { it?.toDouble() } + val int get() = DelegateWrapper(this) { it?.toInt() } + val short get() = DelegateWrapper(this) { it?.toShort() } + val long get() = DelegateWrapper(this) { it?.toLong() } +} + +class DelegateWrapper(val delegate: ReadOnlyProperty, val reader: (T) -> R) : + ReadOnlyProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): R { + return reader(delegate.getValue(thisRef, property)) } } //Delegates with non-null values -class SafeStringDelegate(private val key: String? = null, private val default: String) : - ReadOnlyProperty { - override fun getValue(thisRef: Metoid, property: KProperty<*>): String { - return thisRef.meta[key ?: property.name]?.string ?: default +class SafeStringDelegate(val meta: Meta, private val key: String? = null, private val default: String) : + ReadOnlyProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): String { + return meta[key ?: property.name]?.string ?: default } } -class SafeBooleanDelegate(private val key: String? = null, private val default: Boolean) : - ReadOnlyProperty { - override fun getValue(thisRef: Metoid, property: KProperty<*>): Boolean { - return thisRef.meta[key ?: property.name]?.boolean ?: default +class SafeBooleanDelegate(val meta: Meta, private val key: String? = null, private val default: Boolean) : + ReadOnlyProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean { + return meta[key ?: property.name]?.boolean ?: default } } -class SafeNumberDelegate(private val key: String? = null, private val default: Number) : - ReadOnlyProperty { - override fun getValue(thisRef: Metoid, property: KProperty<*>): Number { - return thisRef.meta[key ?: property.name]?.number ?: default +class SafeNumberDelegate(val meta: Meta, private val key: String? = null, private val default: Number) : + ReadOnlyProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): Number { + return meta[key ?: property.name]?.number ?: default } + + val double get() = DelegateWrapper(this) { it.toDouble() } + val int get() = DelegateWrapper(this) { it.toInt() } + val short get() = DelegateWrapper(this) { it.toShort() } + val long get() = DelegateWrapper(this) { it.toLong() } } class SafeEnumDelegate>( + val meta: Meta, private val key: String? = null, private val default: E, private val resolver: (String) -> E -) : ReadOnlyProperty { - override fun getValue(thisRef: Metoid, property: KProperty<*>): E { - return (thisRef.meta[key ?: property.name]?.string)?.let { resolver(it) } ?: default +) : ReadOnlyProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): E { + return (meta[key ?: property.name]?.string)?.let { resolver(it) } ?: default } } //Child node delegate -class ChildDelegate(private val key: String? = null, private val converter: (Meta) -> T) : - ReadOnlyProperty { - override fun getValue(thisRef: Metoid, property: KProperty<*>): T? { - return thisRef.meta[key ?: property.name]?.node?.let { converter(it) } +class ChildDelegate(val meta: Meta, private val key: String? = null, private val converter: (Meta) -> T) : + ReadOnlyProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): T? { + return meta[key ?: property.name]?.node?.let { converter(it) } } } -//Read-only delegates +//Read-only delegates for Metas /** * A property delegate that uses custom key */ -fun Metoid.value(default: Value = Null, key: String? = null) = ValueDelegate(key, default) +fun Meta.value(default: Value = Null, key: String? = null) = ValueDelegate(this, key, default) -fun Metoid.string(default: String? = null, key: String? = null) = StringDelegate(key, default) +fun Meta.string(default: String? = null, key: String? = null) = StringDelegate(this, key, default) -fun Metoid.boolean(default: Boolean? = null, key: String? = null) = BooleanDelegate(key, default) +fun Meta.boolean(default: Boolean? = null, key: String? = null) = BooleanDelegate(this, key, default) -fun Metoid.number(default: Number? = null, key: String? = null) = NumberDelegate(key, default) +fun Meta.number(default: Number? = null, key: String? = null) = NumberDelegate(this, key, default) -fun Metoid.child(key: String? = null) = ChildDelegate(key) { it } - -fun Metoid.child(key: String? = null, converter: (Meta) -> T) = ChildDelegate(key, converter) +fun Meta.child(key: String? = null) = ChildDelegate(this, key) { it } @JvmName("safeString") -fun Metoid.string(default: String, key: String? = null) = SafeStringDelegate(key, default) +fun Meta.string(default: String, key: String? = null) = SafeStringDelegate(this, key, default) @JvmName("safeBoolean") -fun Metoid.boolean(default: Boolean, key: String? = null) = SafeBooleanDelegate(key, default) +fun Meta.boolean(default: Boolean, key: String? = null) = SafeBooleanDelegate(this, key, default) @JvmName("safeNumber") -fun Metoid.number(default: Number, key: String? = null) = SafeNumberDelegate(key, default) +fun Meta.number(default: Number, key: String? = null) = SafeNumberDelegate(this, key, default) + +inline fun > Meta.enum(default: E, key: String? = null) = + SafeEnumDelegate(this, key, default) { enumValueOf(it) } -inline fun > Metoid.enum(default: E, key: String? = null) = - SafeEnumDelegate(key, default) { enumValueOf(it) } /* Config delegates */ -class ValueConfigDelegate(private val key: String? = null, private val default: Value? = null) : - ReadWriteProperty { - override fun getValue(thisRef: Configurable, property: KProperty<*>): Value? { - return thisRef.config[key ?: property.name]?.value ?: default +class ValueConfigDelegate>( + val config: M, + private val key: String? = null, + private val default: Value? = null +) : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): Value? { + return config[key ?: property.name]?.value ?: default } - override fun setValue(thisRef: Configurable, property: KProperty<*>, value: Value?) { + override fun setValue(thisRef: Any?, property: KProperty<*>, value: Value?) { val name = key ?: property.name if (value == null) { - thisRef.config.remove(name) + config.remove(name) } else { - thisRef.config[name] = value + config.setValue(name, value) } } } -class StringConfigDelegate(private val key: String? = null, private val default: String? = null) : - ReadWriteProperty { - override fun getValue(thisRef: Configurable, property: KProperty<*>): String? { - return thisRef.config[key ?: property.name]?.string ?: default +class StringConfigDelegate>( + val config: M, + private val key: String? = null, + private val default: String? = null +) : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): String? { + return config[key ?: property.name]?.string ?: default } - override fun setValue(thisRef: Configurable, property: KProperty<*>, value: String?) { - thisRef.config[key ?: property.name] = value + override fun setValue(thisRef: Any?, property: KProperty<*>, value: String?) { + val name = key ?: property.name + if (value == null) { + config.remove(name) + } else { + config.setValue(name, value.asValue()) + } } } -class BooleanConfigDelegate(private val key: String? = null, private val default: Boolean? = null) : - ReadWriteProperty { - override fun getValue(thisRef: Configurable, property: KProperty<*>): Boolean? { - return thisRef.config[key ?: property.name]?.boolean ?: default +class BooleanConfigDelegate>( + val config: M, + private val key: String? = null, + private val default: Boolean? = null +) : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean? { + return config[key ?: property.name]?.boolean ?: default } - override fun setValue(thisRef: Configurable, property: KProperty<*>, value: Boolean?) { - thisRef.config[key ?: property.name] = value + override fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean?) { + val name = key ?: property.name + if (value == null) { + config.remove(name) + } else { + config.setValue(name, value.asValue()) + } } } -class NumberConfigDelegate(private val key: String? = null, private val default: Number? = null) : - ReadWriteProperty { - override fun getValue(thisRef: Configurable, property: KProperty<*>): Number? { - return thisRef.config[key ?: property.name]?.number ?: default +class NumberConfigDelegate>( + val config: M, + private val key: String? = null, + private val default: Number? = null +) : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): Number? { + return config[key ?: property.name]?.number ?: default } - override fun setValue(thisRef: Configurable, property: KProperty<*>, value: Number?) { - thisRef.config[key ?: property.name] = value + override fun setValue(thisRef: Any?, property: KProperty<*>, value: Number?) { + val name = key ?: property.name + if (value == null) { + config.remove(name) + } else { + config.setValue(name, value.asValue()) + } } + + val double get() = ReadWriteDelegateWrapper(this, reader = { it?.toDouble() }, writer = { it }) + val int get() = ReadWriteDelegateWrapper(this, reader = { it?.toInt() }, writer = { it }) + val short get() = ReadWriteDelegateWrapper(this, reader = { it?.toShort() }, writer = { it }) + val long get() = ReadWriteDelegateWrapper(this, reader = { it?.toLong() }, writer = { it }) } //Delegates with non-null values -class SafeStringConfigDelegate(private val key: String? = null, private val default: String) : - ReadWriteProperty { - override fun getValue(thisRef: Configurable, property: KProperty<*>): String { - return thisRef.config[key ?: property.name]?.string ?: default +class SafeStringConfigDelegate>( + val config: M, + private val key: String? = null, + private val default: String +) : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): String { + return config[key ?: property.name]?.string ?: default } - override fun setValue(thisRef: Configurable, property: KProperty<*>, value: String) { - thisRef.config[key ?: property.name] = value + override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { + config.setValue(key ?: property.name, value.asValue()) } } -class SafeBooleanConfigDelegate(private val key: String? = null, private val default: Boolean) : - ReadWriteProperty { - override fun getValue(thisRef: Configurable, property: KProperty<*>): Boolean { - return thisRef.config[key ?: property.name]?.boolean ?: default +class SafeBooleanConfigDelegate>( + val config: M, + private val key: String? = null, + private val default: Boolean +) : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean { + return config[key ?: property.name]?.boolean ?: default } - override fun setValue(thisRef: Configurable, property: KProperty<*>, value: Boolean) { - thisRef.config[key ?: property.name] = value + override fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) { + config.setValue(key ?: property.name, value.asValue()) } } -class SafeNumberConfigDelegate(private val key: String? = null, private val default: Number) : - ReadWriteProperty { - override fun getValue(thisRef: Configurable, property: KProperty<*>): Number { - return thisRef.config[key ?: property.name]?.number ?: default +class SafeNumberConfigDelegate>( + val config: M, + private val key: String? = null, + private val default: Number +) : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): Number { + return config[key ?: property.name]?.number ?: default } - override fun setValue(thisRef: Configurable, property: KProperty<*>, value: Number) { - thisRef.config[key ?: property.name] = value + override fun setValue(thisRef: Any?, property: KProperty<*>, value: Number) { + config.setValue(key ?: property.name, value.asValue()) } + + val double get() = ReadWriteDelegateWrapper(this, reader = { it.toDouble() }, writer = { it }) + val int get() = ReadWriteDelegateWrapper(this, reader = { it.toInt() }, writer = { it }) + val short get() = ReadWriteDelegateWrapper(this, reader = { it.toShort() }, writer = { it }) + val long get() = ReadWriteDelegateWrapper(this, reader = { it.toLong() }, writer = { it }) } -class SafeEnumvConfigDelegate>( +class SafeEnumvConfigDelegate, E : Enum>( + val config: M, private val key: String? = null, private val default: E, private val resolver: (String) -> E -) : ReadWriteProperty { - override fun getValue(thisRef: Configurable, property: KProperty<*>): E { - return (thisRef.config[key ?: property.name]?.string)?.let { resolver(it) } ?: default +) : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): E { + return (config[key ?: property.name]?.string)?.let { resolver(it) } ?: default } - override fun setValue(thisRef: Configurable, property: KProperty<*>, value: E) { - thisRef.config[key ?: property.name] = value.name + override fun setValue(thisRef: Any?, property: KProperty<*>, value: E) { + config.setValue(key ?: property.name, value.name.asValue()) } } //Child node delegate -class ChildConfigDelegate(private val key: String? = null, private val converter: (Config) -> T) : - ReadWriteProperty { - override fun getValue(thisRef: Configurable, property: KProperty<*>): T { - return converter(thisRef.config[key ?: property.name]?.node ?: Config()) +class MetaNodeDelegate>( + val config: M, + private val key: String? = null +) : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): Meta { + return config[key ?: property.name]?.node ?: EmptyMeta } - override fun setValue(thisRef: Configurable, property: KProperty<*>, value: T) { - thisRef.config[key ?: property.name] = value.config + override fun setValue(thisRef: Any?, property: KProperty<*>, value: Meta) { + config[key ?: property.name] = value } - } +class ChildConfigDelegate, T : Configurable>( + val config: M, + private val key: String? = null, + private val converter: (Meta) -> T +) : + ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): T { + return converter(config[key ?: property.name]?.node ?: EmptyMeta) + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { + config[key ?: property.name] = value.config + } +} + +class ReadWriteDelegateWrapper( + val delegate: ReadWriteProperty, + val reader: (T) -> R, + val writer: (R) -> T +) : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): R { + return reader(delegate.getValue(thisRef, property)) + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: R) { + delegate.setValue(thisRef, property, writer(value)) + } +} + + //Read-write delegates /** * A property delegate that uses custom key */ -fun Configurable.value(default: Value = Null, key: String? = null) = ValueConfigDelegate(key, default) +fun > M.value(default: Value = Null, key: String? = null) = + ValueConfigDelegate(this, key, default) -fun Configurable.string(default: String? = null, key: String? = null) = StringConfigDelegate(key, default) +fun > M.string(default: String? = null, key: String? = null) = + StringConfigDelegate(this, key, default) -fun Configurable.boolean(default: Boolean? = null, key: String? = null) = BooleanConfigDelegate(key, default) +fun > M.boolean(default: Boolean? = null, key: String? = null) = + BooleanConfigDelegate(this, key, default) -fun Configurable.number(default: Number? = null, key: String? = null) = NumberConfigDelegate(key, default) +fun > M.number(default: Number? = null, key: String? = null) = + NumberConfigDelegate(this, key, default) -fun Configurable.child(key: String? = null) = ChildConfigDelegate(key) { SimpleConfigurable(it) } - -fun Configurable.child(key: String? = null, converter: (Config) -> T) = - ChildConfigDelegate(key, converter) +fun > M.child(key: String? = null) = MetaNodeDelegate(this, key) //fun Configurable.spec(spec: Specification, key: String? = null) = ChildConfigDelegate(key) { spec.wrap(this) } @JvmName("safeString") -fun Configurable.string(default: String, key: String? = null) = SafeStringConfigDelegate(key, default) +fun > M.string(default: String, key: String? = null) = + SafeStringConfigDelegate(this, key, default) @JvmName("safeBoolean") -fun Configurable.boolean(default: Boolean, key: String? = null) = SafeBooleanConfigDelegate(key, default) +fun > M.boolean(default: Boolean, key: String? = null) = + SafeBooleanConfigDelegate(this, key, default) @JvmName("safeNumber") -fun Configurable.number(default: Number, key: String? = null) = SafeNumberConfigDelegate(key, default) +fun > M.number(default: Number, key: String? = null) = + SafeNumberConfigDelegate(this, key, default) -inline fun > Configurable.enum(default: E, key: String? = null) = - SafeEnumvConfigDelegate(key, default) { enumValueOf(it) } \ No newline at end of file +inline fun , reified E : Enum> M.enum(default: E, key: String? = null) = + SafeEnumvConfigDelegate(this, key, default) { enumValueOf(it) } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ExtraMetaDelegates.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ExtraMetaDelegates.kt new file mode 100644 index 00000000..d0eaa54e --- /dev/null +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ExtraMetaDelegates.kt @@ -0,0 +1,36 @@ +package hep.dataforge.meta + +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +/* + * Extra delegates for special cases + */ + +/** + * A delegate for a string list + */ +class StringListConfigDelegate( + val config: Config, + private val key: String? = null, + private val default: List = emptyList() +) : + ReadWriteProperty> { + override fun getValue(thisRef: Any?, property: KProperty<*>): List { + return config[key ?: property.name]?.value?.list?.map { it.string } ?: default + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: List) { + val name = key ?: property.name + config[name] = value + } +} + +fun Configurable.stringList(vararg default: String = emptyArray(), key: String? = null) = + StringListConfigDelegate(config, key, default.toList()) + + +fun Metoid.child(key: String? = null, converter: (Meta) -> T) = ChildDelegate(meta, key, converter) + +fun Configurable.child(key: String? = null, converter: (Meta) -> T) = + ChildConfigDelegate(config, key, converter) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt index 094380c8..a1a62760 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt @@ -3,7 +3,7 @@ package hep.dataforge.meta import hep.dataforge.names.NameToken /** - * A meta laminate consisting of multiple immutable meta layers. For mutable front layer, use [StyledConfig]. + * A meta laminate consisting of multiple immutable meta layers. For mutable front layer, use [Styled]. * * */ diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt index 6d00d319..2a9ef237 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -102,21 +102,26 @@ operator fun Meta.iterator(): Iterator> = asValueSequence().it /** * A meta node that ensures that all of its descendants has at least the same type */ -abstract class MetaNode> : Meta { - abstract override val items: Map> +interface MetaNode> : Meta { + override val items: Map> +} - operator fun get(name: Name): MetaItem? { - return name.first()?.let { token -> - val tail = name.cutFirst() - when (tail.length) { - 0 -> items[token] - else -> items[token]?.node?.get(tail) - } +operator fun > MetaNode.get(name: Name): MetaItem? { + return name.first()?.let { token -> + val tail = name.cutFirst() + when (tail.length) { + 0 -> items[token] + else -> items[token]?.node?.get(tail) } } +} - operator fun get(key: String): MetaItem? = get(key.toName()) +operator fun > MetaNode.get(key: String): MetaItem? = get(key.toName()) +/** + * Equals and hash code implementation for meta node + */ +abstract class AbstractMetaNode> : MetaNode { override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is Meta) return false @@ -134,7 +139,8 @@ abstract class MetaNode> : Meta { * * If the argument is possibly mutable node, it is copied on creation */ -class SealedMeta internal constructor(override val items: Map>) : MetaNode() +class SealedMeta internal constructor(override val items: Map>) : + AbstractMetaNode() /** * Generate sealed node from [this]. If it is already sealed return it as is @@ -154,17 +160,19 @@ object EmptyMeta : Meta { * Unsafe methods to access values and nodes directly from [MetaItem] */ -val MetaItem<*>.value +val MetaItem<*>?.value get() = (this as? MetaItem.ValueItem)?.value - ?: (this.node[VALUE_KEY] as? MetaItem.ValueItem)?.value - ?: error("Trying to interpret node meta item as value item") -val MetaItem<*>.string get() = value.string -val MetaItem<*>.boolean get() = value.boolean -val MetaItem<*>.number get() = value.number -val MetaItem<*>.double get() = number.toDouble() -val MetaItem<*>.int get() = number.toInt() -val MetaItem<*>.long get() = number.toLong() -val MetaItem<*>.short get() = number.toShort() + ?: (this?.node?.get(VALUE_KEY) as? MetaItem.ValueItem)?.value + +val MetaItem<*>?.string get() = value?.string +val MetaItem<*>?.boolean get() = value?.boolean +val MetaItem<*>?.number get() = value?.number +val MetaItem<*>?.double get() = number?.toDouble() +val MetaItem<*>?.int get() = number?.toInt() +val MetaItem<*>?.long get() = number?.toLong() +val MetaItem<*>?.short get() = number?.toShort() + +val MetaItem<*>?.stringList get() = value?.list?.map { it.string } ?: emptyList() val MetaItem.node: M get() = when (this) { diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt similarity index 76% rename from dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt rename to dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt index 31998b46..788758fa 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt @@ -6,19 +6,17 @@ import hep.dataforge.names.plus import hep.dataforge.names.toName import hep.dataforge.values.Value -class MetaListener( +internal data class MetaListener( val owner: Any? = null, val action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit -) { - operator fun invoke(name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) = action(name, oldItem, newItem) -} +) -interface MutableMeta> : Meta { +interface MutableMeta> : MetaNode { override val items: Map> operator fun set(name: Name, item: MetaItem?) fun onChange(owner: Any? = null, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit) - fun removeListener(owner: Any) + fun removeListener(owner: Any? = null) } /** @@ -26,7 +24,7 @@ interface MutableMeta> : Meta { * * Changes in Meta are not thread safe. */ -abstract class MutableMetaNode> : MetaNode(), MutableMeta { +abstract class MutableMetaNode> : AbstractMetaNode(), MutableMeta { private val listeners = HashSet() /** @@ -39,7 +37,7 @@ abstract class MutableMetaNode> : MetaNode(), MutableM /** * Remove all listeners belonging to given owner */ - override fun removeListener(owner: Any) { + override fun removeListener(owner: Any?) { listeners.removeAll { it.owner === owner } } @@ -49,7 +47,7 @@ abstract class MutableMetaNode> : MetaNode(), MutableM get() = _items protected fun itemChanged(name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) { - listeners.forEach { it(name, oldItem, newItem) } + listeners.forEach { it.action(name, oldItem, newItem) } } protected open fun replaceItem(key: NameToken, oldItem: MetaItem?, newItem: MetaItem?) { @@ -72,12 +70,12 @@ abstract class MutableMetaNode> : MetaNode(), MutableM * @param name the name of the node where meta should be attached. Needed for correct assignment validators and styles * @param meta the node itself */ - abstract fun wrap(name: Name, meta: Meta): M + internal abstract fun wrap(name: Name, meta: Meta): M /** * Create empty node */ - abstract fun empty(): M + internal abstract fun empty(): M override operator fun set(name: Name, item: MetaItem?) { when (name.length) { @@ -95,20 +93,22 @@ abstract class MutableMetaNode> : MetaNode(), MutableM } } } - - } -fun > M.remove(name: Name) = set(name, null) -fun > M.remove(name: String) = remove(name.toName()) +fun > MutableMeta.remove(name: Name) = set(name, null) +fun > MutableMeta.remove(name: String) = remove(name.toName()) -fun > M.setValue(name: Name, value: Value) = set(name, MetaItem.ValueItem(value)) -fun > M.setItem(name: String, item: MetaItem) = set(name.toName(), item) -fun > M.setValue(name: String, value: Value) = set(name.toName(), MetaItem.ValueItem(value)) -fun > M.setItem(token: NameToken, item: MetaItem?) = set(token.toName(), item) +fun > MutableMeta.setValue(name: Name, value: Value) = set(name, MetaItem.ValueItem(value)) +fun > MutableMeta.setItem(name: String, item: MetaItem) = set(name.toName(), item) +fun > MutableMeta.setValue(name: String, value: Value) = + set(name.toName(), MetaItem.ValueItem(value)) -fun > M.setNode(name: Name, node: Meta) = set(name, MetaItem.NodeItem(wrap(name, node))) -fun > M.setNode(name: String, node: Meta) = setNode(name.toName(), node) +fun > MutableMeta.setItem(token: NameToken, item: MetaItem?) = set(token.toName(), item) + +fun > MutableMetaNode.setNode(name: Name, node: Meta) = + set(name, MetaItem.NodeItem(wrap(name, node))) + +fun > MutableMetaNode.setNode(name: String, node: Meta) = setNode(name.toName(), node) /** * Universal set method @@ -116,6 +116,10 @@ fun > M.setNode(name: String, node: Meta) = setNode(name. operator fun > M.set(name: Name, value: Any?) { when (value) { null -> remove(name) + is MetaItem<*> -> when (value) { + is MetaItem.ValueItem<*> -> setValue(name, value.value) + is MetaItem.NodeItem<*> -> setNode(name, value.node) + } is Meta -> setNode(name, value) else -> setValue(name, Value.of(value)) } 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 fb3e909f..f1e2beaf 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt @@ -55,5 +55,8 @@ fun > S.createStyle(action: C.( Config().also { update(it, action) } -fun Specification.spec(spec: SpecificationCompanion, key: String? = null) = - ChildConfigDelegate(key) { spec.wrap(config) } \ No newline at end of file +fun , C : Specification> Specification.spec( + spec: SpecificationCompanion, + key: String? = null +) = + ChildConfigDelegate(config, key) { spec.wrap(config) } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styleable.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styleable.kt deleted file mode 100644 index d8aaf24e..00000000 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styleable.kt +++ /dev/null @@ -1,68 +0,0 @@ -package hep.dataforge.meta - -import hep.dataforge.names.Name -import hep.dataforge.names.NameToken -import hep.dataforge.names.toName - -/** - * A configuration decorator with applied style - */ -class StyledConfig(val config: Config, style: Meta = EmptyMeta) : Config() { - - var style: Meta = style - set(value) { - field.items.forEach { - itemChanged(it.key.toName(), it.value, null) - } - field = value - value.items.forEach { - itemChanged(it.key.toName(), null, it.value) - } - } - - init { - config.onChange { name, oldItem, newItem -> this.itemChanged(name, oldItem, newItem) } - } - - override fun set(name: Name, item: MetaItem?) { - when (item) { - null -> config.remove(name) - is MetaItem.ValueItem -> config.setValue(name, item.value) - is MetaItem.NodeItem -> config.setNode(name, item.node) - } - } - - override val items: Map> - get() = (config.items.keys + style.items.keys).associate { key -> - val value = config.items[key] - val styleValue = style[key] - val item: MetaItem = when (value) { - null -> when (styleValue) { - null -> error("Should be unreachable") - is MetaItem.ValueItem -> MetaItem.ValueItem(styleValue.value) - is MetaItem.NodeItem -> MetaItem.NodeItem(StyledConfig(config.empty(), styleValue.node)) - } - is MetaItem.ValueItem -> MetaItem.ValueItem(value.value) - is MetaItem.NodeItem -> MetaItem.NodeItem( - StyledConfig(value.node, styleValue?.node ?: EmptyMeta) - ) - } - key to item - } -} - -fun Config.withStyle(style: Meta = EmptyMeta) = if (this is StyledConfig) { - StyledConfig(this.config, style) -} else { - StyledConfig(this, style) -} - -interface Styleable : Configurable { - override val config: StyledConfig - - var style - get() = config.style - set(value) { - config.style = value - } -} \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styled.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styled.kt new file mode 100644 index 00000000..aed8c413 --- /dev/null +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styled.kt @@ -0,0 +1,68 @@ +package hep.dataforge.meta + +import hep.dataforge.names.Name +import hep.dataforge.names.NameToken +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + + +/** + * A meta object with read-only meta base and changeable configuration on top of it + * @param base - unchangeable base + * @param style - the style + */ +class Styled(val base: Meta, val style: Config = Config().empty()) : MutableMeta { + override val items: Map> + get() = (base.items.keys + style.items.keys).associate { key -> + val value = base.items[key] + val styleValue = style[key] + val item: MetaItem = when (value) { + null -> when (styleValue) { + null -> error("Should be unreachable") + is MetaItem.ValueItem -> MetaItem.ValueItem(styleValue.value) + is MetaItem.NodeItem -> MetaItem.NodeItem(Styled(style.empty(), styleValue.node)) + } + is MetaItem.ValueItem -> MetaItem.ValueItem(value.value) + is MetaItem.NodeItem -> MetaItem.NodeItem( + Styled(value.node, styleValue?.node ?: Config.empty()) + ) + } + key to item + } + + override fun set(name: Name, item: MetaItem?) { + if (item == null) { + style.remove(name) + } else { + style.set(name, item) + } + } + + override fun onChange(owner: Any?, action: (Name, before: MetaItem<*>?, after: MetaItem<*>?) -> Unit) { + //TODO test correct behavior + style.onChange(owner) { name, before, after -> action(name, before ?: base[name], after ?: base[name]) } + } + + override fun removeListener(owner: Any?) { + style.removeListener(owner) + } +} + +fun Styled.configure(meta: Meta) = apply { style.update(style) } + +fun Meta.withStyle(style: Meta = EmptyMeta) = if (this is Styled) { + this.apply { this.configure(style) } +} else { + Styled(this, style.toConfig()) +} + +class StyledNodeDelegate(val owner: Styled, val key: String?) : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): Meta { + return owner[key ?: property.name]?.node ?: EmptyMeta + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: Meta) { + owner.style[key ?: property.name] = value + } + +} \ No newline at end of file diff --git a/dataforge-vis/build.gradle.kts b/dataforge-vis/build.gradle.kts new file mode 100644 index 00000000..9ef8235b --- /dev/null +++ b/dataforge-vis/build.gradle.kts @@ -0,0 +1,24 @@ +plugins { + kotlin("multiplatform") +} + +kotlin { + jvm() + js() + + sourceSets { + val commonMain by getting { + dependencies { + api(project(":dataforge-io")) + } + } + val jvmMain by getting { + dependencies { + } + } + val jsMain by getting { + dependencies { + } + } + } +} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-fx/build.gradle.kts b/dataforge-vis/dataforge-vis-fx/build.gradle.kts new file mode 100644 index 00000000..260b8622 --- /dev/null +++ b/dataforge-vis/dataforge-vis-fx/build.gradle.kts @@ -0,0 +1,23 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.openjfx.gradle.JavaFXOptions + +plugins { + kotlin("jvm") + id("org.openjfx.javafxplugin") +} + +dependencies{ + api(project(":dataforge-vis:dataforge-vis-spatial")) + api("no.tornado:tornadofx:1.7.18") + implementation("org.fxyz3d:fxyz3d:0.4.0") +} + +extensions.findByType()?.apply { + modules("javafx.controls") +} + +tasks.withType { + kotlinOptions{ + jvmTarget = "1.8" + } +} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/FXSpatialRenderer.kt b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/FXSpatialRenderer.kt new file mode 100644 index 00000000..5cd9c140 --- /dev/null +++ b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/FXSpatialRenderer.kt @@ -0,0 +1,45 @@ +package hep.dataforge.vis.spatial + +import hep.dataforge.context.Context +import hep.dataforge.io.Output +import hep.dataforge.meta.Meta +import javafx.scene.* +import javafx.scene.paint.Color +import org.fxyz3d.geometry.Point3D +import org.fxyz3d.shapes.primitives.CuboidMesh +import org.fxyz3d.utils.CameraTransformer + +class FXSpatialRenderer(override val context: Context) : Output { + + private val world: Group = Group() + + val camera = PerspectiveCamera() + + val cameraTransform = CameraTransformer().apply { + children.add(camera) + } + + val canvas: SubScene = SubScene( + Group(world, cameraTransform).apply { DepthTest.ENABLE }, + 1024.0, + 768.0, + true, + SceneAntialiasing.BALANCED + ).apply { + fill = Color.GREY + this.camera = this@FXSpatialRenderer.camera + id = "canvas" + } + + private fun buildObject(obj: DisplayObject3D): Node { + val center = Point3D(obj.x.toFloat(), obj.y.toFloat(), obj.z.toFloat()) + return when (obj) { + is Box3D -> CuboidMesh(obj.xSize, obj.ySize, obj.zSize).apply { this.center = center } + else -> TODO() + } + } + + override fun render(obj: DisplayObject3D, meta: Meta) { + world.children.add(buildObject(obj)) + } +} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt new file mode 100644 index 00000000..8ff1cc68 --- /dev/null +++ b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt @@ -0,0 +1,44 @@ +package hep.dataforge.vis.spatial + +import hep.dataforge.context.Global +import hep.dataforge.meta.EmptyMeta +import javafx.scene.Parent +import tornadofx.* + + +class RendererDemoApp: App(RendererDemoView::class) + + +class RendererDemoView: View(){ + val renderer = FXSpatialRenderer(Global) + override val root: Parent = borderpane{ + center = renderer.canvas + } + + init { + val cube = Box3D(null, EmptyMeta).apply { + xSize = 100.0 + ySize = 100.0 + zSize = 100.0 + } + renderer.render(cube) + + renderer.camera.apply { + nearClip = 0.1 + farClip = 10000.0 + translateX = -200.0 + translateY = -200.0 + fieldOfView = 20.0 + } + + renderer.cameraTransform.apply{ + ry.angle = -30.0 + rx.angle = -15.0 + } + } +} + + +fun main() { + launch() +} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-js/build.gradle.kts b/dataforge-vis/dataforge-vis-js/build.gradle.kts new file mode 100644 index 00000000..f5e385a6 --- /dev/null +++ b/dataforge-vis/dataforge-vis-js/build.gradle.kts @@ -0,0 +1,42 @@ +plugins{ + kotlin("js") + id("kotlin") +} + +// configure(listOf(compilations.main, compilations.test)) { +// tasks.getByName(compileKotlinTaskName).kotlinOptions { +// sourceMap = true +// moduleKind = "umd" +// metaInfo = true +// } +// } +// +// configure(compilations.main) { +// tasks.getByName(compileKotlinTaskName).kotlinOptions { +// main = "call" +// } +// } + +dependencies { + implementation("info.laht.threekt:threejs-wrapper:0.88-npm-1") +} + +extensions.findByType()?.apply { + extensions.findByType()?.apply { + dependency("three") + dependency("three-orbitcontrols") + devDependency("karma") + + } + + sourceMaps = true + + bundle("webpack") { + this as WebPackExtension + bundleName = "main" + proxyUrl = "http://localhost:8080" + contentPath = file("src/main/web") + sourceMapEnabled = true + mode = "development" + } +} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial/build.gradle.kts b/dataforge-vis/dataforge-vis-spatial/build.gradle.kts new file mode 100644 index 00000000..c76b3f57 --- /dev/null +++ b/dataforge-vis/dataforge-vis-spatial/build.gradle.kts @@ -0,0 +1,26 @@ +plugins { + kotlin("multiplatform") +} + +kotlin { + jvm() + js() + + sourceSets { + val commonMain by getting { + dependencies { + api(project(":dataforge-vis")) + } + } + val jvmMain by getting { + dependencies { + + } + } + val jsMain by getting { + dependencies { + + } + } + } +} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/DisplayObject3D.kt b/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/DisplayObject3D.kt new file mode 100644 index 00000000..a1d81c81 --- /dev/null +++ b/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/DisplayObject3D.kt @@ -0,0 +1,28 @@ +package hep.dataforge.vis.spatial + +import hep.dataforge.meta.Meta +import hep.dataforge.vis.DisplayLeaf +import hep.dataforge.vis.DisplayObject +import hep.dataforge.vis.double + + +open class DisplayObject3D(parent: DisplayObject?, type: String, meta: Meta) : DisplayLeaf(parent, type, meta) { + var x by double(0.0) + var y by double(0.0) + var z by double(0.0) + + companion object { + const val TYPE = "geometry.spatial" + } +} + +class Box3D(parent: DisplayObject?, meta: Meta) : DisplayObject3D(parent, + TYPE, meta) { + var xSize by double(1.0) + var ySize by double(1.0) + var zSize by double(1.0) + + companion object { + const val TYPE = "geometry.spatial.box" + } +} diff --git a/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObject.kt b/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObject.kt new file mode 100644 index 00000000..53d97074 --- /dev/null +++ b/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObject.kt @@ -0,0 +1,143 @@ +package hep.dataforge.vis + +import hep.dataforge.meta.* +import hep.dataforge.names.Name +import hep.dataforge.vis.DisplayObject.Companion.DEFAULT_TYPE +import hep.dataforge.vis.DisplayObject.Companion.META_KEY +import hep.dataforge.vis.DisplayObject.Companion.TAGS_KEY + +/** + * A root type for display hierarchy + */ +interface DisplayObject { + + /** + * The parent object of this one. If null, this one is a root. + */ + val parent: DisplayObject? + + /** + * The type of this object. Uses `.` notation. Empty type means untyped group + */ + val type: String + + val properties: Styled + + companion object { + const val DEFAULT_TYPE = "" + const val TYPE_KEY = "@type" + const val CHILDREN_KEY = "@children" + const val META_KEY = "@meta" + const val TAGS_KEY = "@tags" + } +} + +interface DisplayGroup : DisplayObject { + + val children: List + + /** + * Add a child object and notify listeners + */ + fun addChild(obj: DisplayObject) + + /** + * Remove a specific child and notify listeners + */ + fun removeChild(obj: DisplayObject) + + /** + * Add listener for children change + * TODO add detailed information into change listener + */ + fun onChildrenChange(owner: Any? = null, action: () -> Unit) + + /** + * Remove children change listener + */ + fun removeChildrenChangeListener(owner: Any? = null) +} + +/** + * Get the property of this display object of parent's if not found + */ +tailrec fun DisplayObject.getProperty(name: Name): MetaItem<*>? = properties[name] ?: parent?.getProperty(name) + +/** + * A change listener for [DisplayObject] configuration. + */ +fun DisplayObject.onChange(owner: Any?, action: (Name, before: MetaItem<*>?, after: MetaItem<*>?) -> Unit) = + properties.style.onChange(owner, action) + +/** + * Remove all meta listeners with matching owners + */ +fun DisplayObject.removeChangeListener(owner: Any?) = + properties.style.removeListener(owner) + + +/** + * Additional meta not relevant to display + */ +val DisplayObject.meta: Meta get() = properties[META_KEY]?.node ?: EmptyMeta + +val DisplayObject.tags: List get() = properties[TAGS_KEY].stringList + +internal data class ObjectListener( + val owner: Any?, + val action: () -> Unit +) + +/** + * Basic group of display objects + */ +open class DisplayNode( + override val parent: DisplayObject?, + override val type: String = DEFAULT_TYPE, + meta: Meta = EmptyMeta +) : DisplayGroup { + + private val _children = ArrayList() + override val children: List get() = _children + override val properties = Styled(meta) + private val listeners = HashSet() + + override fun addChild(obj: DisplayObject) { +// val before = _children[name] +// if (obj == null) { +// _children.remove(name) +// } else { +// _children[name] = obj +// } +// listeners.forEach { it.action(name, before, obj) } + _children.add(obj) + listeners.forEach { it.action() } + } + + override fun removeChild(obj: DisplayObject) { + if(_children.remove(obj)){ + listeners.forEach { it.action } + } + } + + override fun onChildrenChange(owner: Any?, action: () -> Unit) { + listeners.add(ObjectListener(owner, action)) + } + + + override fun removeChildrenChangeListener(owner: Any?) { + listeners.removeAll { it.owner === owner } + } +} + +/** + * Basic [DisplayObject] leaf element + */ +open class DisplayLeaf( + override val parent: DisplayObject?, + override val type: String, + meta: Meta = EmptyMeta +) : DisplayObject { + final override val properties = Styled(meta) +} + diff --git a/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObjectDelegates.kt b/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObjectDelegates.kt new file mode 100644 index 00000000..b3a2e042 --- /dev/null +++ b/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObjectDelegates.kt @@ -0,0 +1,48 @@ +package hep.dataforge.vis + +import hep.dataforge.meta.* +import hep.dataforge.values.Null +import hep.dataforge.values.Value +import kotlin.jvm.JvmName + +fun DisplayObject.value(default: Value = Null, key: String? = null) = + ValueConfigDelegate(properties, key, default) + +fun DisplayObject.string(default: String? = null, key: String? = null) = + StringConfigDelegate(properties, key, default) + +fun DisplayObject.boolean(default: Boolean? = null, key: String? = null) = + BooleanConfigDelegate(properties, key, default) + +fun DisplayObject.number(default: Number? = null, key: String? = null) = + NumberConfigDelegate(properties, key, default) + +fun DisplayObject.double(default: Double? = null, key: String? = null) = + NumberConfigDelegate(properties, key, default).double + +fun DisplayObject.int(default: Int? = null, key: String? = null) = + NumberConfigDelegate(properties, key, default).int + + +fun DisplayObject.node(key: String? = null) = StyledNodeDelegate(properties, key) + +//fun Configurable.spec(spec: Specification, key: String? = null) = ChildConfigDelegate(key) { spec.wrap(this) } + +@JvmName("safeString") +fun DisplayObject.string(default: String, key: String? = null) = + SafeStringConfigDelegate(properties, key, default) + +@JvmName("safeBoolean") +fun DisplayObject.boolean(default: Boolean, key: String? = null) = + SafeBooleanConfigDelegate(properties, key, default) + +@JvmName("safeNumber") +fun DisplayObject.number(default: Number, key: String? = null) = + SafeNumberConfigDelegate(properties, key, default) + +@JvmName("safeDouble") +fun DisplayObject.double(default: Double, key: String? = null) = + SafeNumberConfigDelegate(properties, key, default).double + +inline fun > DisplayObject.enum(default: E, key: String? = null) = + SafeEnumvConfigDelegate(properties, key, default) { enumValueOf(it) } \ No newline at end of file diff --git a/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/NamedObject.kt b/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/NamedObject.kt new file mode 100644 index 00000000..bc15d9e6 --- /dev/null +++ b/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/NamedObject.kt @@ -0,0 +1,40 @@ +package hep.dataforge.vis + +import hep.dataforge.names.Name +import hep.dataforge.names.NameToken + +interface NamedObject : DisplayObject { + val name: String + + operator fun get(nameToken: NameToken): DisplayGroup? + + operator fun set(nameToken: NameToken, group: DisplayGroup) +} + +/** + * Recursively get a child + */ +tailrec operator fun NamedObject.get(name: Name): DisplayObject? = when (name.length) { + 0 -> this + 1 -> this[name[0]] + else -> name.first()?.let { this[it] as? NamedObject }?.get(name.cutFirst()) +} + + +/** + * Set given object creating intermediate empty groups if needed + * @param name - the full name of a child + * @param objFactory - a function that creates child object from parent (to avoid mutable parent parameter) + */ +fun NamedObject.set(name: Name, objFactory: (parent: DisplayObject) -> DisplayGroup): Unit = when (name.length) { + 0 -> error("Can't set object with empty name") + 1 -> set(name[0], objFactory(this)) + else -> (this[name.first()!!] ?: DisplayNode(this)) + .run { + if (this is NamedObject) { + this.set(name.cutFirst(), objFactory) + } else { + error("Can't assign child to a leaf element $this") + } + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..06efe6a4 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,7 @@ +# Enable official Kotlin Code Style in the IDE. +kotlin.code.style=official + +artifactoryUser=darksnake +artifactoryPassword=nortlander +bintrayUser=altavir +bintrayApiKey=9dcb7a779986e1b08898980269b6d428cadda0c3 \ No newline at end of file diff --git a/gradle/artifactory.gradle b/gradle/artifactory.gradle new file mode 100644 index 00000000..12e59642 --- /dev/null +++ b/gradle/artifactory.gradle @@ -0,0 +1,31 @@ +apply plugin: "com.jfrog.artifactory" + +artifactory { + def artifactory_user = project.hasProperty('artifactoryUser') ? project.property('artifactoryUser') : "" + def artifactory_password = project.hasProperty('artifactoryPassword') ? project.property('artifactoryPassword') : "" + def artifactory_contextUrl = 'http://npm.mipt.ru:8081/artifactory' + + contextUrl = artifactory_contextUrl //The base Artifactory URL if not overridden by the publisher/resolver + publish { + repository { + repoKey = 'gradle-dev-local' + username = artifactory_user + password = artifactory_password + } + + defaults { + publications('jvm', 'js', 'kotlinMultiplatform', 'metadata') + publishBuildInfo = false + publishArtifacts = true + publishPom = true + publishIvy = false + } + } + resolve { + repository { + repoKey = 'gradle-dev' + username = artifactory_user + password = artifactory_password + } + } +} \ No newline at end of file diff --git a/gradle/bintray.gradle b/gradle/bintray.gradle new file mode 100644 index 00000000..8da83c86 --- /dev/null +++ b/gradle/bintray.gradle @@ -0,0 +1,85 @@ +apply plugin: 'com.jfrog.bintray' + +def vcs = "https://github.com/mipt-npm/kmath" + +def pomConfig = { + licenses { + license { + name "The Apache Software License, Version 2.0" + url "http://www.apache.org/licenses/LICENSE-2.0.txt" + distribution "repo" + } + } + developers { + developer { + id "MIPT-NPM" + name "MIPT nuclear physics methods laboratory" + organization "MIPT" + organizationUrl "http://npm.mipt.ru" + } + } + scm { + url vcs + } +} + +project.ext.configureMavenCentralMetadata = { pom -> + def root = asNode() + root.appendNode('name', project.name) + root.appendNode('description', project.description) + root.appendNode('url', vcs) + root.children().last() + pomConfig +} + +project.ext.configurePom = pomConfig + + +// Configure publishing +publishing { + repositories { + maven { + url = "https://bintray.com/mipt-npm/scientifik" + } + } + + // Process each publication we have in this project + publications.all { publication -> + // apply changes to pom.xml files, see pom.gradle + pom.withXml(configureMavenCentralMetadata) + + + } +} + +bintray { + user = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : System.getenv('BINTRAY_USER') + key = project.hasProperty('bintrayApiKey') ? project.property('bintrayApiKey') : System.getenv('BINTRAY_API_KEY') + publish = true + override = true // for multi-platform Kotlin/Native publishing + + pkg { + userOrg = "mipt-npm" + repo = "scientifik" + name = "scientifik.kmath" + issueTrackerUrl = "https://github.com/mipt-npm/kmath/issues" + licenses = ['Apache-2.0'] + vcsUrl = vcs + version { + name = project.version + vcsTag = project.version + released = new Date() + } + } +} + +bintrayUpload.dependsOn publishToMavenLocal + +// This is for easier debugging of bintray uploading problems +bintrayUpload.doFirst { + publications = project.publishing.publications.findAll { + !it.name.contains('-test') && it.name != 'kotlinMultiplatform' + }.collect { + println("Uploading artifact '$it.groupId:$it.artifactId:$it.version' from publication '$it.name'") + it.name//https://github.com/bintray/gradle-bintray-plugin/issues/256 + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..87b738cbd051603d91cc39de6cb000dd98fe6b02 GIT binary patch literal 55190 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 z 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..6d57edc7 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle.kts b/settings.gradle.kts index 75af1911..662388fe 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,11 +1,22 @@ pluginManagement { repositories { + mavenCentral() jcenter() - maven("https://plugins.gradle.org/m2/") + gradlePluginPortal() + maven("https://dl.bintray.com/kotlin/kotlin-eap/") + } + resolutionStrategy { + eachPlugin { + when (requested.id.id) { + "kotlinx-atomicfu" -> useModule("org.jetbrains.kotlinx:atomicfu-gradle-plugin:${requested.version}") + "kotlin-multiplatform" -> useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") + "org.jetbrains.kotlin.frontend" -> useModule("org.jetbrains.kotlin:kotlin-frontend-plugin:0.0.45") + } + } } } -//enableFeaturePreview("GRADLE_METADATA") +enableFeaturePreview("GRADLE_METADATA") rootProject.name = "dataforge-core" include( @@ -15,5 +26,8 @@ include( ":dataforge-data", ":dataforge-io", ":dataforge-workspace", - ":dataforge-scripting" + ":dataforge-scripting", + ":dataforge-vis", + ":dataforge-vis:dataforge-vis-spatial", + ":dataforge-vis:dataforge-vis-fx" ) From 0d15bc1d0b6cd5857a7273b2dd4852629efa06e5 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 3 Mar 2019 15:23:21 +0300 Subject: [PATCH 30/37] Minors fixes to Meta and JsonMetaFormat. Visualization moving forward --- .../hep/dataforge/meta/io/JsonMetaFormat.kt | 37 ++-- .../kotlin/hep/dataforge/meta/Laminate.kt | 2 +- .../kotlin/hep/dataforge/meta/Meta.kt | 3 +- .../vis/DisplayObjectPropertyListener.kt | 38 ++++ .../vis/spatial/FXSpatialRenderer.kt | 185 +++++++++++++++++- .../dataforge/vis/spatial/RendererDemoApp.kt | 54 +++-- .../kotlin/hep/dataforge/vis/spatial/Box.kt | 20 ++ .../dataforge/vis/spatial/DisplayObject3D.kt | 21 +- .../hep/dataforge/vis/spatial/Geometry.kt | 8 + .../kotlin/hep/dataforge/vis/DisplayObject.kt | 12 +- .../dataforge/vis/DisplayObjectDelegates.kt | 102 +++++++--- 11 files changed, 396 insertions(+), 86 deletions(-) create mode 100644 dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/DisplayObjectPropertyListener.kt create mode 100644 dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt create mode 100644 dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Geometry.kt diff --git a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/JsonMetaFormat.kt b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/JsonMetaFormat.kt index c9c67201..2c82c085 100644 --- a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/JsonMetaFormat.kt +++ b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/JsonMetaFormat.kt @@ -20,8 +20,13 @@ object JsonMetaFormat : MetaFormat { override fun read(input: Input): Meta { val str = input.readText() - val json = JsonTreeParser.parse(str) - return json.toMeta() + val json = Json.plain.parseJson(str) + + if(json is JsonObject) { + return json.toMeta() + } else { + TODO("non-object root") + } } } @@ -45,6 +50,20 @@ fun Meta.toJson(): JsonObject { return JsonObject(map) } + +fun JsonElement.toMetaItem() = when (this) { + is JsonPrimitive -> MetaItem.ValueItem(this.toValue()) + is JsonObject -> MetaItem.NodeItem(this.toMeta()) + is JsonArray -> { + if (this.all { it is JsonPrimitive }) { + val value = ListValue(this.map { (it as JsonPrimitive).toValue() }) + MetaItem.ValueItem(value) + } else { + TODO("mixed nodes json") + } + } +} + fun JsonObject.toMeta() = JsonMeta(this) private fun JsonPrimitive.toValue(): Value { @@ -57,19 +76,7 @@ private fun JsonPrimitive.toValue(): Value { class JsonMeta(val json: JsonObject) : Meta { override val items: Map> by lazy { json.mapKeys { NameToken(it.key) }.mapValues { entry -> - val element = entry.value - when (element) { - is JsonPrimitive -> MetaItem.ValueItem(element.toValue()) - is JsonObject -> MetaItem.NodeItem(element.toMeta()) - is JsonArray -> { - if (element.all { it is JsonPrimitive }) { - val value = ListValue(element.map { (it as JsonPrimitive).toValue() }) - MetaItem.ValueItem(value) - } else { - TODO("mixed nodes json") - } - } - } + entry.value.toMetaItem() } } } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt index a1a62760..b16248ac 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt @@ -49,7 +49,7 @@ class Laminate(layers: List) : Meta { first().seal() all { it is MetaItem.NodeItem } -> { //list nodes in item - val nodes = map { it.node } + val nodes = map { (it as MetaItem.NodeItem).node } //represent as key->value entries val entries = nodes.flatMap { it.items.entries.asSequence() } //group by keys diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt index 2a9ef237..57affe76 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -174,8 +174,9 @@ val MetaItem<*>?.short get() = number?.toShort() val MetaItem<*>?.stringList get() = value?.list?.map { it.string } ?: emptyList() -val MetaItem.node: M +val MetaItem?.node: M? get() = when (this) { + null -> null is MetaItem.ValueItem -> error("Trying to interpret value meta item as node item") is MetaItem.NodeItem -> node } diff --git a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/DisplayObjectPropertyListener.kt b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/DisplayObjectPropertyListener.kt new file mode 100644 index 00000000..63eb25c5 --- /dev/null +++ b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/DisplayObjectPropertyListener.kt @@ -0,0 +1,38 @@ +package hep.dataforge.vis + +import hep.dataforge.meta.* +import hep.dataforge.names.Name +import hep.dataforge.names.toName +import javafx.beans.binding.ObjectBinding +import tornadofx.* + +class DisplayObjectPropertyListener(val obj: DisplayObject) { + private val binndings = HashMap?>>() + + init { + obj.onChange(this) { name, _, _ -> + binndings[name]?.invalidate() + } + } + + operator fun get(key: Name): ObjectBinding?> { + return binndings.getOrPut(key) { + object : ObjectBinding?>() { + override fun computeValue(): MetaItem<*>? = obj.getProperty(key) + } + } + } + + operator fun get(key: String) = get(key.toName()) +} + +fun ObjectBinding?>.value() = this.objectBinding { it.value } +fun ObjectBinding?>.string() = this.stringBinding { it.string } +fun ObjectBinding?>.number() = this.objectBinding { it.number } +fun ObjectBinding?>.double() = this.objectBinding { it.double } +fun ObjectBinding?>.float() = this.objectBinding { it.number?.toFloat() } +fun ObjectBinding?>.int() = this.objectBinding { it.int } +fun ObjectBinding?>.long() = this.objectBinding { it.long } +fun ObjectBinding?>.node() = this.objectBinding { it.node } + +fun ObjectBinding?>.transform(transform: (MetaItem<*>) -> T) = this.objectBinding { it?.let(transform) } diff --git a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/FXSpatialRenderer.kt b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/FXSpatialRenderer.kt index 5cd9c140..829ec644 100644 --- a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/FXSpatialRenderer.kt +++ b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/FXSpatialRenderer.kt @@ -3,24 +3,56 @@ package hep.dataforge.vis.spatial import hep.dataforge.context.Context import hep.dataforge.io.Output import hep.dataforge.meta.Meta +import hep.dataforge.meta.int +import hep.dataforge.vis.DisplayGroup +import hep.dataforge.vis.DisplayObject +import hep.dataforge.vis.DisplayObjectPropertyListener +import hep.dataforge.vis.transform +import javafx.event.EventHandler import javafx.scene.* +import javafx.scene.input.KeyCode +import javafx.scene.input.KeyEvent +import javafx.scene.input.MouseEvent +import javafx.scene.input.ScrollEvent import javafx.scene.paint.Color +import javafx.scene.paint.PhongMaterial import org.fxyz3d.geometry.Point3D import org.fxyz3d.shapes.primitives.CuboidMesh import org.fxyz3d.utils.CameraTransformer -class FXSpatialRenderer(override val context: Context) : Output { + +/** + * https://github.com/miho/JCSG for operations + * + * TODO move world and camera boilerplate to another class + */ +class FXSpatialRenderer(override val context: Context) : Output { private val world: Group = Group() - val camera = PerspectiveCamera() - - val cameraTransform = CameraTransformer().apply { - children.add(camera) + private val camera = PerspectiveCamera().apply { + nearClip = CAMERA_NEAR_CLIP + farClip = CAMERA_FAR_CLIP + translateZ = CAMERA_INITIAL_DISTANCE } + val cameraShift = CameraTransformer().apply { + val cameraFlip = CameraTransformer() + cameraFlip.children.add(camera) + cameraFlip.setRotateZ(180.0) + children.add(cameraFlip) + } + + val cameraRotation = CameraTransformer().apply { + children.add(cameraShift) + ry.angle = CAMERA_INITIAL_Y_ANGLE + rx.angle = CAMERA_INITIAL_X_ANGLE + rz.angle = CAMERA_INITIAL_Z_ANGLE + } + + val canvas: SubScene = SubScene( - Group(world, cameraTransform).apply { DepthTest.ENABLE }, + Group(world, cameraRotation).apply { DepthTest.ENABLE }, 1024.0, 768.0, true, @@ -29,17 +61,150 @@ class FXSpatialRenderer(override val context: Context) : Output fill = Color.GREY this.camera = this@FXSpatialRenderer.camera id = "canvas" + handleKeyboard(this) + handleMouse(this) } - private fun buildObject(obj: DisplayObject3D): Node { - val center = Point3D(obj.x.toFloat(), obj.y.toFloat(), obj.z.toFloat()) + private fun buildObject(obj: DisplayObject): Node { return when (obj) { - is Box3D -> CuboidMesh(obj.xSize, obj.ySize, obj.zSize).apply { this.center = center } + is DisplayGroup -> Group(obj.children.map { buildObject(it) }) + is Box -> CuboidMesh(obj.xSize, obj.ySize, obj.zSize).apply { + val listener = DisplayObjectPropertyListener(obj) + this.center = Point3D(obj.x.toFloat(), obj.y.toFloat(), obj.z.toFloat()) + this.diffuseColorProperty().bind(listener["color"].transform { + val int = it.int ?: 0 + val red = int and 0x00ff0000 shr 16 + val green = int and 0x0000ff00 shr 8 + val blue = int and 0x000000ff + return@transform Color.rgb(red, green, blue) + }) + } else -> TODO() } } - override fun render(obj: DisplayObject3D, meta: Meta) { + override fun render(obj: DisplayObject, meta: Meta) { world.children.add(buildObject(obj)) } + + private fun handleKeyboard(scene: SubScene) { + scene.onKeyPressed = EventHandler { event -> + if (event.isControlDown) { + when (event.code) { + KeyCode.Z -> { + cameraShift.t.x = 0.0 + cameraShift.t.y = 0.0 + camera.translateZ = CAMERA_INITIAL_DISTANCE + cameraRotation.ry.angle = CAMERA_INITIAL_Y_ANGLE + cameraRotation.rx.angle = CAMERA_INITIAL_X_ANGLE + } +// KeyCode.X -> axisGroup.isVisible = !axisGroup.isVisible +// KeyCode.S -> snapshot() +// KeyCode.DIGIT1 -> pixelMap.filterKeys { it.getLayerNumber() == 1 }.values.forEach { +// toggleTransparency( +// it +// ) +// } +// KeyCode.DIGIT2 -> pixelMap.filterKeys { it.getLayerNumber() == 2 }.values.forEach { +// toggleTransparency( +// it +// ) +// } +// KeyCode.DIGIT3 -> pixelMap.filterKeys { it.getLayerNumber() == 3 }.values.forEach { +// toggleTransparency( +// it +// ) +// } + else -> { + }//do nothing + } + } + } + } + + private fun handleMouse(scene: SubScene) { + + var mousePosX: Double = 0.0 + var mousePosY: Double = 0.0 + var mouseOldX: Double = 0.0 + var mouseOldY: Double = 0.0 + var mouseDeltaX: Double = 0.0 + var mouseDeltaY: Double = 0.0 + + scene.onMousePressed = EventHandler { me -> + mousePosX = me.sceneX + mousePosY = me.sceneY + mouseOldX = me.sceneX + mouseOldY = me.sceneY + } + + scene.onMouseDragged = EventHandler { me -> + mouseOldX = mousePosX + mouseOldY = mousePosY + mousePosX = me.sceneX + mousePosY = me.sceneY + mouseDeltaX = mousePosX - mouseOldX + mouseDeltaY = mousePosY - mouseOldY + + val modifier = when { + me.isControlDown -> CONTROL_MULTIPLIER + me.isShiftDown -> SHIFT_MULTIPLIER + else -> 1.0 + } + + if (me.isPrimaryButtonDown) { + cameraRotation.rz.angle = + cameraRotation.rz.angle + mouseDeltaX * MOUSE_SPEED * modifier * ROTATION_SPEED + cameraRotation.rx.angle = + cameraRotation.rx.angle + mouseDeltaY * MOUSE_SPEED * modifier * ROTATION_SPEED + // } else if (me.isSecondaryButtonDown()) { + // double z = camera.getTranslateZ(); + // double newZ = z + mouseDeltaX * MOUSE_SPEED * modifier*100; + // camera.setTranslateZ(newZ); + } else if (me.isSecondaryButtonDown) { + cameraShift.t.x = cameraShift.t.x + mouseDeltaX * MOUSE_SPEED * modifier * TRACK_SPEED + cameraShift.t.y = cameraShift.t.y + mouseDeltaY * MOUSE_SPEED * modifier * TRACK_SPEED + } + } + scene.onScroll = EventHandler { event -> + val z = camera.translateZ + val newZ = z + MOUSE_SPEED * event.deltaY * RESIZE_SPEED + camera.translateZ = newZ + } + } + + companion object { + private const val CAMERA_INITIAL_DISTANCE = -4500.0 + private const val CAMERA_INITIAL_X_ANGLE = -50.0 + private const val CAMERA_INITIAL_Y_ANGLE = 0.0 + private const val CAMERA_INITIAL_Z_ANGLE = -210.0 + private const val CAMERA_NEAR_CLIP = 0.1 + private const val CAMERA_FAR_CLIP = 10000.0 + private const val AXIS_LENGTH = 2000.0 + private const val CONTROL_MULTIPLIER = 0.1 + private const val SHIFT_MULTIPLIER = 10.0 + private const val MOUSE_SPEED = 0.1 + private const val ROTATION_SPEED = 2.0 + private const val TRACK_SPEED = 6.0 + private const val RESIZE_SPEED = 50.0 + private const val LINE_WIDTH = 3.0 + + private val redMaterial = PhongMaterial().apply { + diffuseColor = Color.DARKRED + specularColor = Color.RED + } + + private val whiteMaterial = PhongMaterial().apply { + diffuseColor = Color.WHITE + specularColor = Color.LIGHTBLUE + } + + private val greyMaterial = PhongMaterial().apply { + diffuseColor = Color.DARKGREY + specularColor = Color.GREY + } + + private val blueMaterial = PhongMaterial(Color.BLUE) + + } } \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt index 8ff1cc68..41cbb82f 100644 --- a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt +++ b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt @@ -1,37 +1,57 @@ package hep.dataforge.vis.spatial import hep.dataforge.context.Global -import hep.dataforge.meta.EmptyMeta +import hep.dataforge.meta.number +import hep.dataforge.vis.DisplayGroup import javafx.scene.Parent +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch import tornadofx.* +import kotlin.random.Random -class RendererDemoApp: App(RendererDemoView::class) +class RendererDemoApp : App(RendererDemoView::class) -class RendererDemoView: View(){ +class RendererDemoView : View() { val renderer = FXSpatialRenderer(Global) - override val root: Parent = borderpane{ + override val root: Parent = borderpane { center = renderer.canvas } + lateinit var group: DisplayGroup + init { - val cube = Box3D(null, EmptyMeta).apply { - xSize = 100.0 - ySize = 100.0 - zSize = 100.0 - } - renderer.render(cube) - renderer.camera.apply { - nearClip = 0.1 - farClip = 10000.0 - translateX = -200.0 - translateY = -200.0 - fieldOfView = 20.0 + renderer.render { + group = group { + box { + xSize = 100.0 + ySize = 100.0 + zSize = 100.0 + } + box { + x = 110.0 + xSize = 100.0 + ySize = 100.0 + zSize = 100.0 + } + } } - renderer.cameraTransform.apply{ + var color by group.properties.number(1530).int + + GlobalScope.launch { + val random = Random(111) + while (isActive) { + delay(1000) + color = random.nextInt(0, Int.MAX_VALUE) + } + } + + renderer.cameraRotation.apply { ry.angle = -30.0 rx.angle = -15.0 } diff --git a/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt b/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt new file mode 100644 index 00000000..5c0db659 --- /dev/null +++ b/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt @@ -0,0 +1,20 @@ +package hep.dataforge.vis.spatial + +import hep.dataforge.meta.EmptyMeta +import hep.dataforge.meta.Meta +import hep.dataforge.vis.DisplayGroup +import hep.dataforge.vis.DisplayObject +import hep.dataforge.vis.double + +class Box(parent: DisplayObject?, meta: Meta) : DisplayObject3D(parent, TYPE, meta) { + var xSize by double(1.0) + var ySize by double(1.0) + var zSize by double(1.0) + + companion object { + const val TYPE = "geometry.spatial.box" + } +} + +fun DisplayGroup.box(meta: Meta = EmptyMeta, action: Box.() -> Unit = {}) = + Box(this, meta).apply(action).also { addChild(it) } \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/DisplayObject3D.kt b/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/DisplayObject3D.kt index a1d81c81..be72fbd8 100644 --- a/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/DisplayObject3D.kt +++ b/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/DisplayObject3D.kt @@ -1,9 +1,10 @@ package hep.dataforge.vis.spatial +import hep.dataforge.io.Output +import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.Meta -import hep.dataforge.vis.DisplayLeaf -import hep.dataforge.vis.DisplayObject -import hep.dataforge.vis.double +import hep.dataforge.vis.* +import hep.dataforge.vis.DisplayObject.Companion.DEFAULT_TYPE open class DisplayObject3D(parent: DisplayObject?, type: String, meta: Meta) : DisplayLeaf(parent, type, meta) { @@ -16,13 +17,9 @@ open class DisplayObject3D(parent: DisplayObject?, type: String, meta: Meta) : D } } -class Box3D(parent: DisplayObject?, meta: Meta) : DisplayObject3D(parent, - TYPE, meta) { - var xSize by double(1.0) - var ySize by double(1.0) - var zSize by double(1.0) +fun DisplayGroup.group(meta: Meta = EmptyMeta, action: DisplayGroup.() -> Unit = {}) = + DisplayNode(this, DEFAULT_TYPE, meta).apply(action).also{addChild(it)} - companion object { - const val TYPE = "geometry.spatial.box" - } -} + +fun Output.render(meta: Meta = EmptyMeta, action: DisplayGroup.() -> Unit) = + render(DisplayNode(null, DEFAULT_TYPE, EmptyMeta).apply(action), meta) diff --git a/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Geometry.kt b/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Geometry.kt new file mode 100644 index 00000000..53766a49 --- /dev/null +++ b/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Geometry.kt @@ -0,0 +1,8 @@ +package hep.dataforge.vis.spatial + +//TODO replace by platform optimized version +data class Point3D( + val x: Float, + val y: Float, + val z: Float +) \ No newline at end of file diff --git a/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObject.kt b/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObject.kt index 53d97074..cb9dd110 100644 --- a/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObject.kt +++ b/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObject.kt @@ -66,14 +66,18 @@ tailrec fun DisplayObject.getProperty(name: Name): MetaItem<*>? = properties[nam /** * A change listener for [DisplayObject] configuration. */ -fun DisplayObject.onChange(owner: Any?, action: (Name, before: MetaItem<*>?, after: MetaItem<*>?) -> Unit) = +fun DisplayObject.onChange(owner: Any?, action: (Name, before: MetaItem<*>?, after: MetaItem<*>?) -> Unit) { properties.style.onChange(owner, action) + parent?.onChange(owner, action) +} /** * Remove all meta listeners with matching owners */ -fun DisplayObject.removeChangeListener(owner: Any?) = +fun DisplayObject.removeChangeListener(owner: Any?) { properties.style.removeListener(owner) + parent?.removeChangeListener(owner) +} /** @@ -92,7 +96,7 @@ internal data class ObjectListener( * Basic group of display objects */ open class DisplayNode( - override val parent: DisplayObject?, + override val parent: DisplayObject? = null, override val type: String = DEFAULT_TYPE, meta: Meta = EmptyMeta ) : DisplayGroup { @@ -115,7 +119,7 @@ open class DisplayNode( } override fun removeChild(obj: DisplayObject) { - if(_children.remove(obj)){ + if (_children.remove(obj)) { listeners.forEach { it.action } } } diff --git a/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObjectDelegates.kt b/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObjectDelegates.kt index b3a2e042..d99ad547 100644 --- a/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObjectDelegates.kt +++ b/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObjectDelegates.kt @@ -1,48 +1,98 @@ package hep.dataforge.vis import hep.dataforge.meta.* -import hep.dataforge.values.Null +import hep.dataforge.names.Name +import hep.dataforge.names.toName import hep.dataforge.values.Value import kotlin.jvm.JvmName +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty -fun DisplayObject.value(default: Value = Null, key: String? = null) = - ValueConfigDelegate(properties, key, default) +/** + * A delegate for display object properties + */ +class DisplayObjectDelegate( + val key: Name?, + val default: MetaItem<*>?, + val inherited: Boolean +) : ReadWriteProperty?> { + override fun getValue(thisRef: DisplayObject, property: KProperty<*>): MetaItem<*>? { + val name = key ?: property.name.toName() + return if (inherited) { + thisRef.getProperty(name) + } else { + thisRef.properties[name] + } ?: default + } -fun DisplayObject.string(default: String? = null, key: String? = null) = - StringConfigDelegate(properties, key, default) + override fun setValue(thisRef: DisplayObject, property: KProperty<*>, value: MetaItem<*>?) { + val name = key ?: property.name.toName() + thisRef.properties.style[name] = value + } +} -fun DisplayObject.boolean(default: Boolean? = null, key: String? = null) = - BooleanConfigDelegate(properties, key, default) +class DisplayObjectDelegateWrapper( + val key: Name?, + val default: T, + val inherited: Boolean, + val write: Config.(name: Name, value: T) -> Unit = { name, value -> set(name, value) }, + val read: (MetaItem<*>?) -> T? +) : ReadWriteProperty { + override fun getValue(thisRef: DisplayObject, property: KProperty<*>): T { + val name = key ?: property.name.toName() + return if (inherited) { + read(thisRef.getProperty(name)) + } else { + read(thisRef.properties[name]) + } ?: default + } -fun DisplayObject.number(default: Number? = null, key: String? = null) = - NumberConfigDelegate(properties, key, default) - -fun DisplayObject.double(default: Double? = null, key: String? = null) = - NumberConfigDelegate(properties, key, default).double - -fun DisplayObject.int(default: Int? = null, key: String? = null) = - NumberConfigDelegate(properties, key, default).int + override fun setValue(thisRef: DisplayObject, property: KProperty<*>, value: T) { + val name = key ?: property.name.toName() + thisRef.properties.style.write(name, value) + } +} -fun DisplayObject.node(key: String? = null) = StyledNodeDelegate(properties, key) +fun DisplayObject.value(default: Value? = null, key: String? = null, inherited: Boolean = true) = + DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.value } + +fun DisplayObject.string(default: String? = null, key: String? = null, inherited: Boolean = true) = + DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.string } + +fun DisplayObject.boolean(default: Boolean? = null, key: String? = null, inherited: Boolean = true) = + DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.boolean } + +fun DisplayObject.number(default: Number? = null, key: String? = null, inherited: Boolean = true) = + DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.number } + +fun DisplayObject.double(default: Double? = null, key: String? = null, inherited: Boolean = true) = + DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.double } + +fun DisplayObject.int(default: Int? = null, key: String? = null, inherited: Boolean = true) = + DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.int } + + +fun DisplayObject.node(key: String? = null, inherited: Boolean = true) = + DisplayObjectDelegateWrapper(key?.toName(), null, inherited) { it.node } //fun Configurable.spec(spec: Specification, key: String? = null) = ChildConfigDelegate(key) { spec.wrap(this) } @JvmName("safeString") -fun DisplayObject.string(default: String, key: String? = null) = - SafeStringConfigDelegate(properties, key, default) +fun DisplayObject.string(default: String, key: String? = null, inherited: Boolean = true) = + DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.string } @JvmName("safeBoolean") -fun DisplayObject.boolean(default: Boolean, key: String? = null) = - SafeBooleanConfigDelegate(properties, key, default) +fun DisplayObject.boolean(default: Boolean, key: String? = null, inherited: Boolean = true) = + DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.boolean } @JvmName("safeNumber") -fun DisplayObject.number(default: Number, key: String? = null) = - SafeNumberConfigDelegate(properties, key, default) +fun DisplayObject.number(default: Number, key: String? = null, inherited: Boolean = true) = + DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.number } @JvmName("safeDouble") -fun DisplayObject.double(default: Double, key: String? = null) = - SafeNumberConfigDelegate(properties, key, default).double +fun DisplayObject.double(default: Double, key: String? = null, inherited: Boolean = true) = + DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.double } -inline fun > DisplayObject.enum(default: E, key: String? = null) = - SafeEnumvConfigDelegate(properties, key, default) { enumValueOf(it) } \ No newline at end of file +inline fun > DisplayObject.enum(default: E, key: String? = null, inherited: Boolean = true) = + DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { item -> item.string?.let { enumValueOf(it) } } \ No newline at end of file From 8c77d3800b78ec8ef1406da288e750a406fd1389 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 3 Mar 2019 19:11:51 +0300 Subject: [PATCH 31/37] Visualization refactoring --- .../kotlin/hep/dataforge/vis/FXProperties.kt | 23 +++ .../hep/dataforge/vis/spatial/Canvas3D.kt | 157 ++++++++++++++++ .../vis/spatial/FXSpatialRenderer.kt | 172 +----------------- .../dataforge/vis/spatial/RendererDemoApp.kt | 4 +- 4 files changed, 187 insertions(+), 169 deletions(-) create mode 100644 dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/FXProperties.kt create mode 100644 dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/Canvas3D.kt diff --git a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/FXProperties.kt b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/FXProperties.kt new file mode 100644 index 00000000..79ea2d55 --- /dev/null +++ b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/FXProperties.kt @@ -0,0 +1,23 @@ +package hep.dataforge.vis + +import javafx.scene.paint.Color +import javafx.scene.paint.PhongMaterial + +object Materials{ + val RED = PhongMaterial().apply { + diffuseColor = Color.DARKRED + specularColor = Color.RED + } + + val WHITE = PhongMaterial().apply { + diffuseColor = Color.WHITE + specularColor = Color.LIGHTBLUE + } + + val GREY = PhongMaterial().apply { + diffuseColor = Color.DARKGREY + specularColor = Color.GREY + } + + val BLUE = PhongMaterial(Color.BLUE) +} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/Canvas3D.kt b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/Canvas3D.kt new file mode 100644 index 00000000..60b6b6dd --- /dev/null +++ b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/Canvas3D.kt @@ -0,0 +1,157 @@ +package hep.dataforge.vis.spatial + +import javafx.event.EventHandler +import javafx.scene.* +import javafx.scene.input.KeyCode +import javafx.scene.input.KeyEvent +import javafx.scene.input.MouseEvent +import javafx.scene.input.ScrollEvent +import javafx.scene.paint.Color +import org.fxyz3d.utils.CameraTransformer +import tornadofx.* + +class Canvas3D : Fragment() { + val world: Group = Group() + + private val camera = PerspectiveCamera().apply { + nearClip = CAMERA_NEAR_CLIP + farClip = CAMERA_FAR_CLIP + translateZ = CAMERA_INITIAL_DISTANCE + } + + //TODO move up + val cameraShift = CameraTransformer().apply { + val cameraFlip = CameraTransformer() + cameraFlip.children.add(camera) + cameraFlip.setRotateZ(180.0) + children.add(cameraFlip) + } + + val cameraRotation = CameraTransformer().apply { + children.add(cameraShift) + ry.angle = CAMERA_INITIAL_Y_ANGLE + rx.angle = CAMERA_INITIAL_X_ANGLE + rz.angle = CAMERA_INITIAL_Z_ANGLE + } + + + override val root =borderpane { + center = SubScene( + Group(world, cameraRotation).apply { DepthTest.ENABLE }, + 1024.0, + 768.0, + true, + SceneAntialiasing.BALANCED + ).apply { + fill = Color.GREY + this.camera = this@Canvas3D.camera + id = "canvas" + handleKeyboard(this) + handleMouse(this) + } + } + + + private fun handleKeyboard(scene: SubScene) { + scene.onKeyPressed = EventHandler { event -> + if (event.isControlDown) { + when (event.code) { + KeyCode.Z -> { + cameraShift.t.x = 0.0 + cameraShift.t.y = 0.0 + camera.translateZ = CAMERA_INITIAL_DISTANCE + cameraRotation.ry.angle = CAMERA_INITIAL_Y_ANGLE + cameraRotation.rx.angle = CAMERA_INITIAL_X_ANGLE + } +// KeyCode.X -> axisGroup.isVisible = !axisGroup.isVisible +// KeyCode.S -> snapshot() +// KeyCode.DIGIT1 -> pixelMap.filterKeys { it.getLayerNumber() == 1 }.values.forEach { +// toggleTransparency( +// it +// ) +// } +// KeyCode.DIGIT2 -> pixelMap.filterKeys { it.getLayerNumber() == 2 }.values.forEach { +// toggleTransparency( +// it +// ) +// } +// KeyCode.DIGIT3 -> pixelMap.filterKeys { it.getLayerNumber() == 3 }.values.forEach { +// toggleTransparency( +// it +// ) +// } + else -> { + }//do nothing + } + } + } + } + + private fun handleMouse(scene: SubScene) { + + var mousePosX: Double = 0.0 + var mousePosY: Double = 0.0 + var mouseOldX: Double = 0.0 + var mouseOldY: Double = 0.0 + var mouseDeltaX: Double = 0.0 + var mouseDeltaY: Double = 0.0 + + scene.onMousePressed = EventHandler { me -> + mousePosX = me.sceneX + mousePosY = me.sceneY + mouseOldX = me.sceneX + mouseOldY = me.sceneY + } + + scene.onMouseDragged = EventHandler { me -> + mouseOldX = mousePosX + mouseOldY = mousePosY + mousePosX = me.sceneX + mousePosY = me.sceneY + mouseDeltaX = mousePosX - mouseOldX + mouseDeltaY = mousePosY - mouseOldY + + val modifier = when { + me.isControlDown -> CONTROL_MULTIPLIER + me.isShiftDown -> SHIFT_MULTIPLIER + else -> 1.0 + } + + if (me.isPrimaryButtonDown) { + cameraRotation.rz.angle = + cameraRotation.rz.angle + mouseDeltaX * MOUSE_SPEED * modifier * ROTATION_SPEED + cameraRotation.rx.angle = + cameraRotation.rx.angle + mouseDeltaY * MOUSE_SPEED * modifier * ROTATION_SPEED + // } else if (me.isSecondaryButtonDown()) { + // double z = camera.getTranslateZ(); + // double newZ = z + mouseDeltaX * MOUSE_SPEED * modifier*100; + // camera.setTranslateZ(newZ); + } else if (me.isSecondaryButtonDown) { + cameraShift.t.x = cameraShift.t.x + mouseDeltaX * MOUSE_SPEED * modifier * TRACK_SPEED + cameraShift.t.y = cameraShift.t.y + mouseDeltaY * MOUSE_SPEED * modifier * TRACK_SPEED + } + } + scene.onScroll = EventHandler { event -> + val z = camera.translateZ + val newZ = z + MOUSE_SPEED * event.deltaY * RESIZE_SPEED + camera.translateZ = newZ + } + } + + companion object { + private const val CAMERA_INITIAL_DISTANCE = -4500.0 + private const val CAMERA_INITIAL_X_ANGLE = -50.0 + private const val CAMERA_INITIAL_Y_ANGLE = 0.0 + private const val CAMERA_INITIAL_Z_ANGLE = -210.0 + private const val CAMERA_NEAR_CLIP = 0.1 + private const val CAMERA_FAR_CLIP = 10000.0 + private const val AXIS_LENGTH = 2000.0 + private const val CONTROL_MULTIPLIER = 0.1 + private const val SHIFT_MULTIPLIER = 10.0 + private const val MOUSE_SPEED = 0.1 + private const val ROTATION_SPEED = 2.0 + private const val TRACK_SPEED = 6.0 + private const val RESIZE_SPEED = 50.0 + private const val LINE_WIDTH = 3.0 + } +} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/FXSpatialRenderer.kt b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/FXSpatialRenderer.kt index 829ec644..4456cd76 100644 --- a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/FXSpatialRenderer.kt +++ b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/FXSpatialRenderer.kt @@ -8,62 +8,20 @@ import hep.dataforge.vis.DisplayGroup import hep.dataforge.vis.DisplayObject import hep.dataforge.vis.DisplayObjectPropertyListener import hep.dataforge.vis.transform -import javafx.event.EventHandler -import javafx.scene.* -import javafx.scene.input.KeyCode -import javafx.scene.input.KeyEvent -import javafx.scene.input.MouseEvent -import javafx.scene.input.ScrollEvent +import javafx.scene.Group +import javafx.scene.Node import javafx.scene.paint.Color -import javafx.scene.paint.PhongMaterial import org.fxyz3d.geometry.Point3D import org.fxyz3d.shapes.primitives.CuboidMesh -import org.fxyz3d.utils.CameraTransformer /** * https://github.com/miho/JCSG for operations * - * TODO move world and camera boilerplate to another class */ class FXSpatialRenderer(override val context: Context) : Output { - private val world: Group = Group() - - private val camera = PerspectiveCamera().apply { - nearClip = CAMERA_NEAR_CLIP - farClip = CAMERA_FAR_CLIP - translateZ = CAMERA_INITIAL_DISTANCE - } - - val cameraShift = CameraTransformer().apply { - val cameraFlip = CameraTransformer() - cameraFlip.children.add(camera) - cameraFlip.setRotateZ(180.0) - children.add(cameraFlip) - } - - val cameraRotation = CameraTransformer().apply { - children.add(cameraShift) - ry.angle = CAMERA_INITIAL_Y_ANGLE - rx.angle = CAMERA_INITIAL_X_ANGLE - rz.angle = CAMERA_INITIAL_Z_ANGLE - } - - - val canvas: SubScene = SubScene( - Group(world, cameraRotation).apply { DepthTest.ENABLE }, - 1024.0, - 768.0, - true, - SceneAntialiasing.BALANCED - ).apply { - fill = Color.GREY - this.camera = this@FXSpatialRenderer.camera - id = "canvas" - handleKeyboard(this) - handleMouse(this) - } + val canvas by lazy { Canvas3D() } private fun buildObject(obj: DisplayObject): Node { return when (obj) { @@ -72,6 +30,7 @@ class FXSpatialRenderer(override val context: Context) : Output { val listener = DisplayObjectPropertyListener(obj) this.center = Point3D(obj.x.toFloat(), obj.y.toFloat(), obj.z.toFloat()) this.diffuseColorProperty().bind(listener["color"].transform { + //TODO Move to extension val int = it.int ?: 0 val red = int and 0x00ff0000 shr 16 val green = int and 0x0000ff00 shr 8 @@ -84,127 +43,6 @@ class FXSpatialRenderer(override val context: Context) : Output { } override fun render(obj: DisplayObject, meta: Meta) { - world.children.add(buildObject(obj)) - } - - private fun handleKeyboard(scene: SubScene) { - scene.onKeyPressed = EventHandler { event -> - if (event.isControlDown) { - when (event.code) { - KeyCode.Z -> { - cameraShift.t.x = 0.0 - cameraShift.t.y = 0.0 - camera.translateZ = CAMERA_INITIAL_DISTANCE - cameraRotation.ry.angle = CAMERA_INITIAL_Y_ANGLE - cameraRotation.rx.angle = CAMERA_INITIAL_X_ANGLE - } -// KeyCode.X -> axisGroup.isVisible = !axisGroup.isVisible -// KeyCode.S -> snapshot() -// KeyCode.DIGIT1 -> pixelMap.filterKeys { it.getLayerNumber() == 1 }.values.forEach { -// toggleTransparency( -// it -// ) -// } -// KeyCode.DIGIT2 -> pixelMap.filterKeys { it.getLayerNumber() == 2 }.values.forEach { -// toggleTransparency( -// it -// ) -// } -// KeyCode.DIGIT3 -> pixelMap.filterKeys { it.getLayerNumber() == 3 }.values.forEach { -// toggleTransparency( -// it -// ) -// } - else -> { - }//do nothing - } - } - } - } - - private fun handleMouse(scene: SubScene) { - - var mousePosX: Double = 0.0 - var mousePosY: Double = 0.0 - var mouseOldX: Double = 0.0 - var mouseOldY: Double = 0.0 - var mouseDeltaX: Double = 0.0 - var mouseDeltaY: Double = 0.0 - - scene.onMousePressed = EventHandler { me -> - mousePosX = me.sceneX - mousePosY = me.sceneY - mouseOldX = me.sceneX - mouseOldY = me.sceneY - } - - scene.onMouseDragged = EventHandler { me -> - mouseOldX = mousePosX - mouseOldY = mousePosY - mousePosX = me.sceneX - mousePosY = me.sceneY - mouseDeltaX = mousePosX - mouseOldX - mouseDeltaY = mousePosY - mouseOldY - - val modifier = when { - me.isControlDown -> CONTROL_MULTIPLIER - me.isShiftDown -> SHIFT_MULTIPLIER - else -> 1.0 - } - - if (me.isPrimaryButtonDown) { - cameraRotation.rz.angle = - cameraRotation.rz.angle + mouseDeltaX * MOUSE_SPEED * modifier * ROTATION_SPEED - cameraRotation.rx.angle = - cameraRotation.rx.angle + mouseDeltaY * MOUSE_SPEED * modifier * ROTATION_SPEED - // } else if (me.isSecondaryButtonDown()) { - // double z = camera.getTranslateZ(); - // double newZ = z + mouseDeltaX * MOUSE_SPEED * modifier*100; - // camera.setTranslateZ(newZ); - } else if (me.isSecondaryButtonDown) { - cameraShift.t.x = cameraShift.t.x + mouseDeltaX * MOUSE_SPEED * modifier * TRACK_SPEED - cameraShift.t.y = cameraShift.t.y + mouseDeltaY * MOUSE_SPEED * modifier * TRACK_SPEED - } - } - scene.onScroll = EventHandler { event -> - val z = camera.translateZ - val newZ = z + MOUSE_SPEED * event.deltaY * RESIZE_SPEED - camera.translateZ = newZ - } - } - - companion object { - private const val CAMERA_INITIAL_DISTANCE = -4500.0 - private const val CAMERA_INITIAL_X_ANGLE = -50.0 - private const val CAMERA_INITIAL_Y_ANGLE = 0.0 - private const val CAMERA_INITIAL_Z_ANGLE = -210.0 - private const val CAMERA_NEAR_CLIP = 0.1 - private const val CAMERA_FAR_CLIP = 10000.0 - private const val AXIS_LENGTH = 2000.0 - private const val CONTROL_MULTIPLIER = 0.1 - private const val SHIFT_MULTIPLIER = 10.0 - private const val MOUSE_SPEED = 0.1 - private const val ROTATION_SPEED = 2.0 - private const val TRACK_SPEED = 6.0 - private const val RESIZE_SPEED = 50.0 - private const val LINE_WIDTH = 3.0 - - private val redMaterial = PhongMaterial().apply { - diffuseColor = Color.DARKRED - specularColor = Color.RED - } - - private val whiteMaterial = PhongMaterial().apply { - diffuseColor = Color.WHITE - specularColor = Color.LIGHTBLUE - } - - private val greyMaterial = PhongMaterial().apply { - diffuseColor = Color.DARKGREY - specularColor = Color.GREY - } - - private val blueMaterial = PhongMaterial(Color.BLUE) - + canvas.world.children.add(buildObject(obj)) } } \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt index 41cbb82f..26a02d09 100644 --- a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt +++ b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt @@ -18,7 +18,7 @@ class RendererDemoApp : App(RendererDemoView::class) class RendererDemoView : View() { val renderer = FXSpatialRenderer(Global) override val root: Parent = borderpane { - center = renderer.canvas + center = renderer.canvas.root } lateinit var group: DisplayGroup @@ -51,7 +51,7 @@ class RendererDemoView : View() { } } - renderer.cameraRotation.apply { + renderer.canvas.cameraRotation.apply { ry.angle = -30.0 rx.angle = -15.0 } From ec01b0d7a813ec6eb09d053d66733a5e767a8156 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 6 Mar 2019 18:15:30 +0300 Subject: [PATCH 32/37] JS 3d Visualization --- build.gradle.kts | 26 +++- dataforge-meta-io/build.gradle | 59 -------- dataforge-meta-io/build.gradle.kts | 57 +++++++ dataforge-vis/build.gradle.kts | 8 + .../kotlin/hep/dataforge/vis/FXProperties.kt | 23 --- .../vis/spatial/FXSpatialRenderer.kt | 48 ------ .../dataforge-vis-js/build.gradle.kts | 42 ------ .../build.gradle.kts | 2 +- .../vis/DisplayObjectPropertyListener.kt | 3 - .../hep/dataforge/vis/spatial/Canvas3D.kt | 23 ++- .../hep/dataforge/vis/spatial/FX3DOutput.kt | 54 +++++++ .../hep/dataforge/vis/spatial/Materials.kt | 63 ++++++++ .../dataforge/vis/spatial/RendererDemoApp.kt | 11 +- .../dataforge-vis-spatial-js/build.gradle.kts | 59 ++++++++ .../package.json.d/package.info | 3 + .../src/main/kotlin/hep/dataforge/vis/HMR.kt | 19 +++ .../src/main/kotlin/hep/dataforge/vis/main.kt | 50 +++++++ .../hep/dataforge/vis/spatial/ThreeDemoApp.kt | 64 ++++++++ .../hep/dataforge/vis/spatial/ThreeOutput.kt | 140 ++++++++++++++++++ .../kotlin/hep/dataforge/vis/spatial/three.kt | 13 ++ .../src/main/web/index.html | 19 +++ .../webpack.config.d/css.js | 1 + .../dataforge-vis-spatial/build.gradle.kts | 24 ++- .../kotlin/hep/dataforge/vis/spatial/Box.kt | 2 +- .../dataforge/vis/spatial/DisplayObject3D.kt | 24 ++- .../hep/dataforge/vis/spatial/Extruded.kt | 17 +++ .../dataforge/vis/DisplayObjectDelegates.kt | 24 ++- settings.gradle.kts | 11 +- 28 files changed, 682 insertions(+), 207 deletions(-) delete mode 100644 dataforge-meta-io/build.gradle create mode 100644 dataforge-meta-io/build.gradle.kts delete mode 100644 dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/FXProperties.kt delete mode 100644 dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/FXSpatialRenderer.kt delete mode 100644 dataforge-vis/dataforge-vis-js/build.gradle.kts rename dataforge-vis/{dataforge-vis-fx => dataforge-vis-spatial-fx}/build.gradle.kts (89%) rename dataforge-vis/{dataforge-vis-fx => dataforge-vis-spatial-fx}/src/main/kotlin/hep/dataforge/vis/DisplayObjectPropertyListener.kt (93%) rename dataforge-vis/{dataforge-vis-fx => dataforge-vis-spatial-fx}/src/main/kotlin/hep/dataforge/vis/spatial/Canvas3D.kt (87%) create mode 100644 dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/FX3DOutput.kt create mode 100644 dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/Materials.kt rename dataforge-vis/{dataforge-vis-fx => dataforge-vis-spatial-fx}/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt (82%) create mode 100644 dataforge-vis/dataforge-vis-spatial-js/build.gradle.kts create mode 100644 dataforge-vis/dataforge-vis-spatial-js/package.json.d/package.info create mode 100644 dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/HMR.kt create mode 100644 dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/main.kt create mode 100644 dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeDemoApp.kt create mode 100644 dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeOutput.kt create mode 100644 dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three.kt create mode 100644 dataforge-vis/dataforge-vis-spatial-js/src/main/web/index.html create mode 100644 dataforge-vis/dataforge-vis-spatial-js/webpack.config.d/css.js create mode 100644 dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt diff --git a/build.gradle.kts b/build.gradle.kts index 7b8168f8..f172e46d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -58,6 +58,28 @@ subprojects { } } } + + js{ + configure(listOf(compilations["main"], compilations["test"])) { + tasks.getByName(compileKotlinTaskName) { + kotlinOptions { + metaInfo = true + sourceMap = true + sourceMapEmbedSources = "always" + moduleKind = "umd" + } + } + } + + configure(listOf(compilations["main"])) { + tasks.getByName(compileKotlinTaskName) { + kotlinOptions { + main = "call" + } + } + } + } + targets.all { sourceSets.all { languageSettings.progressiveMode = true @@ -119,6 +141,4 @@ subprojects { } } -} - - +} \ No newline at end of file diff --git a/dataforge-meta-io/build.gradle b/dataforge-meta-io/build.gradle deleted file mode 100644 index 2b1b3ffa..00000000 --- a/dataforge-meta-io/build.gradle +++ /dev/null @@ -1,59 +0,0 @@ -plugins { - id "org.jetbrains.kotlin.multiplatform" -} - -description = "IO for meta" - -kotlin { - targets { - fromPreset(presets.jvm, 'jvm') - fromPreset(presets.js, 'js') - // For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64 - // For Linux, preset should be changed to e.g. presets.linuxX64 - // For MacOS, preset should be changed to e.g. presets.macosX64 - //fromPreset(presets.iosX64, 'ios') - } - sourceSets { - commonMain { - dependencies { - api project(":dataforge-meta") - //implementation 'org.jetbrains.kotlin:kotlin-reflect' - api "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serializationVersion" - api "org.jetbrains.kotlinx:kotlinx-io:$ioVersion" - } - } - commonTest { - dependencies { - implementation 'org.jetbrains.kotlin:kotlin-test-common' - implementation 'org.jetbrains.kotlin:kotlin-test-annotations-common' - } - } - jvmMain { - dependencies { - api "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serializationVersion" - api "org.jetbrains.kotlinx:kotlinx-io-jvm:$ioVersion" - } - } - jvmTest { - dependencies { - implementation 'org.jetbrains.kotlin:kotlin-test' - implementation 'org.jetbrains.kotlin:kotlin-test-junit' - } - } - jsMain { - dependencies { - api "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serializationVersion" - api "org.jetbrains.kotlinx:kotlinx-io-js:$ioVersion" - } - } - jsTest { - dependencies { - implementation 'org.jetbrains.kotlin:kotlin-test-js' - } - } -// iosMain { -// } -// iosTest { -// } - } -} \ No newline at end of file diff --git a/dataforge-meta-io/build.gradle.kts b/dataforge-meta-io/build.gradle.kts new file mode 100644 index 00000000..1b57774d --- /dev/null +++ b/dataforge-meta-io/build.gradle.kts @@ -0,0 +1,57 @@ +plugins { + kotlin("multiplatform") +} + +description = "IO for meta" + + +val ioVersion: String by rootProject.extra +val serializationVersion: String by rootProject.extra + +kotlin { + jvm() + js() + sourceSets { + val commonMain by getting{ + dependencies { + api(project(":dataforge-meta")) + //implementation 'org.jetbrains.kotlin:kotlin-reflect' + api("org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serializationVersion") + api("org.jetbrains.kotlinx:kotlinx-io:$ioVersion") + } + } + val commonTest by getting { + dependencies { + implementation("org.jetbrains.kotlin:kotlin-test-common") + implementation("org.jetbrains.kotlin:kotlin-test-annotations-common") + } + } + val jvmMain by getting { + dependencies { + api("org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serializationVersion") + api("org.jetbrains.kotlinx:kotlinx-io-jvm:$ioVersion") + } + } + val jvmTest by getting { + dependencies { + implementation("org.jetbrains.kotlin:kotlin-test") + implementation("org.jetbrains.kotlin:kotlin-test-junit") + } + } + val jsMain by getting { + dependencies { + api("org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serializationVersion") + api("org.jetbrains.kotlinx:kotlinx-io-js:$ioVersion") + } + } + val jsTest by getting { + dependencies { + implementation("org.jetbrains.kotlin:kotlin-test-js") + } + } +// iosMain { +// } +// iosTest { +// } + } +} \ No newline at end of file diff --git a/dataforge-vis/build.gradle.kts b/dataforge-vis/build.gradle.kts index 9ef8235b..8b93e57c 100644 --- a/dataforge-vis/build.gradle.kts +++ b/dataforge-vis/build.gradle.kts @@ -1,5 +1,8 @@ +import org.openjfx.gradle.JavaFXOptions + plugins { kotlin("multiplatform") + id("org.openjfx.javafxplugin") } kotlin { @@ -14,6 +17,7 @@ kotlin { } val jvmMain by getting { dependencies { + //api("no.tornado:tornadofx:1.7.18") } } val jsMain by getting { @@ -21,4 +25,8 @@ kotlin { } } } +} + +configure{ + modules("javafx.controls") } \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/FXProperties.kt b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/FXProperties.kt deleted file mode 100644 index 79ea2d55..00000000 --- a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/FXProperties.kt +++ /dev/null @@ -1,23 +0,0 @@ -package hep.dataforge.vis - -import javafx.scene.paint.Color -import javafx.scene.paint.PhongMaterial - -object Materials{ - val RED = PhongMaterial().apply { - diffuseColor = Color.DARKRED - specularColor = Color.RED - } - - val WHITE = PhongMaterial().apply { - diffuseColor = Color.WHITE - specularColor = Color.LIGHTBLUE - } - - val GREY = PhongMaterial().apply { - diffuseColor = Color.DARKGREY - specularColor = Color.GREY - } - - val BLUE = PhongMaterial(Color.BLUE) -} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/FXSpatialRenderer.kt b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/FXSpatialRenderer.kt deleted file mode 100644 index 4456cd76..00000000 --- a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/FXSpatialRenderer.kt +++ /dev/null @@ -1,48 +0,0 @@ -package hep.dataforge.vis.spatial - -import hep.dataforge.context.Context -import hep.dataforge.io.Output -import hep.dataforge.meta.Meta -import hep.dataforge.meta.int -import hep.dataforge.vis.DisplayGroup -import hep.dataforge.vis.DisplayObject -import hep.dataforge.vis.DisplayObjectPropertyListener -import hep.dataforge.vis.transform -import javafx.scene.Group -import javafx.scene.Node -import javafx.scene.paint.Color -import org.fxyz3d.geometry.Point3D -import org.fxyz3d.shapes.primitives.CuboidMesh - - -/** - * https://github.com/miho/JCSG for operations - * - */ -class FXSpatialRenderer(override val context: Context) : Output { - - val canvas by lazy { Canvas3D() } - - private fun buildObject(obj: DisplayObject): Node { - return when (obj) { - is DisplayGroup -> Group(obj.children.map { buildObject(it) }) - is Box -> CuboidMesh(obj.xSize, obj.ySize, obj.zSize).apply { - val listener = DisplayObjectPropertyListener(obj) - this.center = Point3D(obj.x.toFloat(), obj.y.toFloat(), obj.z.toFloat()) - this.diffuseColorProperty().bind(listener["color"].transform { - //TODO Move to extension - val int = it.int ?: 0 - val red = int and 0x00ff0000 shr 16 - val green = int and 0x0000ff00 shr 8 - val blue = int and 0x000000ff - return@transform Color.rgb(red, green, blue) - }) - } - else -> TODO() - } - } - - override fun render(obj: DisplayObject, meta: Meta) { - canvas.world.children.add(buildObject(obj)) - } -} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-js/build.gradle.kts b/dataforge-vis/dataforge-vis-js/build.gradle.kts deleted file mode 100644 index f5e385a6..00000000 --- a/dataforge-vis/dataforge-vis-js/build.gradle.kts +++ /dev/null @@ -1,42 +0,0 @@ -plugins{ - kotlin("js") - id("kotlin") -} - -// configure(listOf(compilations.main, compilations.test)) { -// tasks.getByName(compileKotlinTaskName).kotlinOptions { -// sourceMap = true -// moduleKind = "umd" -// metaInfo = true -// } -// } -// -// configure(compilations.main) { -// tasks.getByName(compileKotlinTaskName).kotlinOptions { -// main = "call" -// } -// } - -dependencies { - implementation("info.laht.threekt:threejs-wrapper:0.88-npm-1") -} - -extensions.findByType()?.apply { - extensions.findByType()?.apply { - dependency("three") - dependency("three-orbitcontrols") - devDependency("karma") - - } - - sourceMaps = true - - bundle("webpack") { - this as WebPackExtension - bundleName = "main" - proxyUrl = "http://localhost:8080" - contentPath = file("src/main/web") - sourceMapEnabled = true - mode = "development" - } -} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-fx/build.gradle.kts b/dataforge-vis/dataforge-vis-spatial-fx/build.gradle.kts similarity index 89% rename from dataforge-vis/dataforge-vis-fx/build.gradle.kts rename to dataforge-vis/dataforge-vis-spatial-fx/build.gradle.kts index 260b8622..3b57a761 100644 --- a/dataforge-vis/dataforge-vis-fx/build.gradle.kts +++ b/dataforge-vis/dataforge-vis-spatial-fx/build.gradle.kts @@ -12,7 +12,7 @@ dependencies{ implementation("org.fxyz3d:fxyz3d:0.4.0") } -extensions.findByType()?.apply { +configure { modules("javafx.controls") } diff --git a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/DisplayObjectPropertyListener.kt b/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/DisplayObjectPropertyListener.kt similarity index 93% rename from dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/DisplayObjectPropertyListener.kt rename to dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/DisplayObjectPropertyListener.kt index 63eb25c5..44214460 100644 --- a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/DisplayObjectPropertyListener.kt +++ b/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/DisplayObjectPropertyListener.kt @@ -1,8 +1,5 @@ package hep.dataforge.vis -import hep.dataforge.meta.* -import hep.dataforge.names.Name -import hep.dataforge.names.toName import javafx.beans.binding.ObjectBinding import tornadofx.* diff --git a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/Canvas3D.kt b/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/Canvas3D.kt similarity index 87% rename from dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/Canvas3D.kt rename to dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/Canvas3D.kt index 60b6b6dd..bca2ab76 100644 --- a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/Canvas3D.kt +++ b/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/Canvas3D.kt @@ -19,21 +19,34 @@ class Canvas3D : Fragment() { translateZ = CAMERA_INITIAL_DISTANCE } - //TODO move up - val cameraShift = CameraTransformer().apply { + private val cameraShift = CameraTransformer().apply { val cameraFlip = CameraTransformer() cameraFlip.children.add(camera) cameraFlip.setRotateZ(180.0) children.add(cameraFlip) } - val cameraRotation = CameraTransformer().apply { + val translationXProperty get() = cameraShift.t.xProperty() + var translateX by translationXProperty + val translationYProperty get() = cameraShift.t.yProperty() + var translateY by translationYProperty + val translationZProperty get() = cameraShift.t.zProperty() + var translateZ by translationZProperty + + private val cameraRotation = CameraTransformer().apply { children.add(cameraShift) ry.angle = CAMERA_INITIAL_Y_ANGLE rx.angle = CAMERA_INITIAL_X_ANGLE rz.angle = CAMERA_INITIAL_Z_ANGLE } + val rotationXProperty get() = cameraRotation.rx.angleProperty() + var angleX by rotationXProperty + val rotationYProperty get() = cameraRotation.ry.angleProperty() + var angleY by rotationYProperty + val rotationZProperty get() = cameraRotation.rz.angleProperty() + var angleZ by rotationZProperty + override val root =borderpane { center = SubScene( @@ -122,10 +135,6 @@ class Canvas3D : Fragment() { cameraRotation.rz.angle + mouseDeltaX * MOUSE_SPEED * modifier * ROTATION_SPEED cameraRotation.rx.angle = cameraRotation.rx.angle + mouseDeltaY * MOUSE_SPEED * modifier * ROTATION_SPEED - // } else if (me.isSecondaryButtonDown()) { - // double z = camera.getTranslateZ(); - // double newZ = z + mouseDeltaX * MOUSE_SPEED * modifier*100; - // camera.setTranslateZ(newZ); } else if (me.isSecondaryButtonDown) { cameraShift.t.x = cameraShift.t.x + mouseDeltaX * MOUSE_SPEED * modifier * TRACK_SPEED cameraShift.t.y = cameraShift.t.y + mouseDeltaY * MOUSE_SPEED * modifier * TRACK_SPEED diff --git a/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/FX3DOutput.kt b/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/FX3DOutput.kt new file mode 100644 index 00000000..2b19bd45 --- /dev/null +++ b/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/FX3DOutput.kt @@ -0,0 +1,54 @@ +package hep.dataforge.vis.spatial + +import hep.dataforge.vis.DisplayObjectPropertyListener +import javafx.scene.Group +import javafx.scene.Node +import org.fxyz3d.shapes.primitives.CuboidMesh +import tornadofx.* + +/** + * https://github.com/miho/JCSG for operations + * + */ +class FX3DOutput(override val context: Context) : Output { + val canvas by lazy { Canvas3D() } + + + private fun buildNode(obj: Any): Node? { + return when (obj) { + is DisplayShape3D -> { + val listener = DisplayObjectPropertyListener(obj) + val x = listener["x"].float() + val y = listener["y"].float() + val z = listener["z"].float() + val center = objectBinding(x, y, z) { + Point3D(x.value ?: 0f, y.value ?: 0f, z.value ?: 0f) + } + when (obj) { + is DisplayGroup3D -> Group(obj.children.map { buildNode(it) }).apply { + this.translateXProperty().bind(x) + this.translateYProperty().bind(y) + this.translateZProperty().bind(z) + } + is Box -> CuboidMesh(obj.xSize, obj.ySize, obj.zSize).apply { + this.centerProperty().bind(center) + this.materialProperty().bind(listener["color"].transform { it.material() }) + } + else -> { + logger.error { "No renderer defined for ${obj::class}" } + null + } + } + } + is DisplayGroup -> Group(obj.children.map { buildNode(it) }) // a logical group + else -> { + logger.error { "No renderer defined for ${obj::class}" } + null + } + } + } + + override fun render(obj: Any, meta: Meta) { + buildNode(obj)?.let { canvas.world.children.add(it) } + } +} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/Materials.kt b/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/Materials.kt new file mode 100644 index 00000000..911bb503 --- /dev/null +++ b/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/Materials.kt @@ -0,0 +1,63 @@ +package hep.dataforge.vis.spatial + +import javafx.scene.paint.Color +import javafx.scene.paint.Material +import javafx.scene.paint.PhongMaterial + +object Materials { + val RED = PhongMaterial().apply { + diffuseColor = Color.DARKRED + specularColor = Color.RED + } + + val WHITE = PhongMaterial().apply { + diffuseColor = Color.WHITE + specularColor = Color.LIGHTBLUE + } + + val GREY = PhongMaterial().apply { + diffuseColor = Color.DARKGREY + specularColor = Color.GREY + } + + val BLUE = PhongMaterial(Color.BLUE) +} + +/** + * Infer color based on meta item + */ +fun MetaItem<*>.color(): Color { + return when (this) { + is MetaItem.ValueItem -> if (this.value.type == ValueType.STRING) { + Color.web(this.value.string) + } else { + val int = value.number.toInt() + val red = int and 0x00ff0000 shr 16 + val green = int and 0x0000ff00 shr 8 + val blue = int and 0x000000ff + Color.rgb(red, green, blue) + } + is MetaItem.NodeItem -> { + Color.rgb( + node["red"]?.int ?: 0, + node["green"]?.int ?: 0, + node["blue"]?.int ?: 0, + node["opacity"]?.double ?: 1.0 + ) + } + } +} + +/** + * Infer FX material based on meta item + */ +fun MetaItem<*>.material(): Material { + return when (this) { + is MetaItem.ValueItem -> PhongMaterial(color()) + is MetaItem.NodeItem -> PhongMaterial().apply { + node["color"]?.let { diffuseColor = it.color() } + node["specularColor"]?.let { specularColor = it.color() } + } + } +} + diff --git a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt b/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt similarity index 82% rename from dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt rename to dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt index 26a02d09..b339bd9e 100644 --- a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt +++ b/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt @@ -1,8 +1,5 @@ package hep.dataforge.vis.spatial -import hep.dataforge.context.Global -import hep.dataforge.meta.number -import hep.dataforge.vis.DisplayGroup import javafx.scene.Parent import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay @@ -16,7 +13,7 @@ class RendererDemoApp : App(RendererDemoView::class) class RendererDemoView : View() { - val renderer = FXSpatialRenderer(Global) + val renderer = FX3DOutput(Global) override val root: Parent = borderpane { center = renderer.canvas.root } @@ -51,9 +48,9 @@ class RendererDemoView : View() { } } - renderer.canvas.cameraRotation.apply { - ry.angle = -30.0 - rx.angle = -15.0 + renderer.canvas.apply { + angleY = -30.0 + angleX = -15.0 } } } diff --git a/dataforge-vis/dataforge-vis-spatial-js/build.gradle.kts b/dataforge-vis/dataforge-vis-spatial-js/build.gradle.kts new file mode 100644 index 00000000..1336ce78 --- /dev/null +++ b/dataforge-vis/dataforge-vis-spatial-js/build.gradle.kts @@ -0,0 +1,59 @@ +import org.jetbrains.kotlin.gradle.frontend.KotlinFrontendExtension +import org.jetbrains.kotlin.gradle.frontend.npm.NpmExtension +import org.jetbrains.kotlin.gradle.frontend.webpack.WebPackExtension + +plugins { + id("kotlin2js") + id("kotlin-dce-js") + id("org.jetbrains.kotlin.frontend") +} + + +dependencies { + api(project(":dataforge-vis:dataforge-vis-spatial")) + implementation("info.laht.threekt:threejs-wrapper:0.88-npm-1") +} + +configure { + downloadNodeJsVersion = "latest" + + configure { + dependency("three") + dependency("three-orbitcontrols") + dependency("style-loader") + devDependency("karma") + } + + sourceMaps = true + + bundle("webpack") { + this as WebPackExtension + bundleName = "main" + proxyUrl = "http://localhost:8080" + contentPath = file("src/main/web") + sourceMapEnabled = true + //mode = "production" + mode = "development" + } +} + +tasks{ + compileKotlin2Js{ + kotlinOptions{ + metaInfo = true + outputFile = "${project.buildDir.path}/js/${project.name}.js" + sourceMap = true + moduleKind = "umd" + main = "call" + } + } + + compileTestKotlin2Js{ + kotlinOptions{ + metaInfo = true + outputFile = "${project.buildDir.path}/js/${project.name}-test.js" + sourceMap = true + moduleKind = "umd" + } + } +} diff --git a/dataforge-vis/dataforge-vis-spatial-js/package.json.d/package.info b/dataforge-vis/dataforge-vis-spatial-js/package.json.d/package.info new file mode 100644 index 00000000..e2d5f174 --- /dev/null +++ b/dataforge-vis/dataforge-vis-spatial-js/package.json.d/package.info @@ -0,0 +1,3 @@ +{ + "description": "A demo project for particle visualization in JS" +} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/HMR.kt b/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/HMR.kt new file mode 100644 index 00000000..a77b9cf6 --- /dev/null +++ b/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/HMR.kt @@ -0,0 +1,19 @@ +package hep.dataforge.vis.hmr + +external val module: Module + +external interface Module { + val hot: Hot? +} + +external interface Hot { + val data: dynamic + + fun accept() + fun accept(dependency: String, callback: () -> Unit) + fun accept(dependencies: Array, callback: (updated: Array) -> Unit) + + fun dispose(callback: (data: dynamic) -> Unit) +} + +external fun require(name: String): dynamic \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/main.kt b/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/main.kt new file mode 100644 index 00000000..f4359987 --- /dev/null +++ b/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/main.kt @@ -0,0 +1,50 @@ +package hep.dataforge.vis + +import hep.dataforge.vis.hmr.module +import hep.dataforge.vis.spatial.ThreeDemoApp +import kotlin.browser.document +import kotlin.dom.hasClass + + +abstract class ApplicationBase { + abstract val stateKeys: List + + abstract fun start(state: Map) + abstract fun dispose(): Map +} + + +fun main() { + var application: ApplicationBase? = null + + val state: dynamic = module.hot?.let { hot -> + hot.accept() + + hot.dispose { data -> + data.appState = application?.dispose() + application = null + } + + hot.data + } + + if (document.body != null) { + application = start(state) + } else { + application = null + document.addEventListener("DOMContentLoaded", { application = start(state) }) + } +} + +fun start(state: dynamic): ApplicationBase? { + return if (document.body?.hasClass("testApp") == true) { + val application = ThreeDemoApp() + + @Suppress("UnsafeCastFromDynamic") + application.start(state?.appState ?: emptyMap()) + + application + } else { + null + } +} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeDemoApp.kt b/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeDemoApp.kt new file mode 100644 index 00000000..5cab86a2 --- /dev/null +++ b/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeDemoApp.kt @@ -0,0 +1,64 @@ +package hep.dataforge.vis.spatial + +import hep.dataforge.context.Global +import hep.dataforge.meta.number +import hep.dataforge.vis.ApplicationBase +import hep.dataforge.vis.DisplayGroup +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import kotlin.browser.document +import kotlin.random.Random + + +class ThreeDemoApp : ApplicationBase() { + + override val stateKeys: List = emptyList() + + override fun start(state: Map) { + println("started") + val renderer = ThreeOutput(Global) + document.getElementById("canvas")?.appendChild(renderer.root) + + lateinit var group: DisplayGroup + + renderer.render { + group = group { + box { + xSize = 100.0 + ySize = 100.0 + zSize = 100.0 + } + box { + x = 110.0 + xSize = 100.0 + ySize = 100.0 + zSize = 100.0 + } + } + } + + var color by group.properties.number(1530).int + + GlobalScope.launch { + val random = Random(111) + while (isActive) { + delay(1000) + color = random.nextInt(0, Int.MAX_VALUE) + } + } + +// view.animate() + +// view = WebLinesView(document.getElementById("lines")!!, document.getElementById("addForm")!!) +// presenter = LinesPresenter(view) +// +// state["lines"]?.let { linesState -> +// @Suppress("UNCHECKED_CAST") +// presenter.restore(linesState as Array) +// } + } + + override fun dispose() = emptyMap()//mapOf("lines" to presenter.dispose()) +} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeOutput.kt b/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeOutput.kt new file mode 100644 index 00000000..4afc32cc --- /dev/null +++ b/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeOutput.kt @@ -0,0 +1,140 @@ +package hep.dataforge.vis.spatial + +import hep.dataforge.context.Context +import hep.dataforge.io.Output +import hep.dataforge.meta.Meta +import hep.dataforge.vis.DisplayGroup +import info.laht.threekt.WebGLRenderer +import info.laht.threekt.cameras.PerspectiveCamera +import info.laht.threekt.core.Object3D +import info.laht.threekt.external.controls.OrbitControls +import info.laht.threekt.geometries.BoxBufferGeometry +import info.laht.threekt.lights.AmbientLight +import info.laht.threekt.materials.MeshPhongMaterial +import info.laht.threekt.math.ColorConstants +import info.laht.threekt.objects.Mesh +import info.laht.threekt.scenes.Scene +import kotlin.browser.window + +class ThreeOutput(override val context: Context) : Output { + + private val renderer = WebGLRenderer { antialias = true }.apply { + setClearColor(ColorConstants.skyblue, 1) + setSize(window.innerWidth, window.innerHeight) + } + + private val scene: Scene = Scene().apply { + add(AmbientLight()) + } + private val camera = PerspectiveCamera( + 75, + window.innerWidth.toDouble() / window.innerHeight, + 0.1, + 10000 + ).apply { + position.z = 4500.0 + } + + private val controls: OrbitControls = OrbitControls(camera, renderer.domElement) + + val root by lazy { + window.addEventListener("resize", { + camera.aspect = window.innerWidth.toDouble() / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize(window.innerWidth, window.innerHeight) + }, false) + renderer.domElement + } + + + private fun buildNode(obj: Any): Object3D? { + return when (obj) { + is DisplayShape3D -> { +// val listener = DisplayObjectPropertyListener(obj) +// val x = listener["x"].float() +// val y = listener["y"].float() +// val z = listener["z"].float() +// val center = objectBinding(x, y, z) { +// Vector3(x.value ?: 0f, y.value ?: 0f, z.value ?: 0f) +// } + when (obj) { + is DisplayGroup3D -> Group(obj.children.mapNotNull { buildNode(it) }).apply { + this.translateX(obj.x) + this.translateY(obj.y) + this.translateZ(obj.z) + } + is Box -> { + val geometry = BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize) + .translate(obj.x, obj.y, obj.z) + val material = MeshPhongMaterial().apply { + this.color.set(ColorConstants.darkgreen) + } + Mesh(geometry, material) + } + else -> { + logger.error { "No renderer defined for ${obj::class}" } + null + } + } + } + is DisplayGroup -> Group(obj.children.mapNotNull { buildNode(it) }) // a logical group + else -> { + logger.error { "No renderer defined for ${obj::class}" } + null + } + } + } + + override fun render(obj: Any, meta: Meta) { + buildNode(obj)?.let { scene.add(it) } + } + + // init { +// val cube: Mesh + +// cube = Mesh( +// BoxBufferGeometry(1, 1, 1), +// MeshPhongMaterial().apply { +// this.color.set(ColorConstants.darkgreen) +// } +// ).also(scene::add) +// +// Mesh(cube.geometry as BufferGeometry, +// MeshBasicMaterial().apply { +// this.wireframe = true +// this.color.set(ColorConstants.black) +// } +// ).also(cube::add) +// +// val points = CatmullRomCurve3( +// arrayOf( +// Vector3(-10, 0, 10), +// Vector3(-5, 5, 5), +// Vector3(0, 0, 0), +// Vector3(5, -5, 5), +// Vector3(10, 0, 10) +// ) +// ).getPoints(50) +// +// val geometry = BufferGeometry().setFromPoints(points) +// +// val material = LineBasicMaterial().apply { +// color.set(0xff0000) +// } +// +// // Create the final object to add to the scene +// Line(geometry, material).apply(scene::add) + +// } + +// fun animate() { +// window.requestAnimationFrame { +// cube.rotation.x += 0.01 +// cube.rotation.y += 0.01 +// animate() +// } +// renderer.render(scene, camera) +// } + +} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three.kt b/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three.kt new file mode 100644 index 00000000..f35d92c4 --- /dev/null +++ b/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three.kt @@ -0,0 +1,13 @@ +package hep.dataforge.vis.spatial + +import info.laht.threekt.core.Object3D + +/** + * Utility methods for three.kt. + * TODO move to three project + */ + +@Suppress("FunctionName") +fun Group(children: Collection) = info.laht.threekt.objects.Group().apply { + children.forEach { this.add(it) } +} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial-js/src/main/web/index.html b/dataforge-vis/dataforge-vis-spatial-js/src/main/web/index.html new file mode 100644 index 00000000..27a1aae4 --- /dev/null +++ b/dataforge-vis/dataforge-vis-spatial-js/src/main/web/index.html @@ -0,0 +1,19 @@ + + + + Three js demo for particle physics + + + + + + +

Demo canvas

+
+ + \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial-js/webpack.config.d/css.js b/dataforge-vis/dataforge-vis-spatial-js/webpack.config.d/css.js new file mode 100644 index 00000000..f15a9f5e --- /dev/null +++ b/dataforge-vis/dataforge-vis-spatial-js/webpack.config.d/css.js @@ -0,0 +1 @@ +config.module.rules.push({ test: /\.css$/, loader: "style!css" }); \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial/build.gradle.kts b/dataforge-vis/dataforge-vis-spatial/build.gradle.kts index c76b3f57..f1d101ae 100644 --- a/dataforge-vis/dataforge-vis-spatial/build.gradle.kts +++ b/dataforge-vis/dataforge-vis-spatial/build.gradle.kts @@ -4,7 +4,26 @@ plugins { kotlin { jvm() - js() + js { + configure(listOf(compilations["main"], compilations["test"])) { + tasks.getByName(compileKotlinTaskName) { + kotlinOptions { + metaInfo = true + sourceMap = true + sourceMapEmbedSources = "always" + moduleKind = "umd" + } + } + } + + configure(listOf(compilations["main"])) { + tasks.getByName(compileKotlinTaskName) { + kotlinOptions { + main = "call" + } + } + } + } sourceSets { val commonMain by getting { @@ -23,4 +42,5 @@ kotlin { } } } -} \ No newline at end of file +} + diff --git a/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt b/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt index 5c0db659..c46b11e6 100644 --- a/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt +++ b/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt @@ -6,7 +6,7 @@ import hep.dataforge.vis.DisplayGroup import hep.dataforge.vis.DisplayObject import hep.dataforge.vis.double -class Box(parent: DisplayObject?, meta: Meta) : DisplayObject3D(parent, TYPE, meta) { +class Box(parent: DisplayObject?, meta: Meta) : DisplayShape3D(parent, TYPE, meta) { var xSize by double(1.0) var ySize by double(1.0) var zSize by double(1.0) diff --git a/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/DisplayObject3D.kt b/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/DisplayObject3D.kt index be72fbd8..29043c88 100644 --- a/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/DisplayObject3D.kt +++ b/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/DisplayObject3D.kt @@ -7,18 +7,32 @@ import hep.dataforge.vis.* import hep.dataforge.vis.DisplayObject.Companion.DEFAULT_TYPE -open class DisplayObject3D(parent: DisplayObject?, type: String, meta: Meta) : DisplayLeaf(parent, type, meta) { - var x by double(0.0) - var y by double(0.0) - var z by double(0.0) +interface DisplayObject3D : DisplayObject { + val x: Double + val y: Double + val z: Double companion object { const val TYPE = "geometry.spatial" } } +open class DisplayShape3D(parent: DisplayObject?, type: String, meta: Meta) : + DisplayLeaf(parent, type, meta), DisplayObject3D { + override var x by double(0.0, inherited = false) + override var y by double(0.0, inherited = false) + override var z by double(0.0, inherited = false) +} + +class DisplayGroup3D(parent: DisplayObject?, type: String, meta: Meta) : DisplayNode(parent, type, meta), + DisplayObject3D { + override var x by double(0.0, inherited = false) + override var y by double(0.0, inherited = false) + override var z by double(0.0, inherited = false) +} + fun DisplayGroup.group(meta: Meta = EmptyMeta, action: DisplayGroup.() -> Unit = {}) = - DisplayNode(this, DEFAULT_TYPE, meta).apply(action).also{addChild(it)} + DisplayNode(this, DEFAULT_TYPE, meta).apply(action).also { addChild(it) } fun Output.render(meta: Meta = EmptyMeta, action: DisplayGroup.() -> Unit) = diff --git a/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt b/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt new file mode 100644 index 00000000..1c693bfc --- /dev/null +++ b/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt @@ -0,0 +1,17 @@ +package hep.dataforge.vis.spatial + +import hep.dataforge.meta.EmptyMeta +import hep.dataforge.meta.Meta +import hep.dataforge.vis.DisplayGroup +import hep.dataforge.vis.DisplayObject + +class Extruded(parent: DisplayObject?, meta: Meta) : DisplayShape3D(parent, TYPE, meta) { + + + companion object { + const val TYPE = "geometry.spatial.extruded" + } +} + +fun DisplayGroup.extrude(meta: Meta = EmptyMeta, action: Extruded.() -> Unit = {}) = + Extruded(this, meta).apply(action).also { addChild(it) } \ No newline at end of file diff --git a/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObjectDelegates.kt b/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObjectDelegates.kt index d99ad547..0c0fdb36 100644 --- a/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObjectDelegates.kt +++ b/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObjectDelegates.kt @@ -5,6 +5,7 @@ import hep.dataforge.names.Name import hep.dataforge.names.toName import hep.dataforge.values.Value import kotlin.jvm.JvmName +import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty @@ -95,4 +96,25 @@ fun DisplayObject.double(default: Double, key: String? = null, inherited: Boolea DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.double } inline fun > DisplayObject.enum(default: E, key: String? = null, inherited: Boolean = true) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { item -> item.string?.let { enumValueOf(it) } } \ No newline at end of file + DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { item -> item.string?.let { enumValueOf(it) } } + +//merge properties + +fun DisplayObject.merge( + key: String? = null, + transformer: (Sequence>) -> T +): ReadOnlyProperty { + return object : ReadOnlyProperty { + override fun getValue(thisRef: DisplayObject, property: KProperty<*>): T { + val name = key?.toName() ?: property.name.toName() + val sequence = sequence> { + var thisObj: DisplayObject? = thisRef + while (thisObj != null) { + thisObj.properties[name]?.let { yield(it) } + thisObj = thisObj.parent + } + } + return transformer(sequence) + } + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 662388fe..c5175c2f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,16 +1,16 @@ pluginManagement { repositories { - mavenCentral() jcenter() gradlePluginPortal() - maven("https://dl.bintray.com/kotlin/kotlin-eap/") + maven("https://dl.bintray.com/kotlin/kotlin-eap") } resolutionStrategy { eachPlugin { when (requested.id.id) { "kotlinx-atomicfu" -> useModule("org.jetbrains.kotlinx:atomicfu-gradle-plugin:${requested.version}") "kotlin-multiplatform" -> useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") - "org.jetbrains.kotlin.frontend" -> useModule("org.jetbrains.kotlin:kotlin-frontend-plugin:0.0.45") + "kotlin2js" -> useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") + "org.jetbrains.kotlin.frontend" -> useModule("org.jetbrains.kotlin:kotlin-frontend-plugin:0.0.45") } } } @@ -29,5 +29,6 @@ include( ":dataforge-scripting", ":dataforge-vis", ":dataforge-vis:dataforge-vis-spatial", - ":dataforge-vis:dataforge-vis-fx" -) + ":dataforge-vis:dataforge-vis-spatial-fx", + ":dataforge-vis:dataforge-vis-spatial-js" +) \ No newline at end of file From f8266d35c293ba974e35e5a7eb1529cb25158da9 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 7 Mar 2019 09:52:04 +0300 Subject: [PATCH 33/37] Build fixed. JS visualization loop added --- build.gradle.kts | 134 +++++++++--------- dataforge-meta/build.gradle.kts | 12 +- .../hep/dataforge/meta/MetaDelegateTest.kt | 6 +- dataforge-vis/build.gradle.kts | 7 - .../dataforge-vis-spatial-fx/build.gradle.kts | 10 +- .../vis/DisplayObjectPropertyListener.kt | 3 + .../hep/dataforge/vis/spatial/FX3DOutput.kt | 8 +- .../hep/dataforge/vis/spatial/Materials.kt | 10 +- .../dataforge/vis/spatial/RendererDemoApp.kt | 3 + .../hep/dataforge/vis/spatial/Materials.kt | 59 ++++++++ .../hep/dataforge/vis/spatial/ThreeDemoApp.kt | 5 +- .../hep/dataforge/vis/spatial/ThreeOutput.kt | 51 ++++--- .../dataforge-vis-spatial/build.gradle.kts | 21 +-- 13 files changed, 195 insertions(+), 134 deletions(-) create mode 100644 dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/Materials.kt diff --git a/build.gradle.kts b/build.gradle.kts index f172e46d..71c5da82 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,5 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile buildscript { val kotlinVersion: String by rootProject.extra("1.3.21") @@ -39,7 +40,7 @@ allprojects { } group = "hep.dataforge" - version = "0.1.1-dev-4" + version = "0.1.1-dev-5" // apply bintray configuration apply(from = "${rootProject.rootDir}/gradle/bintray.gradle") @@ -50,42 +51,6 @@ allprojects { } subprojects { - extensions.findByType()?.apply { - jvm { - compilations.all { - kotlinOptions { - jvmTarget = "1.8" - } - } - } - - js{ - configure(listOf(compilations["main"], compilations["test"])) { - tasks.getByName(compileKotlinTaskName) { - kotlinOptions { - metaInfo = true - sourceMap = true - sourceMapEmbedSources = "always" - moduleKind = "umd" - } - } - } - - configure(listOf(compilations["main"])) { - tasks.getByName(compileKotlinTaskName) { - kotlinOptions { - main = "call" - } - } - } - } - - targets.all { - sourceSets.all { - languageSettings.progressiveMode = true - } - } - } // dokka { // outputFormat = "html" @@ -97,46 +62,79 @@ subprojects { // classifier = "javadoc" // } + // Create empty jar for sources classifier to satisfy maven requirements + val stubSources by tasks.registering(Jar::class) { + archiveClassifier.set("sources") + //from(sourceSets.main.get().allSource) + } - if (!name.startsWith("dataforge")) return@subprojects + // Create empty jar for javadoc classifier to satisfy maven requirements + val stubJavadoc by tasks.registering(Jar::class) { + archiveClassifier.set("javadoc") + } - extensions.findByType()?.apply { - publications.filterIsInstance().forEach { publication -> - if (publication.name == "kotlinMultiplatform") { - // for our root metadata publication, set artifactId with a package and project name - publication.artifactId = project.name - } else { - // for targets, set artifactId with a package, project name and target name (e.g. iosX64) - publication.artifactId = "${project.name}-${publication.name}" - } + tasks.withType { + kotlinOptions{ + jvmTarget = "1.8" } + } - // Create empty jar for sources classifier to satisfy maven requirements - val stubSources by tasks.registering(Jar::class) { - archiveClassifier.set("sources") - //from(sourceSets.main.get().allSource) - } - - // Create empty jar for javadoc classifier to satisfy maven requirements - val stubJavadoc by tasks.registering(Jar::class) { - archiveClassifier.set("javadoc") - } + afterEvaluate { extensions.findByType()?.apply { + jvm { + compilations.all { + kotlinOptions { + jvmTarget = "1.8" + } + } + } - targets.forEach { target -> - val publication = publications.findByName(target.name) as MavenPublication + js { + compilations.all { + tasks.getByName(compileKotlinTaskName) { + kotlinOptions { + metaInfo = true + sourceMap = true + sourceMapEmbedSources = "always" + moduleKind = "umd" + } + } + } - // Patch publications with fake javadoc - publication.artifact(stubJavadoc) + configure(listOf(compilations["main"])) { + tasks.getByName(compileKotlinTaskName) { + kotlinOptions { + main = "call" + } + } + } + } - // Remove gradle metadata publishing from all targets which are not native -// if (target.platformType.name != "native") { -// publication.gradleModuleMetadataFile = null -// tasks.matching { it.name == "generateMetadataFileFor${name.capitalize()}Publication" }.all { -// onlyIf { false } -// } -// } + targets.all { + sourceSets.all { + languageSettings.progressiveMode = true + } + } + + configure { + + publications.filterIsInstance().forEach { publication -> + if (publication.name == "kotlinMultiplatform") { + // for our root metadata publication, set artifactId with a package and project name + publication.artifactId = project.name + } else { + // for targets, set artifactId with a package, project name and target name (e.g. iosX64) + publication.artifactId = "${project.name}-${publication.name}" + } + } + + targets.all { + val publication = publications.findByName(name) as MavenPublication + + // Patch publications with fake javadoc + publication.artifact(stubJavadoc.get()) + } } } } diff --git a/dataforge-meta/build.gradle.kts b/dataforge-meta/build.gradle.kts index c2d39d6c..e754cf4b 100644 --- a/dataforge-meta/build.gradle.kts +++ b/dataforge-meta/build.gradle.kts @@ -47,4 +47,14 @@ kotlin { // mingwTest { // } } -} \ No newline at end of file +} + +//tasks.withType{ +// kotlinOptions{ +// metaInfo = true +// outputFile = "${project.buildDir.path}/js/${project.name}.js" +// sourceMap = true +// moduleKind = "umd" +// main = "call" +// } +//} \ No newline at end of file diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaDelegateTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaDelegateTest.kt index f6929e65..fbd93e62 100644 --- a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaDelegateTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaDelegateTest.kt @@ -14,9 +14,9 @@ class MetaDelegateTest { fun delegateTest() { val testObject = object : Specification { override val config: Config = Config() - var myValue by string() - var safeValue by number(2.2) - var enumValue by enum(TestEnum.YES) + var myValue by config.string() + var safeValue by config.number(2.2) + var enumValue by config.enum(TestEnum.YES) } testObject.config["myValue"] = "theString" testObject.enumValue = TestEnum.NO diff --git a/dataforge-vis/build.gradle.kts b/dataforge-vis/build.gradle.kts index 8b93e57c..c2006692 100644 --- a/dataforge-vis/build.gradle.kts +++ b/dataforge-vis/build.gradle.kts @@ -1,8 +1,5 @@ -import org.openjfx.gradle.JavaFXOptions - plugins { kotlin("multiplatform") - id("org.openjfx.javafxplugin") } kotlin { @@ -25,8 +22,4 @@ kotlin { } } } -} - -configure{ - modules("javafx.controls") } \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial-fx/build.gradle.kts b/dataforge-vis/dataforge-vis-spatial-fx/build.gradle.kts index 3b57a761..92a577a9 100644 --- a/dataforge-vis/dataforge-vis-spatial-fx/build.gradle.kts +++ b/dataforge-vis/dataforge-vis-spatial-fx/build.gradle.kts @@ -1,4 +1,3 @@ -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.openjfx.gradle.JavaFXOptions plugins { @@ -6,7 +5,8 @@ plugins { id("org.openjfx.javafxplugin") } -dependencies{ +dependencies { + api(project(":dataforge-vis")) api(project(":dataforge-vis:dataforge-vis-spatial")) api("no.tornado:tornadofx:1.7.18") implementation("org.fxyz3d:fxyz3d:0.4.0") @@ -14,10 +14,4 @@ dependencies{ configure { modules("javafx.controls") -} - -tasks.withType { - kotlinOptions{ - jvmTarget = "1.8" - } } \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/DisplayObjectPropertyListener.kt b/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/DisplayObjectPropertyListener.kt index 44214460..63eb25c5 100644 --- a/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/DisplayObjectPropertyListener.kt +++ b/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/DisplayObjectPropertyListener.kt @@ -1,5 +1,8 @@ package hep.dataforge.vis +import hep.dataforge.meta.* +import hep.dataforge.names.Name +import hep.dataforge.names.toName import javafx.beans.binding.ObjectBinding import tornadofx.* diff --git a/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/FX3DOutput.kt b/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/FX3DOutput.kt index 2b19bd45..bc674f79 100644 --- a/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/FX3DOutput.kt +++ b/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/FX3DOutput.kt @@ -1,6 +1,12 @@ package hep.dataforge.vis.spatial +import hep.dataforge.context.Context +import hep.dataforge.io.Output +import hep.dataforge.meta.Meta +import hep.dataforge.vis.DisplayGroup import hep.dataforge.vis.DisplayObjectPropertyListener +import hep.dataforge.vis.float +import hep.dataforge.vis.transform import javafx.scene.Group import javafx.scene.Node import org.fxyz3d.shapes.primitives.CuboidMesh @@ -22,7 +28,7 @@ class FX3DOutput(override val context: Context) : Output { val y = listener["y"].float() val z = listener["z"].float() val center = objectBinding(x, y, z) { - Point3D(x.value ?: 0f, y.value ?: 0f, z.value ?: 0f) + org.fxyz3d.geometry.Point3D(x.value ?: 0f, y.value ?: 0f, z.value ?: 0f) } when (obj) { is DisplayGroup3D -> Group(obj.children.map { buildNode(it) }).apply { diff --git a/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/Materials.kt b/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/Materials.kt index 911bb503..3816f9f6 100644 --- a/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/Materials.kt +++ b/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/Materials.kt @@ -1,5 +1,10 @@ package hep.dataforge.vis.spatial +import hep.dataforge.meta.MetaItem +import hep.dataforge.meta.double +import hep.dataforge.meta.get +import hep.dataforge.meta.int +import hep.dataforge.values.ValueType import javafx.scene.paint.Color import javafx.scene.paint.Material import javafx.scene.paint.PhongMaterial @@ -51,11 +56,12 @@ fun MetaItem<*>.color(): Color { /** * Infer FX material based on meta item */ -fun MetaItem<*>.material(): Material { +fun MetaItem<*>?.material(): Material { return when (this) { + null -> Materials.GREY is MetaItem.ValueItem -> PhongMaterial(color()) is MetaItem.NodeItem -> PhongMaterial().apply { - node["color"]?.let { diffuseColor = it.color() } + (node["color"]?: this@material).let { diffuseColor = it.color() } node["specularColor"]?.let { specularColor = it.color() } } } diff --git a/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt b/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt index b339bd9e..7185f113 100644 --- a/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt +++ b/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt @@ -1,5 +1,8 @@ package hep.dataforge.vis.spatial +import hep.dataforge.context.Global +import hep.dataforge.meta.number +import hep.dataforge.vis.DisplayGroup import javafx.scene.Parent import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay diff --git a/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/Materials.kt b/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/Materials.kt new file mode 100644 index 00000000..b8c96a8d --- /dev/null +++ b/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/Materials.kt @@ -0,0 +1,59 @@ +package hep.dataforge.vis.spatial + +import hep.dataforge.meta.MetaItem +import hep.dataforge.meta.double +import hep.dataforge.meta.get +import hep.dataforge.meta.int +import hep.dataforge.values.ValueType +import info.laht.threekt.materials.Material +import info.laht.threekt.materials.MeshPhongMaterial +import info.laht.threekt.math.Color +import info.laht.threekt.math.ColorConstants + +object Materials { + val DEFAULT = MeshPhongMaterial().apply { + this.color.set(ColorConstants.darkgreen) + } +} + +/** + * Infer color based on meta item + */ +fun MetaItem<*>.color(): Color { + return when (this) { + is MetaItem.ValueItem -> if (this.value.type == ValueType.STRING) { + Color(this.value.string) + } else { + val int = value.number.toInt() + val red = int and 0x00ff0000 shr 16 + val green = int and 0x0000ff00 shr 8 + val blue = int and 0x000000ff + Color(red, green, blue) + } + is MetaItem.NodeItem -> { + Color( + node["red"]?.int ?: 0, + node["green"]?.int ?: 0, + node["blue"]?.int ?: 0 + ) + } + } +} + +/** + * Infer FX material based on meta item + */ +fun MetaItem<*>?.material(): Material { + return when (this) { + null -> Materials.DEFAULT + is MetaItem.ValueItem -> MeshPhongMaterial().apply { + color = this@material.color() + } + is MetaItem.NodeItem -> MeshPhongMaterial().apply { + (node["color"] ?: this@material).let { color = it.color() } + opacity = node["opacity"]?.double ?: 1.0 + node["specularColor"]?.let { specular = it.color() } + } + } +} + diff --git a/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeDemoApp.kt b/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeDemoApp.kt index 5cab86a2..51fb296d 100644 --- a/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeDemoApp.kt +++ b/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeDemoApp.kt @@ -4,6 +4,7 @@ import hep.dataforge.context.Global import hep.dataforge.meta.number import hep.dataforge.vis.ApplicationBase import hep.dataforge.vis.DisplayGroup +import info.laht.threekt.external.controls.OrbitControls import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay import kotlinx.coroutines.isActive @@ -17,9 +18,9 @@ class ThreeDemoApp : ApplicationBase() { override val stateKeys: List = emptyList() override fun start(state: Map) { - println("started") val renderer = ThreeOutput(Global) - document.getElementById("canvas")?.appendChild(renderer.root) + renderer.start(document.getElementById("canvas")!!) + println("started") lateinit var group: DisplayGroup diff --git a/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeOutput.kt b/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeOutput.kt index 4afc32cc..af3809e9 100644 --- a/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeOutput.kt +++ b/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeOutput.kt @@ -3,17 +3,25 @@ package hep.dataforge.vis.spatial import hep.dataforge.context.Context import hep.dataforge.io.Output import hep.dataforge.meta.Meta +import hep.dataforge.meta.get import hep.dataforge.vis.DisplayGroup import info.laht.threekt.WebGLRenderer import info.laht.threekt.cameras.PerspectiveCamera +import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.Object3D import info.laht.threekt.external.controls.OrbitControls +import info.laht.threekt.extras.curves.CatmullRomCurve3 import info.laht.threekt.geometries.BoxBufferGeometry import info.laht.threekt.lights.AmbientLight +import info.laht.threekt.materials.LineBasicMaterial +import info.laht.threekt.materials.MeshBasicMaterial import info.laht.threekt.materials.MeshPhongMaterial import info.laht.threekt.math.ColorConstants +import info.laht.threekt.math.Vector3 +import info.laht.threekt.objects.Line import info.laht.threekt.objects.Mesh import info.laht.threekt.scenes.Scene +import org.w3c.dom.Element import kotlin.browser.window class ThreeOutput(override val context: Context) : Output { @@ -23,28 +31,39 @@ class ThreeOutput(override val context: Context) : Output { setSize(window.innerWidth, window.innerHeight) } - private val scene: Scene = Scene().apply { + val scene: Scene = Scene().apply { add(AmbientLight()) } - private val camera = PerspectiveCamera( + + val camera = PerspectiveCamera( 75, window.innerWidth.toDouble() / window.innerHeight, 0.1, 10000 ).apply { - position.z = 4500.0 + position.setZ(1000) } - private val controls: OrbitControls = OrbitControls(camera, renderer.domElement) + val controls: OrbitControls = OrbitControls(camera, renderer.domElement) - val root by lazy { + val root get() = renderer.domElement + + private fun animate() { + window.requestAnimationFrame { + animate() + } + renderer.render(scene, camera) + } + + fun start(element: Element) { window.addEventListener("resize", { camera.aspect = window.innerWidth.toDouble() / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight) }, false) - renderer.domElement + element.appendChild(root) + animate() } @@ -65,12 +84,10 @@ class ThreeOutput(override val context: Context) : Output { this.translateZ(obj.z) } is Box -> { + //TODO add bindings val geometry = BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize) .translate(obj.x, obj.y, obj.z) - val material = MeshPhongMaterial().apply { - this.color.set(ColorConstants.darkgreen) - } - Mesh(geometry, material) + Mesh(geometry, obj.properties["color"].material()) } else -> { logger.error { "No renderer defined for ${obj::class}" } @@ -90,9 +107,9 @@ class ThreeOutput(override val context: Context) : Output { buildNode(obj)?.let { scene.add(it) } } - // init { +// init { // val cube: Mesh - +// // cube = Mesh( // BoxBufferGeometry(1, 1, 1), // MeshPhongMaterial().apply { @@ -125,16 +142,6 @@ class ThreeOutput(override val context: Context) : Output { // // // Create the final object to add to the scene // Line(geometry, material).apply(scene::add) - -// } - -// fun animate() { -// window.requestAnimationFrame { -// cube.rotation.x += 0.01 -// cube.rotation.y += 0.01 -// animate() -// } -// renderer.render(scene, camera) // } } \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial/build.gradle.kts b/dataforge-vis/dataforge-vis-spatial/build.gradle.kts index f1d101ae..8024a052 100644 --- a/dataforge-vis/dataforge-vis-spatial/build.gradle.kts +++ b/dataforge-vis/dataforge-vis-spatial/build.gradle.kts @@ -4,26 +4,7 @@ plugins { kotlin { jvm() - js { - configure(listOf(compilations["main"], compilations["test"])) { - tasks.getByName(compileKotlinTaskName) { - kotlinOptions { - metaInfo = true - sourceMap = true - sourceMapEmbedSources = "always" - moduleKind = "umd" - } - } - } - - configure(listOf(compilations["main"])) { - tasks.getByName(compileKotlinTaskName) { - kotlinOptions { - main = "call" - } - } - } - } + js() sourceSets { val commonMain by getting { From 06864a3496691ac71f58496819e93040facbd4a2 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 8 Mar 2019 10:36:54 +0300 Subject: [PATCH 34/37] Delete gradle.properties --- gradle.properties | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 gradle.properties diff --git a/gradle.properties b/gradle.properties deleted file mode 100644 index 06efe6a4..00000000 --- a/gradle.properties +++ /dev/null @@ -1,7 +0,0 @@ -# Enable official Kotlin Code Style in the IDE. -kotlin.code.style=official - -artifactoryUser=darksnake -artifactoryPassword=nortlander -bintrayUser=altavir -bintrayApiKey=9dcb7a779986e1b08898980269b6d428cadda0c3 \ No newline at end of file From e5e399e0411a7557bd2b51fb96d65dac0fd7141e Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 8 Mar 2019 11:20:08 +0300 Subject: [PATCH 35/37] Moved visualization to a separate project --- .gitignore | 3 +- .../kotlin/hep/dataforge/meta/Meta.kt | 15 +- .../kotlin/hep/dataforge/meta/Styled.kt | 2 +- dataforge-vis/build.gradle.kts | 25 --- .../dataforge-vis-spatial-fx/build.gradle.kts | 17 -- .../hep/dataforge/vis/spatial/Canvas3D.kt | 166 ------------------ .../hep/dataforge/vis/spatial/FX3DOutput.kt | 60 ------- .../hep/dataforge/vis/spatial/Materials.kt | 69 -------- .../dataforge/vis/spatial/RendererDemoApp.kt | 64 ------- .../dataforge-vis-spatial-js/build.gradle.kts | 59 ------- .../package.json.d/package.info | 3 - .../src/main/kotlin/hep/dataforge/vis/HMR.kt | 19 -- .../src/main/kotlin/hep/dataforge/vis/main.kt | 50 ------ .../hep/dataforge/vis/spatial/Materials.kt | 59 ------- .../hep/dataforge/vis/spatial/ThreeDemoApp.kt | 65 ------- .../hep/dataforge/vis/spatial/ThreeOutput.kt | 147 ---------------- .../kotlin/hep/dataforge/vis/spatial/three.kt | 13 -- .../src/main/web/index.html | 19 -- .../webpack.config.d/css.js | 1 - .../dataforge-vis-spatial/build.gradle.kts | 27 --- .../kotlin/hep/dataforge/vis/spatial/Box.kt | 20 --- .../dataforge/vis/spatial/DisplayObject3D.kt | 39 ---- .../hep/dataforge/vis/spatial/Extruded.kt | 17 -- .../hep/dataforge/vis/spatial/Geometry.kt | 8 - .../kotlin/hep/dataforge/vis/DisplayObject.kt | 147 ---------------- .../dataforge/vis/DisplayObjectDelegates.kt | 120 ------------- .../kotlin/hep/dataforge/vis/NamedObject.kt | 40 ----- gradle.properties | 7 - settings.gradle.kts | 6 +- 29 files changed, 16 insertions(+), 1271 deletions(-) delete mode 100644 dataforge-vis/build.gradle.kts delete mode 100644 dataforge-vis/dataforge-vis-spatial-fx/build.gradle.kts delete mode 100644 dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/Canvas3D.kt delete mode 100644 dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/FX3DOutput.kt delete mode 100644 dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/Materials.kt delete mode 100644 dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt delete mode 100644 dataforge-vis/dataforge-vis-spatial-js/build.gradle.kts delete mode 100644 dataforge-vis/dataforge-vis-spatial-js/package.json.d/package.info delete mode 100644 dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/HMR.kt delete mode 100644 dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/main.kt delete mode 100644 dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/Materials.kt delete mode 100644 dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeDemoApp.kt delete mode 100644 dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeOutput.kt delete mode 100644 dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three.kt delete mode 100644 dataforge-vis/dataforge-vis-spatial-js/src/main/web/index.html delete mode 100644 dataforge-vis/dataforge-vis-spatial-js/webpack.config.d/css.js delete mode 100644 dataforge-vis/dataforge-vis-spatial/build.gradle.kts delete mode 100644 dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt delete mode 100644 dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/DisplayObject3D.kt delete mode 100644 dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt delete mode 100644 dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Geometry.kt delete mode 100644 dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObject.kt delete mode 100644 dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObjectDelegates.kt delete mode 100644 dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/NamedObject.kt delete mode 100644 gradle.properties diff --git a/.gitignore b/.gitignore index 7287ad5a..94cef066 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ */build/** -!gradle-wrapper.jar \ No newline at end of file +!gradle-wrapper.jar +gradle.properties \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt index 57affe76..08b1ba09 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -7,6 +7,7 @@ import hep.dataforge.names.Name import hep.dataforge.names.NameToken import hep.dataforge.names.plus import hep.dataforge.names.toName +import hep.dataforge.values.EnumValue import hep.dataforge.values.Value import hep.dataforge.values.boolean @@ -57,7 +58,8 @@ interface Meta : MetaRepr { */ operator fun Map.get(body: String, query: String = ""): T? = get(NameToken(body, query)) -operator fun Meta.get(name: Name): MetaItem? { +operator fun Meta?.get(name: Name): MetaItem? { + if (this == null) return null return name.first()?.let { token -> val tail = name.cutFirst() when (tail.length) { @@ -67,8 +69,8 @@ operator fun Meta.get(name: Name): MetaItem? { } } -operator fun Meta.get(token: NameToken): MetaItem? = items[token] -operator fun Meta.get(key: String): MetaItem? = get(key.toName()) +operator fun Meta?.get(token: NameToken): MetaItem? = this?.items?.get(token) +operator fun Meta?.get(key: String): MetaItem? = get(key.toName()) /** * Get all items matching given name. @@ -168,10 +170,17 @@ val MetaItem<*>?.string get() = value?.string val MetaItem<*>?.boolean get() = value?.boolean val MetaItem<*>?.number get() = value?.number val MetaItem<*>?.double get() = number?.toDouble() +val MetaItem<*>?.float get() = number?.toFloat() val MetaItem<*>?.int get() = number?.toInt() val MetaItem<*>?.long get() = number?.toLong() val MetaItem<*>?.short get() = number?.toShort() +inline fun > MetaItem<*>?.enum() = if (this is ValueItem && this.value is EnumValue<*>) { + this.value as E +} else { + string?.let { enumValueOf(it) } +} + val MetaItem<*>?.stringList get() = value?.list?.map { it.string } ?: emptyList() val MetaItem?.node: M? diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styled.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styled.kt index aed8c413..ba802f5f 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styled.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styled.kt @@ -34,7 +34,7 @@ class Styled(val base: Meta, val style: Config = Config().empty()) : MutableMeta if (item == null) { style.remove(name) } else { - style.set(name, item) + style[name] = item } } diff --git a/dataforge-vis/build.gradle.kts b/dataforge-vis/build.gradle.kts deleted file mode 100644 index c2006692..00000000 --- a/dataforge-vis/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -plugins { - kotlin("multiplatform") -} - -kotlin { - jvm() - js() - - sourceSets { - val commonMain by getting { - dependencies { - api(project(":dataforge-io")) - } - } - val jvmMain by getting { - dependencies { - //api("no.tornado:tornadofx:1.7.18") - } - } - val jsMain by getting { - dependencies { - } - } - } -} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial-fx/build.gradle.kts b/dataforge-vis/dataforge-vis-spatial-fx/build.gradle.kts deleted file mode 100644 index 92a577a9..00000000 --- a/dataforge-vis/dataforge-vis-spatial-fx/build.gradle.kts +++ /dev/null @@ -1,17 +0,0 @@ -import org.openjfx.gradle.JavaFXOptions - -plugins { - kotlin("jvm") - id("org.openjfx.javafxplugin") -} - -dependencies { - api(project(":dataforge-vis")) - api(project(":dataforge-vis:dataforge-vis-spatial")) - api("no.tornado:tornadofx:1.7.18") - implementation("org.fxyz3d:fxyz3d:0.4.0") -} - -configure { - modules("javafx.controls") -} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/Canvas3D.kt b/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/Canvas3D.kt deleted file mode 100644 index bca2ab76..00000000 --- a/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/Canvas3D.kt +++ /dev/null @@ -1,166 +0,0 @@ -package hep.dataforge.vis.spatial - -import javafx.event.EventHandler -import javafx.scene.* -import javafx.scene.input.KeyCode -import javafx.scene.input.KeyEvent -import javafx.scene.input.MouseEvent -import javafx.scene.input.ScrollEvent -import javafx.scene.paint.Color -import org.fxyz3d.utils.CameraTransformer -import tornadofx.* - -class Canvas3D : Fragment() { - val world: Group = Group() - - private val camera = PerspectiveCamera().apply { - nearClip = CAMERA_NEAR_CLIP - farClip = CAMERA_FAR_CLIP - translateZ = CAMERA_INITIAL_DISTANCE - } - - private val cameraShift = CameraTransformer().apply { - val cameraFlip = CameraTransformer() - cameraFlip.children.add(camera) - cameraFlip.setRotateZ(180.0) - children.add(cameraFlip) - } - - val translationXProperty get() = cameraShift.t.xProperty() - var translateX by translationXProperty - val translationYProperty get() = cameraShift.t.yProperty() - var translateY by translationYProperty - val translationZProperty get() = cameraShift.t.zProperty() - var translateZ by translationZProperty - - private val cameraRotation = CameraTransformer().apply { - children.add(cameraShift) - ry.angle = CAMERA_INITIAL_Y_ANGLE - rx.angle = CAMERA_INITIAL_X_ANGLE - rz.angle = CAMERA_INITIAL_Z_ANGLE - } - - val rotationXProperty get() = cameraRotation.rx.angleProperty() - var angleX by rotationXProperty - val rotationYProperty get() = cameraRotation.ry.angleProperty() - var angleY by rotationYProperty - val rotationZProperty get() = cameraRotation.rz.angleProperty() - var angleZ by rotationZProperty - - - override val root =borderpane { - center = SubScene( - Group(world, cameraRotation).apply { DepthTest.ENABLE }, - 1024.0, - 768.0, - true, - SceneAntialiasing.BALANCED - ).apply { - fill = Color.GREY - this.camera = this@Canvas3D.camera - id = "canvas" - handleKeyboard(this) - handleMouse(this) - } - } - - - private fun handleKeyboard(scene: SubScene) { - scene.onKeyPressed = EventHandler { event -> - if (event.isControlDown) { - when (event.code) { - KeyCode.Z -> { - cameraShift.t.x = 0.0 - cameraShift.t.y = 0.0 - camera.translateZ = CAMERA_INITIAL_DISTANCE - cameraRotation.ry.angle = CAMERA_INITIAL_Y_ANGLE - cameraRotation.rx.angle = CAMERA_INITIAL_X_ANGLE - } -// KeyCode.X -> axisGroup.isVisible = !axisGroup.isVisible -// KeyCode.S -> snapshot() -// KeyCode.DIGIT1 -> pixelMap.filterKeys { it.getLayerNumber() == 1 }.values.forEach { -// toggleTransparency( -// it -// ) -// } -// KeyCode.DIGIT2 -> pixelMap.filterKeys { it.getLayerNumber() == 2 }.values.forEach { -// toggleTransparency( -// it -// ) -// } -// KeyCode.DIGIT3 -> pixelMap.filterKeys { it.getLayerNumber() == 3 }.values.forEach { -// toggleTransparency( -// it -// ) -// } - else -> { - }//do nothing - } - } - } - } - - private fun handleMouse(scene: SubScene) { - - var mousePosX: Double = 0.0 - var mousePosY: Double = 0.0 - var mouseOldX: Double = 0.0 - var mouseOldY: Double = 0.0 - var mouseDeltaX: Double = 0.0 - var mouseDeltaY: Double = 0.0 - - scene.onMousePressed = EventHandler { me -> - mousePosX = me.sceneX - mousePosY = me.sceneY - mouseOldX = me.sceneX - mouseOldY = me.sceneY - } - - scene.onMouseDragged = EventHandler { me -> - mouseOldX = mousePosX - mouseOldY = mousePosY - mousePosX = me.sceneX - mousePosY = me.sceneY - mouseDeltaX = mousePosX - mouseOldX - mouseDeltaY = mousePosY - mouseOldY - - val modifier = when { - me.isControlDown -> CONTROL_MULTIPLIER - me.isShiftDown -> SHIFT_MULTIPLIER - else -> 1.0 - } - - if (me.isPrimaryButtonDown) { - cameraRotation.rz.angle = - cameraRotation.rz.angle + mouseDeltaX * MOUSE_SPEED * modifier * ROTATION_SPEED - cameraRotation.rx.angle = - cameraRotation.rx.angle + mouseDeltaY * MOUSE_SPEED * modifier * ROTATION_SPEED - } else if (me.isSecondaryButtonDown) { - cameraShift.t.x = cameraShift.t.x + mouseDeltaX * MOUSE_SPEED * modifier * TRACK_SPEED - cameraShift.t.y = cameraShift.t.y + mouseDeltaY * MOUSE_SPEED * modifier * TRACK_SPEED - } - } - scene.onScroll = EventHandler { event -> - val z = camera.translateZ - val newZ = z + MOUSE_SPEED * event.deltaY * RESIZE_SPEED - camera.translateZ = newZ - } - } - - companion object { - private const val CAMERA_INITIAL_DISTANCE = -4500.0 - private const val CAMERA_INITIAL_X_ANGLE = -50.0 - private const val CAMERA_INITIAL_Y_ANGLE = 0.0 - private const val CAMERA_INITIAL_Z_ANGLE = -210.0 - private const val CAMERA_NEAR_CLIP = 0.1 - private const val CAMERA_FAR_CLIP = 10000.0 - private const val AXIS_LENGTH = 2000.0 - private const val CONTROL_MULTIPLIER = 0.1 - private const val SHIFT_MULTIPLIER = 10.0 - private const val MOUSE_SPEED = 0.1 - private const val ROTATION_SPEED = 2.0 - private const val TRACK_SPEED = 6.0 - private const val RESIZE_SPEED = 50.0 - private const val LINE_WIDTH = 3.0 - } -} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/FX3DOutput.kt b/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/FX3DOutput.kt deleted file mode 100644 index bc674f79..00000000 --- a/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/FX3DOutput.kt +++ /dev/null @@ -1,60 +0,0 @@ -package hep.dataforge.vis.spatial - -import hep.dataforge.context.Context -import hep.dataforge.io.Output -import hep.dataforge.meta.Meta -import hep.dataforge.vis.DisplayGroup -import hep.dataforge.vis.DisplayObjectPropertyListener -import hep.dataforge.vis.float -import hep.dataforge.vis.transform -import javafx.scene.Group -import javafx.scene.Node -import org.fxyz3d.shapes.primitives.CuboidMesh -import tornadofx.* - -/** - * https://github.com/miho/JCSG for operations - * - */ -class FX3DOutput(override val context: Context) : Output { - val canvas by lazy { Canvas3D() } - - - private fun buildNode(obj: Any): Node? { - return when (obj) { - is DisplayShape3D -> { - val listener = DisplayObjectPropertyListener(obj) - val x = listener["x"].float() - val y = listener["y"].float() - val z = listener["z"].float() - val center = objectBinding(x, y, z) { - org.fxyz3d.geometry.Point3D(x.value ?: 0f, y.value ?: 0f, z.value ?: 0f) - } - when (obj) { - is DisplayGroup3D -> Group(obj.children.map { buildNode(it) }).apply { - this.translateXProperty().bind(x) - this.translateYProperty().bind(y) - this.translateZProperty().bind(z) - } - is Box -> CuboidMesh(obj.xSize, obj.ySize, obj.zSize).apply { - this.centerProperty().bind(center) - this.materialProperty().bind(listener["color"].transform { it.material() }) - } - else -> { - logger.error { "No renderer defined for ${obj::class}" } - null - } - } - } - is DisplayGroup -> Group(obj.children.map { buildNode(it) }) // a logical group - else -> { - logger.error { "No renderer defined for ${obj::class}" } - null - } - } - } - - override fun render(obj: Any, meta: Meta) { - buildNode(obj)?.let { canvas.world.children.add(it) } - } -} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/Materials.kt b/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/Materials.kt deleted file mode 100644 index 3816f9f6..00000000 --- a/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/Materials.kt +++ /dev/null @@ -1,69 +0,0 @@ -package hep.dataforge.vis.spatial - -import hep.dataforge.meta.MetaItem -import hep.dataforge.meta.double -import hep.dataforge.meta.get -import hep.dataforge.meta.int -import hep.dataforge.values.ValueType -import javafx.scene.paint.Color -import javafx.scene.paint.Material -import javafx.scene.paint.PhongMaterial - -object Materials { - val RED = PhongMaterial().apply { - diffuseColor = Color.DARKRED - specularColor = Color.RED - } - - val WHITE = PhongMaterial().apply { - diffuseColor = Color.WHITE - specularColor = Color.LIGHTBLUE - } - - val GREY = PhongMaterial().apply { - diffuseColor = Color.DARKGREY - specularColor = Color.GREY - } - - val BLUE = PhongMaterial(Color.BLUE) -} - -/** - * Infer color based on meta item - */ -fun MetaItem<*>.color(): Color { - return when (this) { - is MetaItem.ValueItem -> if (this.value.type == ValueType.STRING) { - Color.web(this.value.string) - } else { - val int = value.number.toInt() - val red = int and 0x00ff0000 shr 16 - val green = int and 0x0000ff00 shr 8 - val blue = int and 0x000000ff - Color.rgb(red, green, blue) - } - is MetaItem.NodeItem -> { - Color.rgb( - node["red"]?.int ?: 0, - node["green"]?.int ?: 0, - node["blue"]?.int ?: 0, - node["opacity"]?.double ?: 1.0 - ) - } - } -} - -/** - * Infer FX material based on meta item - */ -fun MetaItem<*>?.material(): Material { - return when (this) { - null -> Materials.GREY - is MetaItem.ValueItem -> PhongMaterial(color()) - is MetaItem.NodeItem -> PhongMaterial().apply { - (node["color"]?: this@material).let { diffuseColor = it.color() } - node["specularColor"]?.let { specularColor = it.color() } - } - } -} - diff --git a/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt b/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt deleted file mode 100644 index 7185f113..00000000 --- a/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt +++ /dev/null @@ -1,64 +0,0 @@ -package hep.dataforge.vis.spatial - -import hep.dataforge.context.Global -import hep.dataforge.meta.number -import hep.dataforge.vis.DisplayGroup -import javafx.scene.Parent -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.isActive -import kotlinx.coroutines.launch -import tornadofx.* -import kotlin.random.Random - - -class RendererDemoApp : App(RendererDemoView::class) - - -class RendererDemoView : View() { - val renderer = FX3DOutput(Global) - override val root: Parent = borderpane { - center = renderer.canvas.root - } - - lateinit var group: DisplayGroup - - init { - - renderer.render { - group = group { - box { - xSize = 100.0 - ySize = 100.0 - zSize = 100.0 - } - box { - x = 110.0 - xSize = 100.0 - ySize = 100.0 - zSize = 100.0 - } - } - } - - var color by group.properties.number(1530).int - - GlobalScope.launch { - val random = Random(111) - while (isActive) { - delay(1000) - color = random.nextInt(0, Int.MAX_VALUE) - } - } - - renderer.canvas.apply { - angleY = -30.0 - angleX = -15.0 - } - } -} - - -fun main() { - launch() -} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial-js/build.gradle.kts b/dataforge-vis/dataforge-vis-spatial-js/build.gradle.kts deleted file mode 100644 index 1336ce78..00000000 --- a/dataforge-vis/dataforge-vis-spatial-js/build.gradle.kts +++ /dev/null @@ -1,59 +0,0 @@ -import org.jetbrains.kotlin.gradle.frontend.KotlinFrontendExtension -import org.jetbrains.kotlin.gradle.frontend.npm.NpmExtension -import org.jetbrains.kotlin.gradle.frontend.webpack.WebPackExtension - -plugins { - id("kotlin2js") - id("kotlin-dce-js") - id("org.jetbrains.kotlin.frontend") -} - - -dependencies { - api(project(":dataforge-vis:dataforge-vis-spatial")) - implementation("info.laht.threekt:threejs-wrapper:0.88-npm-1") -} - -configure { - downloadNodeJsVersion = "latest" - - configure { - dependency("three") - dependency("three-orbitcontrols") - dependency("style-loader") - devDependency("karma") - } - - sourceMaps = true - - bundle("webpack") { - this as WebPackExtension - bundleName = "main" - proxyUrl = "http://localhost:8080" - contentPath = file("src/main/web") - sourceMapEnabled = true - //mode = "production" - mode = "development" - } -} - -tasks{ - compileKotlin2Js{ - kotlinOptions{ - metaInfo = true - outputFile = "${project.buildDir.path}/js/${project.name}.js" - sourceMap = true - moduleKind = "umd" - main = "call" - } - } - - compileTestKotlin2Js{ - kotlinOptions{ - metaInfo = true - outputFile = "${project.buildDir.path}/js/${project.name}-test.js" - sourceMap = true - moduleKind = "umd" - } - } -} diff --git a/dataforge-vis/dataforge-vis-spatial-js/package.json.d/package.info b/dataforge-vis/dataforge-vis-spatial-js/package.json.d/package.info deleted file mode 100644 index e2d5f174..00000000 --- a/dataforge-vis/dataforge-vis-spatial-js/package.json.d/package.info +++ /dev/null @@ -1,3 +0,0 @@ -{ - "description": "A demo project for particle visualization in JS" -} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/HMR.kt b/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/HMR.kt deleted file mode 100644 index a77b9cf6..00000000 --- a/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/HMR.kt +++ /dev/null @@ -1,19 +0,0 @@ -package hep.dataforge.vis.hmr - -external val module: Module - -external interface Module { - val hot: Hot? -} - -external interface Hot { - val data: dynamic - - fun accept() - fun accept(dependency: String, callback: () -> Unit) - fun accept(dependencies: Array, callback: (updated: Array) -> Unit) - - fun dispose(callback: (data: dynamic) -> Unit) -} - -external fun require(name: String): dynamic \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/main.kt b/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/main.kt deleted file mode 100644 index f4359987..00000000 --- a/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/main.kt +++ /dev/null @@ -1,50 +0,0 @@ -package hep.dataforge.vis - -import hep.dataforge.vis.hmr.module -import hep.dataforge.vis.spatial.ThreeDemoApp -import kotlin.browser.document -import kotlin.dom.hasClass - - -abstract class ApplicationBase { - abstract val stateKeys: List - - abstract fun start(state: Map) - abstract fun dispose(): Map -} - - -fun main() { - var application: ApplicationBase? = null - - val state: dynamic = module.hot?.let { hot -> - hot.accept() - - hot.dispose { data -> - data.appState = application?.dispose() - application = null - } - - hot.data - } - - if (document.body != null) { - application = start(state) - } else { - application = null - document.addEventListener("DOMContentLoaded", { application = start(state) }) - } -} - -fun start(state: dynamic): ApplicationBase? { - return if (document.body?.hasClass("testApp") == true) { - val application = ThreeDemoApp() - - @Suppress("UnsafeCastFromDynamic") - application.start(state?.appState ?: emptyMap()) - - application - } else { - null - } -} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/Materials.kt b/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/Materials.kt deleted file mode 100644 index b8c96a8d..00000000 --- a/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/Materials.kt +++ /dev/null @@ -1,59 +0,0 @@ -package hep.dataforge.vis.spatial - -import hep.dataforge.meta.MetaItem -import hep.dataforge.meta.double -import hep.dataforge.meta.get -import hep.dataforge.meta.int -import hep.dataforge.values.ValueType -import info.laht.threekt.materials.Material -import info.laht.threekt.materials.MeshPhongMaterial -import info.laht.threekt.math.Color -import info.laht.threekt.math.ColorConstants - -object Materials { - val DEFAULT = MeshPhongMaterial().apply { - this.color.set(ColorConstants.darkgreen) - } -} - -/** - * Infer color based on meta item - */ -fun MetaItem<*>.color(): Color { - return when (this) { - is MetaItem.ValueItem -> if (this.value.type == ValueType.STRING) { - Color(this.value.string) - } else { - val int = value.number.toInt() - val red = int and 0x00ff0000 shr 16 - val green = int and 0x0000ff00 shr 8 - val blue = int and 0x000000ff - Color(red, green, blue) - } - is MetaItem.NodeItem -> { - Color( - node["red"]?.int ?: 0, - node["green"]?.int ?: 0, - node["blue"]?.int ?: 0 - ) - } - } -} - -/** - * Infer FX material based on meta item - */ -fun MetaItem<*>?.material(): Material { - return when (this) { - null -> Materials.DEFAULT - is MetaItem.ValueItem -> MeshPhongMaterial().apply { - color = this@material.color() - } - is MetaItem.NodeItem -> MeshPhongMaterial().apply { - (node["color"] ?: this@material).let { color = it.color() } - opacity = node["opacity"]?.double ?: 1.0 - node["specularColor"]?.let { specular = it.color() } - } - } -} - diff --git a/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeDemoApp.kt b/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeDemoApp.kt deleted file mode 100644 index 51fb296d..00000000 --- a/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeDemoApp.kt +++ /dev/null @@ -1,65 +0,0 @@ -package hep.dataforge.vis.spatial - -import hep.dataforge.context.Global -import hep.dataforge.meta.number -import hep.dataforge.vis.ApplicationBase -import hep.dataforge.vis.DisplayGroup -import info.laht.threekt.external.controls.OrbitControls -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.isActive -import kotlinx.coroutines.launch -import kotlin.browser.document -import kotlin.random.Random - - -class ThreeDemoApp : ApplicationBase() { - - override val stateKeys: List = emptyList() - - override fun start(state: Map) { - val renderer = ThreeOutput(Global) - renderer.start(document.getElementById("canvas")!!) - println("started") - - lateinit var group: DisplayGroup - - renderer.render { - group = group { - box { - xSize = 100.0 - ySize = 100.0 - zSize = 100.0 - } - box { - x = 110.0 - xSize = 100.0 - ySize = 100.0 - zSize = 100.0 - } - } - } - - var color by group.properties.number(1530).int - - GlobalScope.launch { - val random = Random(111) - while (isActive) { - delay(1000) - color = random.nextInt(0, Int.MAX_VALUE) - } - } - -// view.animate() - -// view = WebLinesView(document.getElementById("lines")!!, document.getElementById("addForm")!!) -// presenter = LinesPresenter(view) -// -// state["lines"]?.let { linesState -> -// @Suppress("UNCHECKED_CAST") -// presenter.restore(linesState as Array) -// } - } - - override fun dispose() = emptyMap()//mapOf("lines" to presenter.dispose()) -} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeOutput.kt b/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeOutput.kt deleted file mode 100644 index af3809e9..00000000 --- a/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeOutput.kt +++ /dev/null @@ -1,147 +0,0 @@ -package hep.dataforge.vis.spatial - -import hep.dataforge.context.Context -import hep.dataforge.io.Output -import hep.dataforge.meta.Meta -import hep.dataforge.meta.get -import hep.dataforge.vis.DisplayGroup -import info.laht.threekt.WebGLRenderer -import info.laht.threekt.cameras.PerspectiveCamera -import info.laht.threekt.core.BufferGeometry -import info.laht.threekt.core.Object3D -import info.laht.threekt.external.controls.OrbitControls -import info.laht.threekt.extras.curves.CatmullRomCurve3 -import info.laht.threekt.geometries.BoxBufferGeometry -import info.laht.threekt.lights.AmbientLight -import info.laht.threekt.materials.LineBasicMaterial -import info.laht.threekt.materials.MeshBasicMaterial -import info.laht.threekt.materials.MeshPhongMaterial -import info.laht.threekt.math.ColorConstants -import info.laht.threekt.math.Vector3 -import info.laht.threekt.objects.Line -import info.laht.threekt.objects.Mesh -import info.laht.threekt.scenes.Scene -import org.w3c.dom.Element -import kotlin.browser.window - -class ThreeOutput(override val context: Context) : Output { - - private val renderer = WebGLRenderer { antialias = true }.apply { - setClearColor(ColorConstants.skyblue, 1) - setSize(window.innerWidth, window.innerHeight) - } - - val scene: Scene = Scene().apply { - add(AmbientLight()) - } - - val camera = PerspectiveCamera( - 75, - window.innerWidth.toDouble() / window.innerHeight, - 0.1, - 10000 - ).apply { - position.setZ(1000) - } - - val controls: OrbitControls = OrbitControls(camera, renderer.domElement) - - val root get() = renderer.domElement - - private fun animate() { - window.requestAnimationFrame { - animate() - } - renderer.render(scene, camera) - } - - fun start(element: Element) { - window.addEventListener("resize", { - camera.aspect = window.innerWidth.toDouble() / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize(window.innerWidth, window.innerHeight) - }, false) - element.appendChild(root) - animate() - } - - - private fun buildNode(obj: Any): Object3D? { - return when (obj) { - is DisplayShape3D -> { -// val listener = DisplayObjectPropertyListener(obj) -// val x = listener["x"].float() -// val y = listener["y"].float() -// val z = listener["z"].float() -// val center = objectBinding(x, y, z) { -// Vector3(x.value ?: 0f, y.value ?: 0f, z.value ?: 0f) -// } - when (obj) { - is DisplayGroup3D -> Group(obj.children.mapNotNull { buildNode(it) }).apply { - this.translateX(obj.x) - this.translateY(obj.y) - this.translateZ(obj.z) - } - is Box -> { - //TODO add bindings - val geometry = BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize) - .translate(obj.x, obj.y, obj.z) - Mesh(geometry, obj.properties["color"].material()) - } - else -> { - logger.error { "No renderer defined for ${obj::class}" } - null - } - } - } - is DisplayGroup -> Group(obj.children.mapNotNull { buildNode(it) }) // a logical group - else -> { - logger.error { "No renderer defined for ${obj::class}" } - null - } - } - } - - override fun render(obj: Any, meta: Meta) { - buildNode(obj)?.let { scene.add(it) } - } - -// init { -// val cube: Mesh -// -// cube = Mesh( -// BoxBufferGeometry(1, 1, 1), -// MeshPhongMaterial().apply { -// this.color.set(ColorConstants.darkgreen) -// } -// ).also(scene::add) -// -// Mesh(cube.geometry as BufferGeometry, -// MeshBasicMaterial().apply { -// this.wireframe = true -// this.color.set(ColorConstants.black) -// } -// ).also(cube::add) -// -// val points = CatmullRomCurve3( -// arrayOf( -// Vector3(-10, 0, 10), -// Vector3(-5, 5, 5), -// Vector3(0, 0, 0), -// Vector3(5, -5, 5), -// Vector3(10, 0, 10) -// ) -// ).getPoints(50) -// -// val geometry = BufferGeometry().setFromPoints(points) -// -// val material = LineBasicMaterial().apply { -// color.set(0xff0000) -// } -// -// // Create the final object to add to the scene -// Line(geometry, material).apply(scene::add) -// } - -} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three.kt b/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three.kt deleted file mode 100644 index f35d92c4..00000000 --- a/dataforge-vis/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three.kt +++ /dev/null @@ -1,13 +0,0 @@ -package hep.dataforge.vis.spatial - -import info.laht.threekt.core.Object3D - -/** - * Utility methods for three.kt. - * TODO move to three project - */ - -@Suppress("FunctionName") -fun Group(children: Collection) = info.laht.threekt.objects.Group().apply { - children.forEach { this.add(it) } -} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial-js/src/main/web/index.html b/dataforge-vis/dataforge-vis-spatial-js/src/main/web/index.html deleted file mode 100644 index 27a1aae4..00000000 --- a/dataforge-vis/dataforge-vis-spatial-js/src/main/web/index.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - Three js demo for particle physics - - - - - - -

Demo canvas

-
- - \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial-js/webpack.config.d/css.js b/dataforge-vis/dataforge-vis-spatial-js/webpack.config.d/css.js deleted file mode 100644 index f15a9f5e..00000000 --- a/dataforge-vis/dataforge-vis-spatial-js/webpack.config.d/css.js +++ /dev/null @@ -1 +0,0 @@ -config.module.rules.push({ test: /\.css$/, loader: "style!css" }); \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial/build.gradle.kts b/dataforge-vis/dataforge-vis-spatial/build.gradle.kts deleted file mode 100644 index 8024a052..00000000 --- a/dataforge-vis/dataforge-vis-spatial/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -plugins { - kotlin("multiplatform") -} - -kotlin { - jvm() - js() - - sourceSets { - val commonMain by getting { - dependencies { - api(project(":dataforge-vis")) - } - } - val jvmMain by getting { - dependencies { - - } - } - val jsMain by getting { - dependencies { - - } - } - } -} - diff --git a/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt b/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt deleted file mode 100644 index c46b11e6..00000000 --- a/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt +++ /dev/null @@ -1,20 +0,0 @@ -package hep.dataforge.vis.spatial - -import hep.dataforge.meta.EmptyMeta -import hep.dataforge.meta.Meta -import hep.dataforge.vis.DisplayGroup -import hep.dataforge.vis.DisplayObject -import hep.dataforge.vis.double - -class Box(parent: DisplayObject?, meta: Meta) : DisplayShape3D(parent, TYPE, meta) { - var xSize by double(1.0) - var ySize by double(1.0) - var zSize by double(1.0) - - companion object { - const val TYPE = "geometry.spatial.box" - } -} - -fun DisplayGroup.box(meta: Meta = EmptyMeta, action: Box.() -> Unit = {}) = - Box(this, meta).apply(action).also { addChild(it) } \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/DisplayObject3D.kt b/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/DisplayObject3D.kt deleted file mode 100644 index 29043c88..00000000 --- a/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/DisplayObject3D.kt +++ /dev/null @@ -1,39 +0,0 @@ -package hep.dataforge.vis.spatial - -import hep.dataforge.io.Output -import hep.dataforge.meta.EmptyMeta -import hep.dataforge.meta.Meta -import hep.dataforge.vis.* -import hep.dataforge.vis.DisplayObject.Companion.DEFAULT_TYPE - - -interface DisplayObject3D : DisplayObject { - val x: Double - val y: Double - val z: Double - - companion object { - const val TYPE = "geometry.spatial" - } -} - -open class DisplayShape3D(parent: DisplayObject?, type: String, meta: Meta) : - DisplayLeaf(parent, type, meta), DisplayObject3D { - override var x by double(0.0, inherited = false) - override var y by double(0.0, inherited = false) - override var z by double(0.0, inherited = false) -} - -class DisplayGroup3D(parent: DisplayObject?, type: String, meta: Meta) : DisplayNode(parent, type, meta), - DisplayObject3D { - override var x by double(0.0, inherited = false) - override var y by double(0.0, inherited = false) - override var z by double(0.0, inherited = false) -} - -fun DisplayGroup.group(meta: Meta = EmptyMeta, action: DisplayGroup.() -> Unit = {}) = - DisplayNode(this, DEFAULT_TYPE, meta).apply(action).also { addChild(it) } - - -fun Output.render(meta: Meta = EmptyMeta, action: DisplayGroup.() -> Unit) = - render(DisplayNode(null, DEFAULT_TYPE, EmptyMeta).apply(action), meta) diff --git a/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt b/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt deleted file mode 100644 index 1c693bfc..00000000 --- a/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt +++ /dev/null @@ -1,17 +0,0 @@ -package hep.dataforge.vis.spatial - -import hep.dataforge.meta.EmptyMeta -import hep.dataforge.meta.Meta -import hep.dataforge.vis.DisplayGroup -import hep.dataforge.vis.DisplayObject - -class Extruded(parent: DisplayObject?, meta: Meta) : DisplayShape3D(parent, TYPE, meta) { - - - companion object { - const val TYPE = "geometry.spatial.extruded" - } -} - -fun DisplayGroup.extrude(meta: Meta = EmptyMeta, action: Extruded.() -> Unit = {}) = - Extruded(this, meta).apply(action).also { addChild(it) } \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Geometry.kt b/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Geometry.kt deleted file mode 100644 index 53766a49..00000000 --- a/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Geometry.kt +++ /dev/null @@ -1,8 +0,0 @@ -package hep.dataforge.vis.spatial - -//TODO replace by platform optimized version -data class Point3D( - val x: Float, - val y: Float, - val z: Float -) \ No newline at end of file diff --git a/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObject.kt b/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObject.kt deleted file mode 100644 index cb9dd110..00000000 --- a/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObject.kt +++ /dev/null @@ -1,147 +0,0 @@ -package hep.dataforge.vis - -import hep.dataforge.meta.* -import hep.dataforge.names.Name -import hep.dataforge.vis.DisplayObject.Companion.DEFAULT_TYPE -import hep.dataforge.vis.DisplayObject.Companion.META_KEY -import hep.dataforge.vis.DisplayObject.Companion.TAGS_KEY - -/** - * A root type for display hierarchy - */ -interface DisplayObject { - - /** - * The parent object of this one. If null, this one is a root. - */ - val parent: DisplayObject? - - /** - * The type of this object. Uses `.` notation. Empty type means untyped group - */ - val type: String - - val properties: Styled - - companion object { - const val DEFAULT_TYPE = "" - const val TYPE_KEY = "@type" - const val CHILDREN_KEY = "@children" - const val META_KEY = "@meta" - const val TAGS_KEY = "@tags" - } -} - -interface DisplayGroup : DisplayObject { - - val children: List - - /** - * Add a child object and notify listeners - */ - fun addChild(obj: DisplayObject) - - /** - * Remove a specific child and notify listeners - */ - fun removeChild(obj: DisplayObject) - - /** - * Add listener for children change - * TODO add detailed information into change listener - */ - fun onChildrenChange(owner: Any? = null, action: () -> Unit) - - /** - * Remove children change listener - */ - fun removeChildrenChangeListener(owner: Any? = null) -} - -/** - * Get the property of this display object of parent's if not found - */ -tailrec fun DisplayObject.getProperty(name: Name): MetaItem<*>? = properties[name] ?: parent?.getProperty(name) - -/** - * A change listener for [DisplayObject] configuration. - */ -fun DisplayObject.onChange(owner: Any?, action: (Name, before: MetaItem<*>?, after: MetaItem<*>?) -> Unit) { - properties.style.onChange(owner, action) - parent?.onChange(owner, action) -} - -/** - * Remove all meta listeners with matching owners - */ -fun DisplayObject.removeChangeListener(owner: Any?) { - properties.style.removeListener(owner) - parent?.removeChangeListener(owner) -} - - -/** - * Additional meta not relevant to display - */ -val DisplayObject.meta: Meta get() = properties[META_KEY]?.node ?: EmptyMeta - -val DisplayObject.tags: List get() = properties[TAGS_KEY].stringList - -internal data class ObjectListener( - val owner: Any?, - val action: () -> Unit -) - -/** - * Basic group of display objects - */ -open class DisplayNode( - override val parent: DisplayObject? = null, - override val type: String = DEFAULT_TYPE, - meta: Meta = EmptyMeta -) : DisplayGroup { - - private val _children = ArrayList() - override val children: List get() = _children - override val properties = Styled(meta) - private val listeners = HashSet() - - override fun addChild(obj: DisplayObject) { -// val before = _children[name] -// if (obj == null) { -// _children.remove(name) -// } else { -// _children[name] = obj -// } -// listeners.forEach { it.action(name, before, obj) } - _children.add(obj) - listeners.forEach { it.action() } - } - - override fun removeChild(obj: DisplayObject) { - if (_children.remove(obj)) { - listeners.forEach { it.action } - } - } - - override fun onChildrenChange(owner: Any?, action: () -> Unit) { - listeners.add(ObjectListener(owner, action)) - } - - - override fun removeChildrenChangeListener(owner: Any?) { - listeners.removeAll { it.owner === owner } - } -} - -/** - * Basic [DisplayObject] leaf element - */ -open class DisplayLeaf( - override val parent: DisplayObject?, - override val type: String, - meta: Meta = EmptyMeta -) : DisplayObject { - final override val properties = Styled(meta) -} - diff --git a/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObjectDelegates.kt b/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObjectDelegates.kt deleted file mode 100644 index 0c0fdb36..00000000 --- a/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObjectDelegates.kt +++ /dev/null @@ -1,120 +0,0 @@ -package hep.dataforge.vis - -import hep.dataforge.meta.* -import hep.dataforge.names.Name -import hep.dataforge.names.toName -import hep.dataforge.values.Value -import kotlin.jvm.JvmName -import kotlin.properties.ReadOnlyProperty -import kotlin.properties.ReadWriteProperty -import kotlin.reflect.KProperty - -/** - * A delegate for display object properties - */ -class DisplayObjectDelegate( - val key: Name?, - val default: MetaItem<*>?, - val inherited: Boolean -) : ReadWriteProperty?> { - override fun getValue(thisRef: DisplayObject, property: KProperty<*>): MetaItem<*>? { - val name = key ?: property.name.toName() - return if (inherited) { - thisRef.getProperty(name) - } else { - thisRef.properties[name] - } ?: default - } - - override fun setValue(thisRef: DisplayObject, property: KProperty<*>, value: MetaItem<*>?) { - val name = key ?: property.name.toName() - thisRef.properties.style[name] = value - } -} - -class DisplayObjectDelegateWrapper( - val key: Name?, - val default: T, - val inherited: Boolean, - val write: Config.(name: Name, value: T) -> Unit = { name, value -> set(name, value) }, - val read: (MetaItem<*>?) -> T? -) : ReadWriteProperty { - override fun getValue(thisRef: DisplayObject, property: KProperty<*>): T { - val name = key ?: property.name.toName() - return if (inherited) { - read(thisRef.getProperty(name)) - } else { - read(thisRef.properties[name]) - } ?: default - } - - override fun setValue(thisRef: DisplayObject, property: KProperty<*>, value: T) { - val name = key ?: property.name.toName() - thisRef.properties.style.write(name, value) - } -} - - -fun DisplayObject.value(default: Value? = null, key: String? = null, inherited: Boolean = true) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.value } - -fun DisplayObject.string(default: String? = null, key: String? = null, inherited: Boolean = true) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.string } - -fun DisplayObject.boolean(default: Boolean? = null, key: String? = null, inherited: Boolean = true) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.boolean } - -fun DisplayObject.number(default: Number? = null, key: String? = null, inherited: Boolean = true) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.number } - -fun DisplayObject.double(default: Double? = null, key: String? = null, inherited: Boolean = true) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.double } - -fun DisplayObject.int(default: Int? = null, key: String? = null, inherited: Boolean = true) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.int } - - -fun DisplayObject.node(key: String? = null, inherited: Boolean = true) = - DisplayObjectDelegateWrapper(key?.toName(), null, inherited) { it.node } - -//fun Configurable.spec(spec: Specification, key: String? = null) = ChildConfigDelegate(key) { spec.wrap(this) } - -@JvmName("safeString") -fun DisplayObject.string(default: String, key: String? = null, inherited: Boolean = true) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.string } - -@JvmName("safeBoolean") -fun DisplayObject.boolean(default: Boolean, key: String? = null, inherited: Boolean = true) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.boolean } - -@JvmName("safeNumber") -fun DisplayObject.number(default: Number, key: String? = null, inherited: Boolean = true) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.number } - -@JvmName("safeDouble") -fun DisplayObject.double(default: Double, key: String? = null, inherited: Boolean = true) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.double } - -inline fun > DisplayObject.enum(default: E, key: String? = null, inherited: Boolean = true) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { item -> item.string?.let { enumValueOf(it) } } - -//merge properties - -fun DisplayObject.merge( - key: String? = null, - transformer: (Sequence>) -> T -): ReadOnlyProperty { - return object : ReadOnlyProperty { - override fun getValue(thisRef: DisplayObject, property: KProperty<*>): T { - val name = key?.toName() ?: property.name.toName() - val sequence = sequence> { - var thisObj: DisplayObject? = thisRef - while (thisObj != null) { - thisObj.properties[name]?.let { yield(it) } - thisObj = thisObj.parent - } - } - return transformer(sequence) - } - } -} \ No newline at end of file diff --git a/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/NamedObject.kt b/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/NamedObject.kt deleted file mode 100644 index bc15d9e6..00000000 --- a/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/NamedObject.kt +++ /dev/null @@ -1,40 +0,0 @@ -package hep.dataforge.vis - -import hep.dataforge.names.Name -import hep.dataforge.names.NameToken - -interface NamedObject : DisplayObject { - val name: String - - operator fun get(nameToken: NameToken): DisplayGroup? - - operator fun set(nameToken: NameToken, group: DisplayGroup) -} - -/** - * Recursively get a child - */ -tailrec operator fun NamedObject.get(name: Name): DisplayObject? = when (name.length) { - 0 -> this - 1 -> this[name[0]] - else -> name.first()?.let { this[it] as? NamedObject }?.get(name.cutFirst()) -} - - -/** - * Set given object creating intermediate empty groups if needed - * @param name - the full name of a child - * @param objFactory - a function that creates child object from parent (to avoid mutable parent parameter) - */ -fun NamedObject.set(name: Name, objFactory: (parent: DisplayObject) -> DisplayGroup): Unit = when (name.length) { - 0 -> error("Can't set object with empty name") - 1 -> set(name[0], objFactory(this)) - else -> (this[name.first()!!] ?: DisplayNode(this)) - .run { - if (this is NamedObject) { - this.set(name.cutFirst(), objFactory) - } else { - error("Can't assign child to a leaf element $this") - } - } -} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties deleted file mode 100644 index 06efe6a4..00000000 --- a/gradle.properties +++ /dev/null @@ -1,7 +0,0 @@ -# Enable official Kotlin Code Style in the IDE. -kotlin.code.style=official - -artifactoryUser=darksnake -artifactoryPassword=nortlander -bintrayUser=altavir -bintrayApiKey=9dcb7a779986e1b08898980269b6d428cadda0c3 \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index c5175c2f..c7ab404e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -26,9 +26,5 @@ include( ":dataforge-data", ":dataforge-io", ":dataforge-workspace", - ":dataforge-scripting", - ":dataforge-vis", - ":dataforge-vis:dataforge-vis-spatial", - ":dataforge-vis:dataforge-vis-spatial-fx", - ":dataforge-vis:dataforge-vis-spatial-js" + ":dataforge-scripting" ) \ No newline at end of file From 593dc8ca798adceb1b69e458d92bb7a657b87a6e Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 8 Mar 2019 11:29:14 +0300 Subject: [PATCH 36/37] Moved visualization to a separate project --- .../vis/DisplayObjectPropertyListener.kt | 38 ------------------- 1 file changed, 38 deletions(-) delete mode 100644 dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/DisplayObjectPropertyListener.kt diff --git a/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/DisplayObjectPropertyListener.kt b/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/DisplayObjectPropertyListener.kt deleted file mode 100644 index 63eb25c5..00000000 --- a/dataforge-vis/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/DisplayObjectPropertyListener.kt +++ /dev/null @@ -1,38 +0,0 @@ -package hep.dataforge.vis - -import hep.dataforge.meta.* -import hep.dataforge.names.Name -import hep.dataforge.names.toName -import javafx.beans.binding.ObjectBinding -import tornadofx.* - -class DisplayObjectPropertyListener(val obj: DisplayObject) { - private val binndings = HashMap?>>() - - init { - obj.onChange(this) { name, _, _ -> - binndings[name]?.invalidate() - } - } - - operator fun get(key: Name): ObjectBinding?> { - return binndings.getOrPut(key) { - object : ObjectBinding?>() { - override fun computeValue(): MetaItem<*>? = obj.getProperty(key) - } - } - } - - operator fun get(key: String) = get(key.toName()) -} - -fun ObjectBinding?>.value() = this.objectBinding { it.value } -fun ObjectBinding?>.string() = this.stringBinding { it.string } -fun ObjectBinding?>.number() = this.objectBinding { it.number } -fun ObjectBinding?>.double() = this.objectBinding { it.double } -fun ObjectBinding?>.float() = this.objectBinding { it.number?.toFloat() } -fun ObjectBinding?>.int() = this.objectBinding { it.int } -fun ObjectBinding?>.long() = this.objectBinding { it.long } -fun ObjectBinding?>.node() = this.objectBinding { it.node } - -fun ObjectBinding?>.transform(transform: (MetaItem<*>) -> T) = this.objectBinding { it?.let(transform) } From 5eb2bcfce6273f886cada41db84e010cde6f043f Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 8 Mar 2019 11:34:51 +0300 Subject: [PATCH 37/37] Fixed config delegates --- .../hep/dataforge/meta/ConfigDelegates.kt | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ConfigDelegates.kt diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ConfigDelegates.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ConfigDelegates.kt new file mode 100644 index 00000000..581fa7ed --- /dev/null +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ConfigDelegates.kt @@ -0,0 +1,42 @@ +package hep.dataforge.meta + +import hep.dataforge.values.Null +import hep.dataforge.values.Value +import kotlin.jvm.JvmName + + +//Configurable delegates + +/** + * A property delegate that uses custom key + */ +fun Configurable.value(default: Value = Null, key: String? = null) = + ValueConfigDelegate(config, key, default) + +fun Configurable.string(default: String? = null, key: String? = null) = + StringConfigDelegate(config, key, default) + +fun Configurable.boolean(default: Boolean? = null, key: String? = null) = + BooleanConfigDelegate(config, key, default) + +fun Configurable.number(default: Number? = null, key: String? = null) = + NumberConfigDelegate(config, key, default) + +fun Configurable.child(key: String? = null) = MetaNodeDelegate(config, key) + +//fun Configurable.spec(spec: Specification, key: String? = null) = ChildConfigDelegate(key) { spec.wrap(this) } + +@JvmName("safeString") +fun Configurable.string(default: String, key: String? = null) = + SafeStringConfigDelegate(config, key, default) + +@JvmName("safeBoolean") +fun Configurable.boolean(default: Boolean, key: String? = null) = + SafeBooleanConfigDelegate(config, key, default) + +@JvmName("safeNumber") +fun Configurable.number(default: Number, key: String? = null) = + SafeNumberConfigDelegate(config, key, default) + +inline fun > Configurable.enum(default: E, key: String? = null) = + SafeEnumvConfigDelegate(config, key, default) { enumValueOf(it) } \ No newline at end of file