All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.maryk.yaml.MapItemsReader.kt Maven / Gradle / Ivy

package maryk.yaml

import maryk.json.JsonToken
import maryk.json.JsonToken.EndObject
import maryk.json.JsonToken.FieldName
import maryk.json.JsonToken.NullValue
import maryk.json.JsonToken.SimpleStartObject
import maryk.json.JsonToken.StartArray
import maryk.json.JsonToken.StartComplexFieldName
import maryk.json.JsonToken.StartObject
import maryk.json.JsonToken.Value
import maryk.json.MapType
import maryk.json.TokenType
import maryk.json.ValueType.Null
import maryk.lib.extensions.isLineBreak
import maryk.lib.extensions.isSpacing
import maryk.yaml.MapState.KEY_FOUND
import maryk.yaml.MapState.NEW_PAIR
import maryk.yaml.MapState.VALUE_FOUND

private enum class MapState {
    NEW_PAIR, KEY_FOUND, VALUE_FOUND
}

/** Reader for Map Items */
internal class MapItemsReader(
    yamlReader: YamlReaderImpl,
    parentReader: P,
    private var indentToAdd: Int = 0
) : YamlCharWithParentAndIndentReader

(yamlReader, parentReader), IsYamlCharWithIndentsReader { private var state: MapState = KEY_FOUND // Because is always created after finding key private var isStarted = false private val fieldNames = mutableListOf() private var stateWasSetOnRead: Boolean = false override fun readUntilToken(extraIndent: Int, tag: TokenType?): JsonToken { this.stateWasSetOnRead = false return if (!this.isStarted) { this.isStarted = true return tag?.let { val mapType = it as? MapType ?: throw InvalidYamlContent("Can only use map tags on maps") StartObject(mapType) } ?: SimpleStartObject } else { if (this.lastChar.isLineBreak()) { val currentIndentCount = this.yamlReader.skipEmptyLinesAndCommentsAndCountIndents() val readerIndentCount = this.indentCount() if (currentIndentCount < readerIndentCount) { this.endIndentLevel(currentIndentCount, tag, null) } else { if (currentIndentCount == readerIndentCount && this.state == VALUE_FOUND) { this.state = NEW_PAIR } this.selectReaderAndRead(true, tag, currentIndentCount - readerIndentCount, this::jsonTokenCreator) .also { return checkAndSetState(currentIndentCount == readerIndentCount, tag, it) } } } else { this.selectReaderAndRead(false, tag, extraIndent, this::jsonTokenCreator).also(::setState) } } } internal fun setState(it: JsonToken) { if (this.stateWasSetOnRead) { return } if (it is FieldName || it == StartComplexFieldName) { if (this.state == KEY_FOUND) { throw InvalidYamlContent("Already found mapping key. No other : allowed") } this.state = KEY_FOUND } // Set value found on any return when key was found else if (this.state == KEY_FOUND) { this.state = VALUE_FOUND } this.stateWasSetOnRead = true } override fun foundMap(tag: TokenType?, startedAtIndent: Int): JsonToken? { if (startedAtIndent > 0) { return MapItemsReader( this.yamlReader, this, indentToAdd = startedAtIndent ).let { this.currentReader = it it.readUntilToken(0, tag) } } return null } override fun checkAndCreateFieldName(fieldName: String?, isPlainStringReader: Boolean): FieldName { return checkAndCreateFieldName(this.fieldNames, fieldName, isPlainStringReader) } override fun continueIndentLevel(extraIndent: Int, tag: TokenType?): JsonToken { this.stateWasSetOnRead = false this.currentReader = this val token = this.selectReaderAndRead(true, tag, extraIndent, this::jsonTokenCreator) return checkAndSetState(extraIndent == 0, tag, token) } private fun jsonTokenCreator( value: String?, isPlainStringReader: Boolean, tag: TokenType?, extraIndent: Int ): JsonToken { while (this.lastChar.isSpacing() && !this.yamlReader.hasException) { read() } if (this.lastChar == ':' && !this.yamlReader.hasUnclaimedIndenting()) { read() if (this.lastChar.isWhitespace()) { if (!this.lastChar.isLineBreak()) { read() } return this.foundMap(tag, extraIndent)?.let { this.yamlReader.pushToken( this.checkAndCreateFieldName(value, isPlainStringReader) ) it } ?: this.checkAndCreateFieldName(value, isPlainStringReader) } else { throw InvalidYamlContent("There should be whitespace after :") } } else if (this.state == KEY_FOUND) { return createYamlValueToken(value, tag, isPlainStringReader) } else { this.yamlReader.pushToken(createYamlValueToken(null, Null, false)) return this.checkAndCreateFieldName(value, isPlainStringReader) } } override fun indentCount(): Int = this.indentToAdd + this.parentReader.indentCount() override fun endIndentLevel( indentCount: Int, tag: TokenType?, tokenToReturn: (() -> JsonToken)? ): JsonToken { if (indentCount == this.indentCount()) { // this reader should handle the read this.currentReader = this return if (tokenToReturn != null) { this.yamlReader.setUnclaimedIndenting(indentCount) tokenToReturn() } else { this.yamlReader.setUnclaimedIndenting(null) this.continueIndentLevel(0, tag) } } // Return tag placeholders if tag was encountered if (tag != null) { this.yamlReader.setUnclaimedIndenting(indentCount) tokenToReturn?.let { this.yamlReader.pushTokenAsFirst(it()) } return this.createTokensFittingTag(tag) } if (this.state == KEY_FOUND && tokenToReturn == null) { // Return null value if no value was returned yet this.state = VALUE_FOUND this.yamlReader.setUnclaimedIndenting(indentCount) return NullValue } this.currentReader = this.parentReader return if (indentToAdd > 0) { this.yamlReader.setUnclaimedIndenting(indentCount) tokenToReturn?.let { this.yamlReader.pushToken(EndObject) return it() } EndObject } else { val returnFunction = tokenToReturn?.let { this.yamlReader.pushToken(EndObject) it } ?: { EndObject } this.parentReader.endIndentLevel(indentCount, tag, returnFunction) } } private fun checkAndSetState( onZeroExtraIndent: Boolean, tag: TokenType?, it: JsonToken ): JsonToken { if (this.stateWasSetOnRead) { return it } if (onZeroExtraIndent && this.state == KEY_FOUND && it !is StartArray && it !is Value<*> ) { this.state = VALUE_FOUND this.setState(it) this.stateWasSetOnRead = true return if (this.state == KEY_FOUND) { this.yamlReader.pushTokenAsFirst(it) this.createTokensFittingTag(tag) } else { it } } else { this.state = NEW_PAIR this.setState(it) return it } } override fun handleReaderInterrupt(): JsonToken { if (this.state == KEY_FOUND) { // Return null value if no value was returned yet this.state = VALUE_FOUND return NullValue } this.currentReader = this.parentReader return EndObject } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy