Fix build with new IO

This commit is contained in:
Alexander Nozik 2019-12-01 20:53:32 +03:00
parent ab47d7723e
commit c789dabdae
13 changed files with 86 additions and 100 deletions

View File

@ -5,8 +5,10 @@ import hep.dataforge.io.*
import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.DFExperimental
import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.EmptyMeta
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import kotlinx.io.Input import kotlinx.io.*
import kotlinx.io.Output import kotlinx.io.text.readUtf8Line
import kotlinx.io.text.writeRawString
import kotlinx.io.text.writeUtf8String
import kotlinx.serialization.toUtf8Bytes import kotlinx.serialization.toUtf8Bytes
@DFExperimental @DFExperimental
@ -27,14 +29,17 @@ class FrontMatterEnvelopeFormat(
metaTypeRegex.matchEntire(line)?.groupValues?.first() metaTypeRegex.matchEntire(line)?.groupValues?.first()
?.let { io.metaFormat(it) } ?: YamlMetaFormat ?.let { io.metaFormat(it) } ?: YamlMetaFormat
val metaBlock = buildPacket { val meta = buildBytes {
do { do {
line = readUtf8Line() ?: error("Input does not contain closing front matter separator") line = readUtf8Line()
appendln(line) writeUtf8String(line + "\r\n")
offset += line.toUtf8Bytes().size.toUInt() offset += line.toUtf8Bytes().size.toUInt()
} while (!line.startsWith(SEPARATOR)) } while (!line.startsWith(SEPARATOR))
}.read {
readMetaFormat.run {
readMeta()
}
} }
val meta = readMetaFormat.fromBytes(metaBlock)
return PartialEnvelope(meta, offset, null) return PartialEnvelope(meta, offset, null)
} }
@ -48,23 +53,29 @@ class FrontMatterEnvelopeFormat(
metaTypeRegex.matchEntire(line)?.groupValues?.first() metaTypeRegex.matchEntire(line)?.groupValues?.first()
?.let { io.metaFormat(it) } ?: YamlMetaFormat ?.let { io.metaFormat(it) } ?: YamlMetaFormat
val metaBlock = buildPacket { val meta = buildBytes {
do { do {
appendln(readUtf8Line() ?: error("Input does not contain closing front matter separator")) writeUtf8String(readUtf8Line() + "\r\n")
} while (!line.startsWith(SEPARATOR)) } while (!line.startsWith(SEPARATOR))
}.read {
readMetaFormat.run {
readMeta()
}
} }
val meta = readMetaFormat.fromBytes(metaBlock) val bytes = readRemaining()
val bytes = readBytes()
val data = bytes.asBinary() val data = bytes.asBinary()
return SimpleEnvelope(meta, data) return SimpleEnvelope(meta, data)
} }
override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta) { override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta) {
val metaFormat = metaFormatFactory(formatMeta, io.context) val metaFormat = metaFormatFactory(formatMeta, io.context)
writeText("$SEPARATOR\r\n") writeRawString("$SEPARATOR\r\n")
metaFormat.run { writeObject(envelope.meta) } metaFormat.run { writeObject(envelope.meta) }
writeText("$SEPARATOR\r\n") writeRawString("$SEPARATOR\r\n")
envelope.data?.read { copyTo(this@writeEnvelope) } //Printing data
envelope.data?.let { data ->
writeBinary(data)
}
} }
companion object : EnvelopeFormatFactory { companion object : EnvelopeFormatFactory {
@ -77,7 +88,7 @@ class FrontMatterEnvelopeFormat(
} }
override fun peekFormat(io: IOPlugin, input: Input): EnvelopeFormat? { override fun peekFormat(io: IOPlugin, input: Input): EnvelopeFormat? {
val line = input.readUtf8Line(3, 30) val line = input.readUtf8Line()
return if (line != null && line.startsWith("---")) { return if (line != null && line.startsWith("---")) {
invoke() invoke()
} else { } else {

View File

@ -11,13 +11,13 @@ import hep.dataforge.meta.toMeta
import kotlinx.io.Input import kotlinx.io.Input
import kotlinx.io.Output import kotlinx.io.Output
import kotlinx.io.readUByte import kotlinx.io.readUByte
import kotlinx.io.writeText import kotlinx.io.text.writeUtf8String
import org.yaml.snakeyaml.Yaml import org.yaml.snakeyaml.Yaml
import java.io.InputStream import java.io.InputStream
private class InputAsStream(val input: Input) : InputStream() { private class InputAsStream(val input: Input) : InputStream() {
override fun read(): Int { override fun read(): Int {
if (input.endOfInput) return -1 if (input.eof()) return -1
return input.readUByte().toInt() return input.readUByte().toInt()
} }
@ -34,7 +34,7 @@ class YamlMetaFormat(val meta: Meta) : MetaFormat {
override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) { override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) {
val string = yaml.dump(meta.toMap(descriptor)) val string = yaml.dump(meta.toMap(descriptor))
writeText(string) writeUtf8String(string)
} }
override fun Input.readMeta(descriptor: NodeDescriptor?): Meta { override fun Input.readMeta(descriptor: NodeDescriptor?): Meta {

View File

@ -6,7 +6,7 @@ import hep.dataforge.meta.Meta
import hep.dataforge.meta.buildMeta import hep.dataforge.meta.buildMeta
import hep.dataforge.meta.get import hep.dataforge.meta.get
import hep.dataforge.meta.seal import hep.dataforge.meta.seal
import org.junit.jupiter.api.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals

View File

@ -3,7 +3,8 @@ package hep.dataforge.output.html
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.output.Output import hep.dataforge.output.Output
import hep.dataforge.output.TextRenderer import hep.dataforge.output.Renderer
import hep.dataforge.output.TextFormat
import hep.dataforge.output.html.HtmlBuilder.Companion.HTML_CONVERTER_TYPE import hep.dataforge.output.html.HtmlBuilder.Companion.HTML_CONVERTER_TYPE
import hep.dataforge.provider.Type import hep.dataforge.provider.Type
import hep.dataforge.provider.top import hep.dataforge.provider.top
@ -14,11 +15,11 @@ import kotlinx.html.p
import kotlin.reflect.KClass import kotlin.reflect.KClass
class HtmlOutput<T : Any>(override val context: Context, private val consumer: TagConsumer<*>) : Output<T> { class HtmlRenderer<T : Any>(override val context: Context, private val consumer: TagConsumer<*>) : Renderer<T> {
private val cache = HashMap<KClass<*>, HtmlBuilder<*>>() private val cache = HashMap<KClass<*>, HtmlBuilder<*>>()
/** /**
* Find the first [TextRenderer] matching the given object type. * Find the first [TextFormat] matching the given object type.
*/ */
override fun render(obj: T, meta: Meta) { override fun render(obj: T, meta: Meta) {
@ -47,7 +48,7 @@ class HtmlOutput<T : Any>(override val context: Context, private val consumer: T
} }
/** /**
* A text or binary renderer based on [Output] * A text or binary renderer based on [Renderer]
*/ */
@Type(HTML_CONVERTER_TYPE) @Type(HTML_CONVERTER_TYPE)
interface HtmlBuilder<T : Any> { interface HtmlBuilder<T : Any> {

View File

@ -1,9 +1,6 @@
package hep.dataforge.output package hep.dataforge.output
import hep.dataforge.context.AbstractPlugin import hep.dataforge.context.*
import hep.dataforge.context.Context
import hep.dataforge.context.PluginFactory
import hep.dataforge.context.PluginTag
import hep.dataforge.context.PluginTag.Companion.DATAFORGE_GROUP import hep.dataforge.context.PluginTag.Companion.DATAFORGE_GROUP
import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.EmptyMeta
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
@ -22,14 +19,14 @@ interface OutputManager {
* Get an output specialized for given type, name and stage. * Get an output specialized for given type, name and stage.
* @param stage represents the node or directory for the output. Empty means root node. * @param stage represents the node or directory for the output. Empty means root node.
* @param name represents the name inside the node. * @param name represents the name inside the node.
* @param meta configuration for [Output] (not for rendered object) * @param meta configuration for [Renderer] (not for rendered object)
*/ */
operator fun <T : Any> get( operator fun <T : Any> get(
type: KClass<out T>, type: KClass<out T>,
name: Name, name: Name,
stage: Name = EmptyName, stage: Name = EmptyName,
meta: Meta = EmptyMeta meta: Meta = EmptyMeta
): Output<T> ): Renderer<T>
} }
/** /**
@ -44,7 +41,7 @@ inline operator fun <reified T : Any> OutputManager.get(
name: Name, name: Name,
stage: Name = EmptyName, stage: Name = EmptyName,
meta: Meta = EmptyMeta meta: Meta = EmptyMeta
): Output<T> { ): Renderer<T> {
return get(T::class, name, stage, meta) return get(T::class, name, stage, meta)
} }
@ -56,14 +53,21 @@ fun OutputManager.render(obj: Any, name: Name, stage: Name = EmptyName, meta: Me
/** /**
* System console output. * System console output.
* The [ConsoleOutput] is used when no other [OutputManager] is provided. * The [CONSOLE_RENDERER] is used when no other [OutputManager] is provided.
*/ */
expect val ConsoleOutput: Output<Any> val CONSOLE_RENDERER: Renderer<Any> = object : Renderer<Any> {
override fun render(obj: Any, meta: Meta) {
println(obj)
}
override val context: Context get() = Global
}
class ConsoleOutputManager : AbstractPlugin(), OutputManager { class ConsoleOutputManager : AbstractPlugin(), OutputManager {
override val tag: PluginTag get() = ConsoleOutputManager.tag override val tag: PluginTag get() = ConsoleOutputManager.tag
override fun <T : Any> get(type: KClass<out T>, name: Name, stage: Name, meta: Meta): Output<T> = ConsoleOutput override fun <T : Any> get(type: KClass<out T>, name: Name, stage: Name, meta: Meta): Renderer<T> = CONSOLE_RENDERER
companion object : PluginFactory<ConsoleOutputManager> { companion object : PluginFactory<ConsoleOutputManager> {
override val tag = PluginTag("output.console", group = DATAFORGE_GROUP) override val tag = PluginTag("output.console", group = DATAFORGE_GROUP)

View File

@ -7,11 +7,11 @@ import hep.dataforge.meta.Meta
/** /**
* A generic way to render any object in the output. * 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] * An object could be rendered either in append or overlay mode. The mode is decided by the [Renderer]
* based on its configuration and provided meta * based on its configuration and provided meta
* *
*/ */
interface Output<in T : Any> : ContextAware { interface Renderer<in T : Any> : ContextAware {
/** /**
* Render specific object with configuration. * Render specific object with configuration.
* *

View File

@ -2,48 +2,50 @@ package hep.dataforge.output
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.output.TextRenderer.Companion.TEXT_RENDERER_TYPE import hep.dataforge.output.TextFormat.Companion.TEXT_RENDERER_TYPE
import hep.dataforge.provider.Type import hep.dataforge.provider.Type
import hep.dataforge.provider.top import hep.dataforge.provider.top
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.io.Output
import kotlinx.io.text.writeUtf8String
import kotlin.reflect.KClass import kotlin.reflect.KClass
class TextOutput(override val context: Context, private val output: kotlinx.io.Output) : Output<Any> { class TextRenderer(override val context: Context, private val output: Output) : Renderer<Any> {
private val cache = HashMap<KClass<*>, TextRenderer>() private val cache = HashMap<KClass<*>, TextFormat>()
/** /**
* Find the first [TextRenderer] matching the given object type. * Find the first [TextFormat] matching the given object type.
*/ */
override fun render(obj: Any, meta: Meta) { override fun render(obj: Any, meta: Meta) {
val renderer: TextRenderer = if (obj is CharSequence) { val format: TextFormat = if (obj is CharSequence) {
DefaultTextRenderer DefaultTextFormat
} else { } else {
val value = cache[obj::class] val value = cache[obj::class]
if (value == null) { if (value == null) {
val answer = val answer =
context.top<TextRenderer>(TEXT_RENDERER_TYPE).values.firstOrNull { it.type.isInstance(obj) } context.top<TextFormat>(TEXT_RENDERER_TYPE).values.firstOrNull { it.type.isInstance(obj) }
if (answer != null) { if (answer != null) {
cache[obj::class] = answer cache[obj::class] = answer
answer answer
} else { } else {
DefaultTextRenderer DefaultTextFormat
} }
} else { } else {
value value
} }
} }
context.launch(Dispatchers.Output) { context.launch(Dispatchers.Output) {
renderer.run { output.render(obj) } format.run { output.render(obj) }
} }
} }
} }
/** /**
* A text or binary renderer based on [kotlinx.io.Output] * A text or binary renderer based on [Output]
*/ */
@Type(TEXT_RENDERER_TYPE) @Type(TEXT_RENDERER_TYPE)
interface TextRenderer { interface TextFormat {
/** /**
* The priority of this renderer compared to other renderers * The priority of this renderer compared to other renderers
*/ */
@ -53,19 +55,18 @@ interface TextRenderer {
*/ */
val type: KClass<*> val type: KClass<*>
suspend fun kotlinx.io.Output.render(obj: Any) suspend fun Output.render(obj: Any)
companion object { companion object {
const val TEXT_RENDERER_TYPE = "dataforge.textRenderer" const val TEXT_RENDERER_TYPE = "dataforge.textRenderer"
} }
} }
object DefaultTextRenderer : TextRenderer { object DefaultTextFormat : TextFormat {
override val priority: Int = Int.MAX_VALUE override val priority: Int = Int.MAX_VALUE
override val type: KClass<*> = Any::class override val type: KClass<*> = Any::class
override suspend fun kotlinx.io.Output.render(obj: Any) { override suspend fun Output.render(obj: Any) {
append(obj.toString()) writeUtf8String(obj.toString() + "\n")
append('\n')
} }
} }

View File

@ -1,22 +0,0 @@
package hep.dataforge.output
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<Any> = object : Output<Any> {
override fun render(obj: Any, meta: Meta) {
println(obj)
}
override val context: Context get() = Global
}
actual val Dispatchers.Output: CoroutineDispatcher get() = Dispatchers.Default

View File

@ -0,0 +1,7 @@
package hep.dataforge.output
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
actual val Dispatchers.Output: CoroutineDispatcher get() = Dispatchers.Default

View File

@ -1,13 +0,0 @@
package hep.dataforge.output
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<Any> = TextOutput(Global, System.out.asOutput())
actual val Dispatchers.Output get() = Dispatchers.IO

View File

@ -0,0 +1,5 @@
package hep.dataforge.output
import kotlinx.coroutines.Dispatchers
actual val Dispatchers.Output get() = Dispatchers.IO

View File

@ -7,8 +7,7 @@ import hep.dataforge.io.IOFormat
import hep.dataforge.io.SimpleEnvelope import hep.dataforge.io.SimpleEnvelope
import hep.dataforge.io.readWith import hep.dataforge.io.readWith
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import kotlinx.io.Input import kotlinx.io.ArrayBinary
import kotlinx.io.buildPacket
import kotlin.reflect.KClass import kotlin.reflect.KClass
/** /**
@ -22,15 +21,8 @@ suspend fun <T : Any> Data<T>.toEnvelope(format: IOFormat<T>): Envelope {
val obj = coroutineScope { val obj = coroutineScope {
await(this) await(this)
} }
val binary = object : Binary { val binary = ArrayBinary.write {
override fun <R> read(block: Input.() -> R): R { format.run { writeObject(obj) }
//TODO optimize away additional copy by creating inputs that reads directly from output
val packet = buildPacket {
format.run { writeObject(obj) }
}
return packet.block()
}
} }
return SimpleEnvelope(meta, binary) return SimpleEnvelope(meta, binary)
} }

View File

@ -8,8 +8,8 @@ import hep.dataforge.meta.DFExperimental
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.io.Input import kotlinx.io.Input
import kotlinx.io.Output import kotlinx.io.Output
import kotlinx.io.readText import kotlinx.io.text.readUtf8String
import kotlinx.io.writeText import kotlinx.io.text.writeUtf8String
import java.nio.file.Files import java.nio.file.Files
import kotlin.test.Ignore import kotlin.test.Ignore
import kotlin.test.Test import kotlin.test.Test
@ -30,11 +30,11 @@ class FileDataTest {
object StringIOFormat : IOFormat<String> { object StringIOFormat : IOFormat<String> {
override fun Output.writeObject(obj: String) { override fun Output.writeObject(obj: String) {
writeText(obj) writeUtf8String(obj)
} }
override fun Input.readObject(): String { override fun Input.readObject(): String {
return readText() return readUtf8String()
} }
} }