From 10b83853240304f24d7c4aa1010a2b66a54ed432 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 8 Sep 2019 15:11:46 +0300 Subject: [PATCH] Named return Name instead of String --- .../hep/dataforge/context/AbstractPlugin.kt | 25 ++++- .../kotlin/hep/dataforge/context/Context.kt | 16 +-- .../hep/dataforge/context/ContextBuilder.kt | 5 +- .../kotlin/hep/dataforge/context/Named.kt | 13 ++- .../kotlin/hep/dataforge/context/Plugin.kt | 6 +- .../hep/dataforge/context/ContextTest.kt | 3 +- .../kotlin/hep/dataforge/io/Binary.kt | 10 +- .../hep/dataforge/io/BinaryMetaFormat.kt | 8 +- .../kotlin/hep/dataforge/io/Envelope.kt | 4 + .../kotlin/hep/dataforge/io/EnvelopeFormat.kt | 6 ++ .../kotlin/hep/dataforge/io/IOFormat.kt | 51 ++++++++- .../kotlin/hep/dataforge/io/IOPlugin.kt | 26 ++++- .../kotlin/hep/dataforge/io/JsonMetaFormat.kt | 4 +- .../kotlin/hep/dataforge/io/MetaFormat.kt | 9 +- .../hep/dataforge/io/TaggedEnvelopeFormat.kt | 4 +- .../dataforge/io/functions/FunctionServer.kt | 43 ++++++-- .../dataforge/io/functions/FunctionSpec.kt | 9 -- .../dataforge/io/functions/FunctionsPlugin.kt | 58 ---------- .../io/functions/RemoteFunctionClient.kt | 101 ++++++++---------- .../io/functions/RemoteFunctionServer.kt | 34 +++--- .../kotlin/hep/dataforge/io/FileBinary.kt | 4 +- .../kotlin/hep/dataforge/io/ioFormatsJVM.kt | 34 ++++++ .../hep/dataforge/io/tcp/EnvelopeClient.kt | 61 +++++++++++ .../hep/dataforge/io/tcp/EnvelopeServer.kt | 70 ++++++++++++ .../dataforge/io/tcp/EnvelopeServerTest.kt | 58 ++++++++++ .../hep/dataforge/workspace/GenericTask.kt | 5 +- .../dataforge/workspace/SimpleWorkspace.kt | 4 +- .../hep/dataforge/workspace/TaskModel.kt | 11 +- .../dataforge/workspace/WorkspacePlugin.kt | 4 +- .../hep/dataforge/workspace/TaskBuilder.kt | 3 +- gradle/wrapper/gradle-wrapper.jar | Bin 55190 -> 55616 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 22 +++- gradlew.bat | 18 +++- 34 files changed, 523 insertions(+), 208 deletions(-) delete mode 100644 dataforge-io/src/commonMain/kotlin/hep/dataforge/io/functions/FunctionSpec.kt delete mode 100644 dataforge-io/src/commonMain/kotlin/hep/dataforge/io/functions/FunctionsPlugin.kt create mode 100644 dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/ioFormatsJVM.kt create mode 100644 dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/tcp/EnvelopeClient.kt create mode 100644 dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/tcp/EnvelopeServer.kt create mode 100644 dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/tcp/EnvelopeServerTest.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 c9268790..232c7cd4 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/AbstractPlugin.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/AbstractPlugin.kt @@ -3,10 +3,13 @@ package hep.dataforge.context import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.Meta import hep.dataforge.names.Name -import hep.dataforge.names.toName +import kotlin.properties.ReadOnlyProperty +import kotlin.reflect.KClass +import kotlin.reflect.KProperty abstract class AbstractPlugin(override val meta: Meta = EmptyMeta) : Plugin { private var _context: Context? = null + private val dependencies = ArrayList>() override val context: Context get() = _context ?: error("Plugin $tag is not attached") @@ -19,9 +22,23 @@ abstract class AbstractPlugin(override val meta: Meta = EmptyMeta) : Plugin { this._context = null } - override fun provideTop(target: String): Map = emptyMap() + final override fun dependsOn(): List> = dependencies - companion object{ - fun Collection.toMap(): Map = associate { it.name.toName() to it } + /** + * Register plugin dependency and return a delegate which provides lazily initialized reference to dependent plugin + */ + protected fun

require(factory: PluginFactory

): ReadOnlyProperty { + dependencies.add(factory) + return PluginDependencyDelegate(factory.type) + } + + override fun provideTop(target: String): Map = emptyMap() +} + +fun Collection.toMap(): Map = associate { it.name to it } + +private class PluginDependencyDelegate

(val type: KClass) : ReadOnlyProperty { + override fun getValue(thisRef: AbstractPlugin, property: KProperty<*>): P { + return thisRef.context.plugins[type] ?: error("Plugin with type $type not found") } } \ 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 80746456..cc79ec44 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt @@ -2,8 +2,8 @@ package hep.dataforge.context import hep.dataforge.meta.* import hep.dataforge.names.Name -import hep.dataforge.names.appendLeft -import hep.dataforge.names.toName +import hep.dataforge.names.asName +import hep.dataforge.names.plus import hep.dataforge.provider.Provider import hep.dataforge.provider.top import hep.dataforge.values.Value @@ -27,7 +27,7 @@ import kotlin.jvm.JvmName * @author Alexander Nozik */ open class Context( - final override val name: String, + final override val name: Name, val parent: Context? = Global ) : Named, MetaRepr, Provider, CoroutineScope { @@ -45,7 +45,7 @@ open class Context( /** * Context logger */ - val logger: KLogger = KotlinLogging.logger(name) + val logger: KLogger = KotlinLogging.logger(name.toString()) /** * A [PluginManager] for current context @@ -64,7 +64,7 @@ open class Context( override fun provideTop(target: String): Map { return when (target) { Value.TYPE -> properties.sequence().toMap() - Plugin.PLUGIN_TARGET -> plugins.sequence(true).associateBy { it.name.toName() } + Plugin.PLUGIN_TARGET -> plugins.sequence(true).associateBy { it.name } else -> emptyMap() } } @@ -118,14 +118,14 @@ fun Context.content(target: String): Map = content(target) @JvmName("typedContent") inline fun Context.content(target: String): Map = plugins.flatMap { plugin -> - plugin.top(target).entries.map { (it.key.appendLeft(plugin.name)) to it.value } + plugin.top(target).entries.map { (plugin.name + it.key) to it.value } }.associate { it } /** * A global root context. Closing [Global] terminates the framework. */ -object Global : Context("GLOBAL", null) { +object Global : Context("GLOBAL".asName(), null) { /** * Closing all contexts * @@ -173,7 +173,7 @@ interface ContextAware { val logger: KLogger get() = if (this is Named) { - KotlinLogging.logger(context.name + "." + (this as Named).name) + KotlinLogging.logger((context.name + this.name).toString()) } else { context.logger } diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/ContextBuilder.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/ContextBuilder.kt index 92840862..58a03554 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/ContextBuilder.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/ContextBuilder.kt @@ -2,11 +2,12 @@ package hep.dataforge.context import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.buildMeta +import hep.dataforge.names.toName /** * A convenience builder for context */ -class ContextBuilder(var name: String = "@anonimous", val parent: Context = Global) { +class ContextBuilder(var name: String = "@anonymous", val parent: Context = Global) { private val plugins = ArrayList() private var meta = MetaBuilder() @@ -31,7 +32,7 @@ class ContextBuilder(var name: String = "@anonimous", val parent: Context = Glob } fun build(): Context { - return Context(name, parent).apply { + return Context(name.toName(), parent).apply { this@ContextBuilder.plugins.forEach { plugins.load(it) } diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Named.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Named.kt index 1ac31702..9e9db17f 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Named.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Named.kt @@ -15,6 +15,10 @@ */ package hep.dataforge.context +import hep.dataforge.names.Name +import hep.dataforge.names.asName +import hep.dataforge.names.isEmpty + /** * Any object that have name * @@ -27,10 +31,9 @@ interface Named { * * @return */ - val name: String + val name: Name companion object { - const val ANONYMOUS = "" /** * Get the name of given object. If object is Named its name is used, @@ -39,11 +42,11 @@ interface Named { * @param obj * @return */ - fun nameOf(obj: Any): String { + fun nameOf(obj: Any): Name { return if (obj is Named) { obj.name } else { - obj.toString() + obj.toString().asName() } } } @@ -54,4 +57,4 @@ interface Named { * @return */ val Named.isAnonymous: Boolean - get() = this.name == Named.ANONYMOUS + get() = this.name.isEmpty() 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 e06bf3f9..d795d881 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt @@ -3,6 +3,8 @@ package hep.dataforge.context import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaRepr import hep.dataforge.meta.buildMeta +import hep.dataforge.names.Name +import hep.dataforge.names.toName import hep.dataforge.provider.Provider /** @@ -37,7 +39,7 @@ interface Plugin : Named, ContextAware, Provider, MetaRepr { * * @return */ - override val name: String get() = tag.name + override val name: Name get() = tag.name.toName() /** * Plugin dependencies which are required to attach this plugin. Plugin @@ -46,7 +48,7 @@ interface Plugin : Named, ContextAware, Provider, MetaRepr { * * @return */ - fun dependsOn(): List> = emptyList() + fun dependsOn(): Collection> /** * Start this plugin and attach registration info to the context. This method diff --git a/dataforge-context/src/commonTest/kotlin/hep/dataforge/context/ContextTest.kt b/dataforge-context/src/commonTest/kotlin/hep/dataforge/context/ContextTest.kt index c77439d6..584e500c 100644 --- a/dataforge-context/src/commonTest/kotlin/hep/dataforge/context/ContextTest.kt +++ b/dataforge-context/src/commonTest/kotlin/hep/dataforge/context/ContextTest.kt @@ -5,7 +5,6 @@ import hep.dataforge.names.appendLeft import hep.dataforge.names.toName import kotlin.test.Test import kotlin.test.assertEquals -import kotlin.test.assertTrue class ContextTest { @@ -26,7 +25,7 @@ class ContextTest { val members = Global.content("test") assertEquals(3, members.count()) members.forEach { - assertTrue{it.key == it.value.appendLeft("test")} + assertEquals(it.key, it.value.appendLeft("test")) } } diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Binary.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Binary.kt index 811986b6..da62113b 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Binary.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Binary.kt @@ -4,6 +4,7 @@ import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.Input import kotlinx.io.core.buildPacket import kotlinx.io.core.readBytes +import kotlin.math.min /** * A source of binary data @@ -30,6 +31,8 @@ interface RandomAccessBinary : Binary { /** * Read at most [size] of bytes starting at [from] offset from the beginning of the binary. * This method could be called multiple times simultaneously. + * + * If size */ fun read(from: UInt, size: UInt = UInt.MAX_VALUE, block: Input.() -> R): R @@ -60,11 +63,12 @@ class ArrayBinary(val array: ByteArray) : RandomAccessBinary { override val size: ULong get() = array.size.toULong() override fun read(from: UInt, size: UInt, block: Input.() -> R): R { - return ByteReadPacket(array, from.toInt(), size.toInt()).block() + val theSize = min(size, array.size.toUInt() - from) + return ByteReadPacket(array, from.toInt(), theSize.toInt()).block() } } -class PacketBinary(val packet: ByteReadPacket): Binary{ +class PacketBinary(val packet: ByteReadPacket) : Binary { override val size: ULong get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates. @@ -87,7 +91,7 @@ fun Binary.readWith(format: IOFormat): T = format.run { * Write this object to a binary * TODO make a lazy binary that does not use intermediate array */ -fun T.writeWith(format: IOFormat): Binary = format.run{ +fun T.writeWith(format: IOFormat): Binary = format.run { val packet = buildPacket { writeThis(this@writeWith) } diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/BinaryMetaFormat.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/BinaryMetaFormat.kt index daf08756..8774387b 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/BinaryMetaFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/BinaryMetaFormat.kt @@ -2,6 +2,8 @@ package hep.dataforge.io import hep.dataforge.descriptors.NodeDescriptor import hep.dataforge.meta.* +import hep.dataforge.names.Name +import hep.dataforge.names.plus import hep.dataforge.values.* import kotlinx.io.core.Input import kotlinx.io.core.Output @@ -9,7 +11,7 @@ import kotlinx.io.core.readText import kotlinx.io.core.writeText object BinaryMetaFormat : MetaFormat { - override val name: String = "bin" + override val name: Name = super.name + "bin" override val key: Short = 0x4249//BI override fun Input.readMeta(descriptor: NodeDescriptor?): Meta { @@ -23,7 +25,7 @@ object BinaryMetaFormat : MetaFormat { writeText(str) } - private fun Output.writeValue(value: Value) { + fun Output.writeValue(value: Value) { if (value.isList()) { writeChar('L') writeInt(value.list.size) @@ -92,7 +94,7 @@ object BinaryMetaFormat : MetaFormat { } @Suppress("UNCHECKED_CAST") - private fun Input.readMetaItem(): MetaItem { + fun Input.readMetaItem(): MetaItem { return when (val keyChar = readByte().toChar()) { 'S' -> MetaItem.ValueItem(StringValue(readString())) 'N' -> MetaItem.ValueItem(Null) diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Envelope.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Envelope.kt index 606a5ffb..36178d98 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Envelope.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Envelope.kt @@ -77,6 +77,10 @@ class EnvelopeBuilder { metaBuilder.apply(block) } + fun meta(meta: Meta) { + metaBuilder.update(meta) + } + var type by metaBuilder.string(key = Envelope.ENVELOPE_TYPE_KEY) var dataType by metaBuilder.string(key = Envelope.ENVELOPE_DATA_TYPE_KEY) var description by metaBuilder.string(key = Envelope.ENVELOPE_DESCRIPTION_KEY) diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeFormat.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeFormat.kt index 24217e14..a0734c04 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeFormat.kt @@ -3,9 +3,12 @@ package hep.dataforge.io import hep.dataforge.context.Named import hep.dataforge.io.EnvelopeFormat.Companion.ENVELOPE_FORMAT_TYPE import hep.dataforge.meta.Meta +import hep.dataforge.names.Name +import hep.dataforge.names.asName import hep.dataforge.provider.Type import kotlinx.io.core.Input import kotlinx.io.core.Output +import kotlin.reflect.KClass /** * A partially read envelope with meta, but without data @@ -15,6 +18,9 @@ data class PartialEnvelope(val meta: Meta, val dataOffset: UInt, val dataSize: U @Type(ENVELOPE_FORMAT_TYPE) interface EnvelopeFormat : IOFormat, Named { + override val name: Name get() = "envelope".asName() + override val type: KClass get() = Envelope::class + fun Input.readPartial(formats: Collection = IOPlugin.defaultMetaFormats): PartialEnvelope fun Input.readEnvelope(formats: Collection = IOPlugin.defaultMetaFormats): Envelope diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOFormat.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOFormat.kt index faa110f7..0ebf9485 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOFormat.kt @@ -1,16 +1,31 @@ package hep.dataforge.io +import hep.dataforge.context.Named import hep.dataforge.io.IOFormat.Companion.TYPE +import hep.dataforge.meta.MetaItem +import hep.dataforge.names.EmptyName +import hep.dataforge.names.Name +import hep.dataforge.names.asName +import hep.dataforge.names.toName import hep.dataforge.provider.Type +import hep.dataforge.values.Value import kotlinx.io.core.* +import kotlinx.serialization.ImplicitReflectionSerializer import kotlinx.serialization.KSerializer import kotlinx.serialization.cbor.Cbor +import kotlinx.serialization.serializer +import kotlin.reflect.KClass /** * And interface for serialization facilities */ @Type(TYPE) -interface IOFormat { +interface IOFormat : Named { + /** + * Explicit type for dynamic type checks + */ + val type: KClass + fun Output.writeThis(obj: T) fun Input.readThis(): T @@ -23,17 +38,47 @@ fun IOFormat.writePacket(obj: T): ByteReadPacket = buildPacket { wr fun IOFormat.writeBytes(obj: T): ByteArray = buildPacket { writeThis(obj) }.readBytes() -object DoubleIOFormat: IOFormat{ +object DoubleIOFormat : IOFormat { + + override val name: Name = "double".asName() + + override val type: KClass get() = Double::class + override fun Output.writeThis(obj: Double) { writeDouble(obj) } + override fun Input.readThis(): Double = readDouble() } +object ValueIOFormat : IOFormat { + + override val name: Name = "value".asName() + + override val type: KClass get() = Value::class + + override fun Output.writeThis(obj: Value) { + BinaryMetaFormat.run { writeValue(obj) } + } + + override fun Input.readThis(): Value { + return (BinaryMetaFormat.run { readMetaItem() } as? MetaItem.ValueItem)?.value + ?: error("The item is not a value") + } +} + /** * Experimental */ -class SerializerIOFormat(val serializer: KSerializer) : IOFormat { +@ImplicitReflectionSerializer +class SerializerIOFormat( + override val type: KClass, + val serializer: KSerializer = type.serializer() +) : IOFormat { + + override val name: Name = type.simpleName?.toName() ?: EmptyName + + override fun Output.writeThis(obj: T) { val bytes = Cbor.plain.dump(serializer, obj) writeFully(bytes) diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOPlugin.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOPlugin.kt index b65e4982..2dbd2b88 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOPlugin.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOPlugin.kt @@ -1,11 +1,11 @@ package hep.dataforge.io -import hep.dataforge.context.AbstractPlugin -import hep.dataforge.context.PluginFactory -import hep.dataforge.context.PluginTag -import hep.dataforge.context.content +import hep.dataforge.context.* import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaItem +import hep.dataforge.meta.string import hep.dataforge.names.Name +import hep.dataforge.names.get import kotlin.reflect.KClass class IOPlugin(meta: Meta) : AbstractPlugin(meta) { @@ -16,20 +16,36 @@ class IOPlugin(meta: Meta) : AbstractPlugin(meta) { } fun metaFormat(key: Short): MetaFormat? = metaFormats.find { it.key == key } - fun metaFormat(name: String): MetaFormat? = metaFormats.find { it.name == name } + fun metaFormat(name: String): MetaFormat? = metaFormats.find { it.name.toString() == name } override fun provideTop(target: String): Map { return when (target) { MetaFormat.META_FORMAT_TYPE -> defaultMetaFormats.toMap() EnvelopeFormat.ENVELOPE_FORMAT_TYPE -> defaultEnvelopeFormats.toMap() + IOFormat.TYPE -> defaultIOFormats.toMap() else -> super.provideTop(target) } } + val ioFormats: Map> by lazy { + context.content>(IOFormat.TYPE) + } + + fun resolveIOFormat(item: MetaItem<*>): IOFormat<*>? { + val key = item.string ?: error("Not a string value!") + return ioFormats[key] + } + companion object : PluginFactory { val defaultMetaFormats: List = listOf(JsonMetaFormat, BinaryMetaFormat) val defaultEnvelopeFormats = listOf(TaggedEnvelopeFormat) + val defaultIOFormats = listOf( + DoubleIOFormat, + ValueIOFormat, + BinaryMetaFormat + ) + override val tag: PluginTag = PluginTag("io", group = PluginTag.DATAFORGE_GROUP) override val type: KClass = IOPlugin::class override fun invoke(meta: Meta): IOPlugin = IOPlugin(meta) diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/JsonMetaFormat.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/JsonMetaFormat.kt index 8b3bb1d6..41946768 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/JsonMetaFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/JsonMetaFormat.kt @@ -6,7 +6,9 @@ import hep.dataforge.descriptors.ValueDescriptor import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaBase import hep.dataforge.meta.MetaItem +import hep.dataforge.names.Name import hep.dataforge.names.NameToken +import hep.dataforge.names.plus import hep.dataforge.names.toName import hep.dataforge.values.* import kotlinx.io.core.Input @@ -21,7 +23,7 @@ import kotlin.collections.set object JsonMetaFormat : MetaFormat { - override val name: String = "json" + override val name: Name = super.name + "json" override val key: Short = 0x4a53//"JS" override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) { diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/MetaFormat.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/MetaFormat.kt index 3185e29e..c208701d 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/MetaFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/MetaFormat.kt @@ -4,17 +4,22 @@ import hep.dataforge.context.Named import hep.dataforge.descriptors.NodeDescriptor import hep.dataforge.io.MetaFormat.Companion.META_FORMAT_TYPE import hep.dataforge.meta.Meta +import hep.dataforge.names.Name +import hep.dataforge.names.asName import hep.dataforge.provider.Type import kotlinx.io.core.* +import kotlin.reflect.KClass /** * A format for meta serialization */ @Type(META_FORMAT_TYPE) interface MetaFormat : IOFormat, Named { - override val name: String + override val name: Name get() = "meta".asName() val key: Short + override val type: KClass get() = Meta::class + override fun Output.writeThis(obj: Meta) { writeMeta(obj, null) } @@ -24,7 +29,7 @@ interface MetaFormat : IOFormat, Named { fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor? = null) fun Input.readMeta(descriptor: NodeDescriptor? = null): Meta - companion object{ + companion object { const val META_FORMAT_TYPE = "metaFormat" } } diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/TaggedEnvelopeFormat.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/TaggedEnvelopeFormat.kt index 291f539d..47e8ab5d 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/TaggedEnvelopeFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/TaggedEnvelopeFormat.kt @@ -1,5 +1,7 @@ package hep.dataforge.io +import hep.dataforge.names.Name +import hep.dataforge.names.plus import kotlinx.io.core.* @@ -10,7 +12,7 @@ object TaggedEnvelopeFormat : EnvelopeFormat { private const val END_SEQUENCE = "~#\r\n" private const val TAG_SIZE = 26u - override val name: String get() = VERSION + override val name: Name = super.name + VERSION private fun Tag.toBytes(): ByteReadPacket = buildPacket(24) { writeText(START_SEQUENCE) diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/functions/FunctionServer.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/functions/FunctionServer.kt index 5fd81711..872a5989 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/functions/FunctionServer.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/functions/FunctionServer.kt @@ -1,7 +1,12 @@ -package hep.dataforge.io +package hep.dataforge.io.functions import hep.dataforge.context.ContextAware -import hep.dataforge.io.functions.FunctionSpec +import hep.dataforge.io.IOFormat +import hep.dataforge.io.IOPlugin +import hep.dataforge.meta.Meta +import hep.dataforge.meta.get +import hep.dataforge.names.asName +import hep.dataforge.names.plus /** @@ -11,24 +16,40 @@ interface FunctionServer : ContextAware { /** * Call a function with given name and descriptor */ - suspend fun call(name: String, spec: FunctionSpec, arg: T): R + suspend fun call(meta: Meta, arg: T): R suspend fun callMany( - name: String, - spec: FunctionSpec, + meta: Meta, arg: List ): List = List(arg.size) { - call(name, spec, arg[it]) + call(meta, arg[it]) } /** * Get a generic suspended function with given name and descriptor */ - fun get( - name: String, - spec: FunctionSpec - ): (suspend (T) -> R) = - { call(name, spec, it) } + fun function( + meta: Meta + ): (suspend (T) -> R) = { call(meta, it) } + + companion object { + const val FUNCTION_NAME_KEY = "function" + val FORMAT_KEY = "format".asName() + val INPUT_FORMAT_KEY = FORMAT_KEY + "input" + val OUTPUT_FORMAT_KEY = FORMAT_KEY + "output" + } +} + +fun IOPlugin.getInputFormat(meta: Meta): IOFormat { + return meta[FunctionServer.INPUT_FORMAT_KEY]?.let { + resolveIOFormat(it) as IOFormat + } ?: error("Input format not resolved") +} + +fun IOPlugin.getOutputFormat(meta: Meta): IOFormat { + return meta[FunctionServer.OUTPUT_FORMAT_KEY]?.let { + resolveIOFormat(it) as IOFormat + } ?: error("Input format not resolved") } diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/functions/FunctionSpec.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/functions/FunctionSpec.kt deleted file mode 100644 index 3ed6f18e..00000000 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/functions/FunctionSpec.kt +++ /dev/null @@ -1,9 +0,0 @@ -package hep.dataforge.io.functions - -import hep.dataforge.io.IOFormat -import hep.dataforge.meta.MetaRepr - -interface FunctionSpec: MetaRepr { - val inputFormat: IOFormat - val outputFormat: IOFormat -} \ No newline at end of file diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/functions/FunctionsPlugin.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/functions/FunctionsPlugin.kt deleted file mode 100644 index 495a4c47..00000000 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/functions/FunctionsPlugin.kt +++ /dev/null @@ -1,58 +0,0 @@ -package hep.dataforge.io.functions - -import hep.dataforge.context.AbstractPlugin -import hep.dataforge.context.PluginFactory -import hep.dataforge.context.PluginTag -import hep.dataforge.io.DoubleIOFormat -import hep.dataforge.io.IOFormat -import hep.dataforge.io.IOPlugin -import hep.dataforge.meta.Meta -import hep.dataforge.meta.buildMeta -import kotlin.reflect.KClass - -class FunctionsPlugin(meta: Meta) : AbstractPlugin(meta) { - override val tag: PluginTag get() = Companion.tag - - override fun dependsOn(): List> = listOf(IOPlugin) - - private val specs: Collection> = listOf( - DoubleToDoubleFunctionSpec - ) - - fun resolve(meta: Meta): FunctionSpec<*, *>? { - return specs.find { it.toMeta() == meta } - } - -// fun resolve(inputType: KClass, outputType: KClass): FunctionSpec { -// -// } - - companion object : PluginFactory { - - override val tag: PluginTag = PluginTag("io.functions", group = PluginTag.DATAFORGE_GROUP) - override val type: KClass = FunctionsPlugin::class - override fun invoke(meta: Meta): FunctionsPlugin = FunctionsPlugin(meta) - } -} - -object DoubleToDoubleFunctionSpec : FunctionSpec { - override val inputFormat: IOFormat get() = DoubleIOFormat - override val outputFormat: IOFormat get() = DoubleIOFormat - - override fun toMeta(): Meta = buildMeta { - "input" to "Double" - "output" to "Double" - } -} - -//suspend inline fun FunctionServer.call(name: String, arg: T): R { -// val plugin = context.plugins.load(FunctionsPlugin) -// val spec = plugin.resolve(T::class, R::class) -// return call(name, spec, arg) -//} -// -//inline operator fun FunctionServer.get(name: String): (suspend (T) -> R) { -// val plugin = context.plugins.load(FunctionsPlugin) -// val spec = plugin.resolve(T::class, R::class) -// return get(name, spec) -//} \ No newline at end of file diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/functions/RemoteFunctionClient.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/functions/RemoteFunctionClient.kt index b285e80d..0ba90282 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/functions/RemoteFunctionClient.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/functions/RemoteFunctionClient.kt @@ -1,56 +1,58 @@ -package hep.dataforge.io +package hep.dataforge.io.functions import hep.dataforge.context.Context import hep.dataforge.context.ContextAware -import hep.dataforge.io.functions.FunctionSpec -import hep.dataforge.io.functions.FunctionsPlugin +import hep.dataforge.io.* +import hep.dataforge.meta.Meta import hep.dataforge.meta.get import hep.dataforge.meta.int -import kotlin.reflect.KClass class RemoteFunctionClient(override val context: Context, val responder: Responder) : FunctionServer, ContextAware { - private fun encodeOne(name: String, spec: FunctionSpec, value: T): Envelope = - Envelope.build { - type = REQUEST_TYPE - meta { - FUNCTION_NAME_KEY to name - FUNCTION_SPEC_KEY to spec.toMeta() + private fun IOPlugin.encodeOne( + meta: Meta, + value: T + ): Envelope = Envelope.build { + meta(meta) + type = REQUEST_TYPE + data { + val inputFormat: IOFormat = getInputFormat(meta) + inputFormat.run { + writeThis(value) } - data { - spec.inputFormat.run { - writeThis(value) + } + } + + private fun IOPlugin.encodeMany( + meta: Meta, + values: List + ): Envelope = Envelope.build { + meta(meta) + type = REQUEST_TYPE + meta { + SIZE_KEY to values.size + } + data { + val inputFormat: IOFormat = getInputFormat(meta) + inputFormat.run { + values.forEach { + writeThis(it) } } } + } - private fun encodeMany(name: String, spec: FunctionSpec, values: List): Envelope = - Envelope.build { - type = REQUEST_TYPE - meta { - FUNCTION_NAME_KEY to name - FUNCTION_SPEC_KEY to spec.toMeta() - SIZE_KEY to values.size - } - data { - spec.inputFormat.run { - values.forEach { - writeThis(it) - } - } - } - } - - private fun decode(spec: FunctionSpec<*, R>, envelope: Envelope): List { + private fun IOPlugin.decode(envelope: Envelope): List { require(envelope.type == RESPONSE_TYPE) { "Unexpected message type: ${envelope.type}" } val size = envelope.meta[SIZE_KEY].int ?: 1 return if (size == 0) { emptyList() } else { + val outputFormat: IOFormat = getOutputFormat(envelope.meta) envelope.data?.read { List(size) { - spec.outputFormat.run { + outputFormat.run { readThis() } } @@ -58,43 +60,32 @@ class RemoteFunctionClient(override val context: Context, val responder: Respond } } + private val plugin by lazy { + context.plugins.load(IOPlugin) + } + override suspend fun call( - name: String, - spec: FunctionSpec, + meta: Meta, arg: T - ): R { - val request = encodeOne(name, spec, arg) + ): R = plugin.run { + val request = encodeOne(meta, arg) val response = responder.respond(request) - return decode(spec, response).first() + return decode(response).first() } override suspend fun callMany( - name: String, - spec: FunctionSpec, + meta: Meta, arg: List - ): List { - val request = encodeMany(name, spec, arg) + ): List = plugin.run { + val request = encodeMany(meta, arg) val response = responder.respond(request) - return decode(spec, response) - } - - private val plugin by lazy { - context.plugins.load(FunctionsPlugin) - } - - fun resolveSpec( - inputType: KClass, - outputType: KClass - ): FunctionSpec { - return plugin.resolve(inputType, outputType) + return decode(response) } companion object { const val REQUEST_TYPE = "function.request" const val RESPONSE_TYPE = "function.response" - const val FUNCTION_NAME_KEY = "function" const val SIZE_KEY = "size" - const val FUNCTION_SPEC_KEY = "spec" } } \ No newline at end of file diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/functions/RemoteFunctionServer.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/functions/RemoteFunctionServer.kt index ba93c0a0..41a2a271 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/functions/RemoteFunctionServer.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/functions/RemoteFunctionServer.kt @@ -2,11 +2,12 @@ package hep.dataforge.io.functions import hep.dataforge.context.Context import hep.dataforge.context.ContextAware -import hep.dataforge.io.* +import hep.dataforge.io.Envelope +import hep.dataforge.io.IOPlugin +import hep.dataforge.io.Responder +import hep.dataforge.io.type import hep.dataforge.meta.get import hep.dataforge.meta.int -import hep.dataforge.meta.node -import hep.dataforge.meta.string class RemoteFunctionServer( override val context: Context, @@ -14,39 +15,38 @@ class RemoteFunctionServer( ) : ContextAware, Responder { private val plugin by lazy { - context.plugins.load(FunctionsPlugin) + context.plugins.load(IOPlugin) } + override suspend fun respond(request: Envelope): Envelope { require(request.type == RemoteFunctionClient.REQUEST_TYPE) { "Unexpected message type: ${request.type}" } - val functionName = request.meta[RemoteFunctionClient.FUNCTION_NAME_KEY].string ?: "" - @Suppress("UNCHECKED_CAST") val spec = request.meta[RemoteFunctionClient.FUNCTION_SPEC_KEY].node?.let { - plugin.resolve(it) as FunctionSpec - } ?: error("Function specification not found") + val inputFormat = plugin.getInputFormat(request.meta) + val outputFormat = plugin.getOutputFormat(request.meta) - val size = request - .meta[RemoteFunctionClient.SIZE_KEY].int ?: 1 + val size = request.meta[RemoteFunctionClient.SIZE_KEY].int ?: 1 val input = request.data?.read { - spec.inputFormat.run { + inputFormat.run { List(size) { readThis() } } } ?: error("Input is empty") - val output = functionServer.callMany(functionName, spec, input) + val output = functionServer.callMany( + request.meta, + input + ) return Envelope.build { - type = RemoteFunctionClient.RESPONSE_TYPE meta { - RemoteFunctionClient.FUNCTION_NAME_KEY to functionName - RemoteFunctionClient.FUNCTION_SPEC_KEY to spec.toMeta() - RemoteFunctionClient.SIZE_KEY to output.size + meta(request.meta) } + type = RemoteFunctionClient.RESPONSE_TYPE data { - spec.outputFormat.run { + outputFormat.run { output.forEach { writeThis(it) } diff --git a/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/FileBinary.kt b/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/FileBinary.kt index 038281d4..b69969ed 100644 --- a/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/FileBinary.kt +++ b/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/FileBinary.kt @@ -6,6 +6,7 @@ import java.nio.channels.FileChannel import java.nio.file.Files import java.nio.file.Path import java.nio.file.StandardOpenOption +import kotlin.math.min @ExperimentalUnsignedTypes class FileBinary(val path: Path, private val offset: UInt = 0u, size: ULong? = null) : RandomAccessBinary { @@ -14,7 +15,8 @@ class FileBinary(val path: Path, private val offset: UInt = 0u, size: ULong? = n override fun read(from: UInt, size: UInt, block: Input.() -> R): R { FileChannel.open(path, StandardOpenOption.READ).use { - val buffer = it.map(FileChannel.MapMode.READ_ONLY, (from + offset).toLong(), size.toLong()) + val theSize: UInt = min(size, Files.size(path).toUInt() - offset) + val buffer = it.map(FileChannel.MapMode.READ_ONLY, (from + offset).toLong(), theSize.toLong()) return ByteReadPacket(buffer).block() } } diff --git a/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/ioFormatsJVM.kt b/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/ioFormatsJVM.kt new file mode 100644 index 00000000..8ba97fc3 --- /dev/null +++ b/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/ioFormatsJVM.kt @@ -0,0 +1,34 @@ +package hep.dataforge.io + +import hep.dataforge.io.functions.FunctionServer +import hep.dataforge.io.functions.FunctionServer.Companion.FUNCTION_NAME_KEY +import hep.dataforge.io.functions.FunctionServer.Companion.INPUT_FORMAT_KEY +import hep.dataforge.io.functions.FunctionServer.Companion.OUTPUT_FORMAT_KEY +import hep.dataforge.meta.Meta +import hep.dataforge.meta.buildMeta +import hep.dataforge.names.Name +import kotlin.reflect.KClass +import kotlin.reflect.full.isSuperclassOf + +inline fun IOPlugin.resolveIOFormat(): IOFormat? { + return ioFormats.values.find { it.type.isSuperclassOf(T::class) } as IOFormat? +} + +fun IOPlugin.resolveIOFormatName(type: KClass<*>): Name { + return ioFormats.entries.find { it.value.type.isSuperclassOf(type) }?.key + ?: error("Can't resolve IOFormat for type $type") +} + +inline fun IOPlugin.generateFunctionMeta(functionName: String): Meta = buildMeta { + FUNCTION_NAME_KEY to functionName + INPUT_FORMAT_KEY to resolveIOFormatName(T::class) + OUTPUT_FORMAT_KEY to resolveIOFormatName(R::class) +} + +inline fun FunctionServer.function( + functionName: String +): (suspend (T) -> R) { + val plugin = context.plugins.get() ?: error("IO plugin not loaded") + val meta = plugin.generateFunctionMeta(functionName) + return function(meta) +} \ No newline at end of file diff --git a/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/tcp/EnvelopeClient.kt b/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/tcp/EnvelopeClient.kt new file mode 100644 index 00000000..61b792fd --- /dev/null +++ b/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/tcp/EnvelopeClient.kt @@ -0,0 +1,61 @@ +package hep.dataforge.io.tcp + +import hep.dataforge.context.Context +import hep.dataforge.context.ContextAware +import hep.dataforge.io.* +import kotlinx.coroutines.asCoroutineDispatcher +import kotlinx.coroutines.withContext +import kotlinx.coroutines.withTimeout +import kotlinx.io.streams.asInput +import kotlinx.io.streams.asOutput +import java.net.Socket +import java.util.concurrent.Executors +import kotlin.time.Duration +import kotlin.time.ExperimentalTime +import kotlin.time.seconds + +@ExperimentalTime +class EnvelopeClient( + override val context: Context, + val host: String, + val port: Int, + val timeout: Duration = 2.seconds, + val format: EnvelopeFormat = TaggedEnvelopeFormat +) : Responder, ContextAware { + + private val dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() + + private var socket: Socket? = null + + private fun getSocket(): Socket { + val socket = socket ?: Socket(host, port).also { this.socket = it } + return if (socket.isConnected) { + socket + } else { + Socket(host, port).also { this.socket = it } + } + } + + suspend fun close() { + respond( + Envelope.build { + type = EnvelopeServer.SHUTDOWN_ENVELOPE_TYPE + } + ) + } + + override suspend fun respond(request: Envelope): Envelope = withContext(dispatcher) { + withTimeout(timeout.toLongMilliseconds()) { + val socket = getSocket() + val input = socket.getInputStream().asInput() + val output = socket.getOutputStream().asOutput() + format.run { + output.writeThis(request) + logger.debug { "Sent request with type ${request.type} to ${socket.remoteSocketAddress}" } + val res = input.readThis() + logger.debug { "Received response with type ${res.type} from ${socket.remoteSocketAddress}" } + return@withTimeout res + } + } + } +} \ No newline at end of file diff --git a/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/tcp/EnvelopeServer.kt b/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/tcp/EnvelopeServer.kt new file mode 100644 index 00000000..9d6c5826 --- /dev/null +++ b/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/tcp/EnvelopeServer.kt @@ -0,0 +1,70 @@ +package hep.dataforge.io.tcp + +import hep.dataforge.context.Context +import hep.dataforge.context.ContextAware +import hep.dataforge.io.EnvelopeFormat +import hep.dataforge.io.Responder +import hep.dataforge.io.TaggedEnvelopeFormat +import hep.dataforge.io.type +import kotlinx.coroutines.* +import kotlinx.io.streams.asInput +import kotlinx.io.streams.asOutput +import java.net.ServerSocket +import java.net.Socket + +class EnvelopeServer( + override val context: Context, + val port: Int, + val responder: Responder, + val scope: CoroutineScope, + val format: EnvelopeFormat = TaggedEnvelopeFormat +) : ContextAware { + + private var job: Job? = null + + fun start() { + if (job == null) { + logger.info { "Starting envelope server on port $port" } + val job = scope.launch(Dispatchers.IO) { + val serverSocket = ServerSocket(port) + //TODO add handshake and format negotiation + while (!serverSocket.isClosed) { + val socket = serverSocket.accept() + logger.info { "Accepted connection from ${socket.remoteSocketAddress}" } + readSocket(socket) + } + } + } + } + + fun stop() { + logger.info { "Stopping envelope server on port $port" } + job?.cancel() + job = null + } + + private fun CoroutineScope.readSocket(socket: Socket) { + val input = socket.getInputStream().asInput() + val output = socket.getOutputStream().asOutput() + format.run { + launch { + while (isActive && socket.isConnected) { + val request = input.readThis() + logger.debug { "Accepted request with type ${request.type} from ${socket.remoteSocketAddress}" } + if (request.type == SHUTDOWN_ENVELOPE_TYPE) { + //Echo shutdown command + logger.info { "Accepted graceful shutdown signal from ${socket.inetAddress}" } + socket.close() + cancel("Graceful connection shutdown requested by client") + } + val response = responder.respond(request) + output.writeThis(response) + } + } + } + } + + companion object { + const val SHUTDOWN_ENVELOPE_TYPE = "@shutdown" + } +} \ No newline at end of file diff --git a/dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/tcp/EnvelopeServerTest.kt b/dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/tcp/EnvelopeServerTest.kt new file mode 100644 index 00000000..a2642564 --- /dev/null +++ b/dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/tcp/EnvelopeServerTest.kt @@ -0,0 +1,58 @@ +package hep.dataforge.io.tcp + +import hep.dataforge.context.Global +import hep.dataforge.io.Envelope +import hep.dataforge.io.Responder +import kotlinx.coroutines.GlobalScope +import org.junit.AfterClass +import org.junit.BeforeClass +import kotlin.time.ExperimentalTime + +object EchoResponder : Responder { + override suspend fun respond(request: Envelope): Envelope = request +} + +@ExperimentalTime +class EnvelopeServerTest { + companion object { + @JvmStatic + val echoEnvelopeServer = EnvelopeServer(Global, 7778, EchoResponder, GlobalScope) + + @BeforeClass + @JvmStatic + fun start() { + echoEnvelopeServer.start() + } + + @AfterClass + @JvmStatic + fun close() { + echoEnvelopeServer.stop() + } + } + + +// @Test +// fun doEchoTest() { +// val client = EnvelopeClient(Global, host = "localhost", port = 7778) +// val request = Envelope.build { +// type = "test.echo" +// meta { +// "test.value" to 22 +// } +// data { +// writeDouble(22.7) +// } +// } +// val response = runBlocking { +// client.respond(request) +// } +// +// assertEquals(request.meta, response.meta) +// assertEquals(request.data, response.data) +// +// runBlocking { +// client.close() +// } +// } +} \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/GenericTask.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/GenericTask.kt index 278098d1..a46862a0 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/GenericTask.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/GenericTask.kt @@ -1,17 +1,18 @@ package hep.dataforge.workspace -import hep.dataforge.data.* +import hep.dataforge.data.DataNode import hep.dataforge.descriptors.NodeDescriptor import hep.dataforge.meta.Meta import hep.dataforge.meta.get import hep.dataforge.meta.node +import hep.dataforge.names.Name import kotlin.reflect.KClass //data class TaskEnv(val workspace: Workspace, val model: TaskModel) class GenericTask( - override val name: String, + override val name: Name, override val type: KClass, override val descriptor: NodeDescriptor, private val modelTransform: TaskModelBuilder.(Meta) -> Unit, diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/SimpleWorkspace.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/SimpleWorkspace.kt index abdb70e3..cb621963 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/SimpleWorkspace.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/SimpleWorkspace.kt @@ -3,10 +3,10 @@ package hep.dataforge.workspace import hep.dataforge.context.Context import hep.dataforge.context.Global import hep.dataforge.context.content +import hep.dataforge.context.toMap import hep.dataforge.data.DataNode import hep.dataforge.meta.Meta import hep.dataforge.names.Name -import hep.dataforge.names.toName /** @@ -20,7 +20,7 @@ class SimpleWorkspace( ) : Workspace { override val tasks: Map> by lazy { - context.content>(Task.TYPE) + tasks.associateBy { it.name.toName() } + context.content>(Task.TYPE) + tasks.toMap() } companion object { diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt index 07fb6638..cda164c7 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt @@ -23,7 +23,7 @@ import hep.dataforge.workspace.TaskModel.Companion.MODEL_TARGET_KEY * @param dependencies a list of direct dependencies for this task */ data class TaskModel( - val name: String, + val name: Name, val meta: Meta, val dependencies: Collection ) : MetaRepr { @@ -66,7 +66,7 @@ annotation class TaskBuildScope * A builder for [TaskModel] */ @TaskBuildScope -class TaskModelBuilder(val name: String, meta: Meta = EmptyMeta) { +class TaskModelBuilder(val name: Name, meta: Meta = EmptyMeta) { /** * Meta for current task. By default uses the whole input meta */ @@ -78,10 +78,13 @@ class TaskModelBuilder(val name: String, meta: Meta = EmptyMeta) { /** * Add dependency for a task defined in a workspace and resolved by */ - fun dependsOn(name: String, meta: Meta = this.meta, placement: Name = EmptyName) { - dependencies.add(WorkspaceTaskDependency(name.toName(), meta, placement)) + fun dependsOn(name: Name, meta: Meta = this.meta, placement: Name = EmptyName) { + dependencies.add(WorkspaceTaskDependency(name, meta, placement)) } + fun dependsOn(name: String, meta: Meta = this.meta, placement: Name = EmptyName) = + dependsOn(name.toName(),meta,placement) + fun dependsOn(task: Task<*>, meta: Meta = this.meta, placement: Name = EmptyName) { dependencies.add(DirectTaskDependency(task, meta, placement)) } diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspacePlugin.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspacePlugin.kt index 2fefd4f8..1ddd291b 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspacePlugin.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspacePlugin.kt @@ -1,8 +1,8 @@ package hep.dataforge.workspace import hep.dataforge.context.AbstractPlugin +import hep.dataforge.context.toMap import hep.dataforge.names.Name -import hep.dataforge.names.toName /** * An abstract plugin with some additional boilerplate to effectively work with workspace context @@ -12,7 +12,7 @@ abstract class WorkspacePlugin : AbstractPlugin() { override fun provideTop(target: String): Map { return when(target){ - Task.TYPE -> tasks.associateBy { it.name.toName() } + Task.TYPE -> tasks.toMap() else -> emptyMap() } } diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt index c35003d3..afe9ddd1 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt @@ -8,6 +8,7 @@ import hep.dataforge.meta.get import hep.dataforge.meta.string import hep.dataforge.names.EmptyName import hep.dataforge.names.Name +import hep.dataforge.names.asName import hep.dataforge.names.toName import kotlin.reflect.KClass @@ -176,7 +177,7 @@ class TaskBuilder(val name: String) { internal fun build(): GenericTask = GenericTask( - name, + name.asName(), Any::class, descriptor ?: NodeDescriptor.empty(), modelTransform diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 87b738cbd051603d91cc39de6cb000dd98fe6b02..5c2d1cf016b3885f6930543d57b744ea8c220a1a 100644 GIT binary patch delta 3320 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