Skip to content

Commit

Permalink
Merge pull request #1 from lightningkite/attemptnull
Browse files Browse the repository at this point in the history
Attemptnull
  • Loading branch information
UnknownJoe796 authored Nov 21, 2023
2 parents 4d640f9 + 643a157 commit e1e737c
Show file tree
Hide file tree
Showing 10 changed files with 357 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package kotlinx.serialization.csv.decode

import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerializationException
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.CompositeDecoder
import kotlinx.serialization.encoding.Decoder

@ExperimentalSerializationApi
public abstract class AbstractDecoderAlt : Decoder, CompositeDecoder {

/**
* Invoked to decode a value when specialized `encode*` method was not overridden.
*/
public open fun decodeValue(): Any = throw SerializationException("${this::class} can't retrieve untyped values")

override fun decodeNotNullMark(): Boolean = true
override fun decodeNull(): Nothing? = null
override fun decodeBoolean(): Boolean = decodeValue() as Boolean
override fun decodeByte(): Byte = decodeValue() as Byte
override fun decodeShort(): Short = decodeValue() as Short
override fun decodeInt(): Int = decodeValue() as Int
override fun decodeLong(): Long = decodeValue() as Long
override fun decodeFloat(): Float = decodeValue() as Float
override fun decodeDouble(): Double = decodeValue() as Double
override fun decodeChar(): Char = decodeValue() as Char
override fun decodeString(): String = decodeValue() as String
override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = decodeValue() as Int

// overwrite by default
public open fun <T : Any?> decodeSerializableValue(
deserializer: DeserializationStrategy<T>,
previousValue: T? = null
): T = decodeSerializableValue(deserializer)

override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder = this

override fun endStructure(descriptor: SerialDescriptor) {
}

final override fun decodeBooleanElement(descriptor: SerialDescriptor, index: Int): Boolean = decodeBoolean()
final override fun decodeByteElement(descriptor: SerialDescriptor, index: Int): Byte = decodeByte()
final override fun decodeShortElement(descriptor: SerialDescriptor, index: Int): Short = decodeShort()
final override fun decodeIntElement(descriptor: SerialDescriptor, index: Int): Int = decodeInt()
final override fun decodeLongElement(descriptor: SerialDescriptor, index: Int): Long = decodeLong()
final override fun decodeFloatElement(descriptor: SerialDescriptor, index: Int): Float = decodeFloat()
final override fun decodeDoubleElement(descriptor: SerialDescriptor, index: Int): Double = decodeDouble()
final override fun decodeCharElement(descriptor: SerialDescriptor, index: Int): Char = decodeChar()
final override fun decodeStringElement(descriptor: SerialDescriptor, index: Int): String = decodeString()

final override fun <T> decodeSerializableElement(
descriptor: SerialDescriptor,
index: Int,
deserializer: DeserializationStrategy<T>,
previousValue: T?
): T = decodeSerializableValue(deserializer, previousValue)

override fun <T : Any> decodeNullableSerializableElement(
descriptor: SerialDescriptor,
index: Int,
deserializer: DeserializationStrategy<T?>,
previousValue: T?
): T? {
val isNullabilitySupported = deserializer.descriptor.isNullable
return if (isNullabilitySupported || decodeNotNullMark()) decodeSerializableValue(deserializer, previousValue) else decodeNull()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,28 @@ internal class ClassCsvDecoder(
private var elementIndex = 0
private var columnIndex = 0

override fun decodeElementIndex(descriptor: SerialDescriptor): Int = when {
reader.isDone -> DECODE_DONE
elementIndex >= descriptor.elementsCount -> DECODE_DONE
classHeaders != null && columnIndex >= classHeaders.size -> DECODE_DONE
override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
// if(descriptor != classHeaders?.descriptor) throw Exception("ClassCsvDecoder with ${descriptor.serialName} cannot decode an index for ${classHeaders?.descriptor?.serialName}")
return when {
reader.isDone -> DECODE_DONE
elementIndex >= descriptor.elementsCount -> DECODE_DONE
classHeaders != null && columnIndex >= classHeaders.size -> DECODE_DONE

classHeaders != null ->
when (val result = classHeaders[columnIndex]) {
UNKNOWN_NAME -> {
ignoreColumn()
decodeElementIndex(descriptor)
classHeaders != null -> {
println("${descriptor.serialName} decoded column index ${columnIndex} to be ${classHeaders[columnIndex]} (${classHeaders[columnIndex]?.takeIf { it >= 0 }?.let { descriptor.getElementName(it)}}) due to column ${columnIndex} in ${classHeaders}")
when (val result = classHeaders[columnIndex]) {
UNKNOWN_NAME -> {
ignoreColumn()
decodeElementIndex(descriptor)
}

null -> UNKNOWN_NAME
else -> result
}
null -> UNKNOWN_NAME
else -> result
}

else -> elementIndex
else -> elementIndex.also { println("${descriptor.serialName} decoded ${it} (${descriptor.getElementName(it)}) because there were no class headers") }
}
}

override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
Expand All @@ -49,7 +55,7 @@ internal class ClassCsvDecoder(
csv,
reader,
this,
classHeaders?.getSubHeaders(decodeElementIndex(descriptor))
classHeaders?.let { it.getSubHeaders(columnIndex) ?: throw IllegalStateException("Could not find sub headers at index $elementIndex; see $classHeaders}") }
)

else ->
Expand Down Expand Up @@ -84,4 +90,9 @@ internal class ClassCsvDecoder(
reader.readColumn()
columnIndex++
}

override fun virtualColumnAdvance() {
columnIndex++
elementIndex++
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package kotlinx.serialization.csv.decode

import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.csv.Csv
import kotlinx.serialization.descriptors.PolymorphicKind
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.StructureKind
import kotlinx.serialization.encoding.CompositeDecoder.Companion.DECODE_DONE

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ package kotlinx.serialization.csv.decode

import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.nullable
import kotlinx.serialization.csv.Csv
import kotlinx.serialization.csv.HeadersNotSupportedForSerialDescriptorException
import kotlinx.serialization.csv.UnknownColumnHeaderException
import kotlinx.serialization.csv.UnsupportedSerialDescriptorException
import kotlinx.serialization.csv.config.CsvConfig
import kotlinx.serialization.descriptors.PolymorphicKind
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.SerialKind
import kotlinx.serialization.descriptors.StructureKind
import kotlinx.serialization.encoding.AbstractDecoder
import kotlinx.serialization.encoding.CompositeDecoder
import kotlinx.serialization.encoding.CompositeDecoder.Companion.UNKNOWN_NAME
import kotlinx.serialization.modules.SerializersModule
Expand All @@ -24,7 +25,7 @@ internal abstract class CsvDecoder(
protected val csv: Csv,
protected val reader: CsvReader,
private val parent: CsvDecoder?
) : AbstractDecoder() {
) : AbstractDecoderAlt() {

override val serializersModule: SerializersModule
get() = csv.serializersModule
Expand Down Expand Up @@ -111,11 +112,18 @@ internal abstract class CsvDecoder(
return null
}

fun skipEmpty(): Nothing? {
val value = reader.readColumn()
println("SKIPPING $value")
require(value == config.nullString) { "Expected '${config.nullString}' but was '$value'." }
return null
}

override fun decodeEnum(enumDescriptor: SerialDescriptor): Int {
return enumDescriptor.getElementIndex(decodeColumn())
}

protected open fun decodeColumn() = reader.readColumn()
protected open fun decodeColumn() = reader.readColumn().also { println("READ COLUMN $it") }

protected fun readHeaders(descriptor: SerialDescriptor) {
if (config.hasHeaderRecord && headers == null) {
Expand All @@ -126,7 +134,7 @@ internal abstract class CsvDecoder(
}

private fun readHeaders(descriptor: SerialDescriptor, prefix: String): Headers {
val headers = Headers()
val headers = Headers(descriptor)
var position = 0
while (!reader.isDone && reader.isFirstRecord) {
val offset = reader.offset
Expand All @@ -144,16 +152,20 @@ internal abstract class CsvDecoder(
val headerIndex = descriptor.getElementIndex(header)
if (headerIndex != UNKNOWN_NAME) {
headers[position] = headerIndex
println("SET $position TO $header")
reader.unmark()
val desc = descriptor.getElementDescriptor(headerIndex)
if(desc.kind == StructureKind.CLASS && desc.isNullable) position--
} else {
val name = header.substringBefore(config.headerSeparator)
val nameIndex = descriptor.getElementIndex(name)
if (nameIndex != UNKNOWN_NAME) {
val childDesc = descriptor.getElementDescriptor(nameIndex)
if (childDesc.kind is StructureKind.CLASS) {
reader.reset()
headers[position] = nameIndex
headers[nameIndex] = readHeaders(childDesc, "$prefix$name.")
if(headers[position] == null)
headers[position] = nameIndex
headers[position] = readHeaders(childDesc, "$prefix$name.")
} else {
reader.unmark()
}
Expand All @@ -168,7 +180,7 @@ internal abstract class CsvDecoder(
}
position++
}
return headers
return headers.also { println(it.toString()) }
}

protected fun readTrailingDelimiter() {
Expand All @@ -177,7 +189,7 @@ internal abstract class CsvDecoder(
}
}

internal class Headers {
internal class Headers(val descriptor: SerialDescriptor) {
private val map = mutableMapOf<Int, Int>()
private val subHeaders = mutableMapOf<Int, Headers>()

Expand All @@ -197,18 +209,71 @@ internal abstract class CsvDecoder(
operator fun set(key: Int, value: Headers) {
subHeaders[key] = value
}

override fun toString(): String {
return "Headers(descriptor=${descriptor.serialName}, map=${map.mapValues { if(it.value in 0 until descriptor.elementsCount) descriptor.getElementName(it.value) else "???" }}, subHeaders=${subHeaders})"
}
}

override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T {
return if (config.deferToFormatWhenVariableColumns != null) {
if (config.deferToFormatWhenVariableColumns != null) {
when (deserializer.descriptor.kind) {
is StructureKind.LIST,
is StructureKind.MAP,
is PolymorphicKind.OPEN -> {
config.deferToFormatWhenVariableColumns!!.decodeFromString(deserializer, decodeColumn())
return config.deferToFormatWhenVariableColumns!!.decodeFromString(deserializer, decodeColumn())
}
else -> super.decodeSerializableValue(deserializer)
else -> {}
}
} else super.decodeSerializableValue(deserializer)
}
if(deserializer.descriptor.isNullable && deserializer.descriptor.kind == StructureKind.CLASS) {
val isPresent = reader.readColumn().toBoolean()
println("READ PRESENT $isPresent")
if(isPresent) {
return deserializer.deserialize(this)
} else {
virtualColumnAdvance()
decodeNulls(deserializer.descriptor, deserializer.descriptor.serialName)
@Suppress("UNCHECKED_CAST")
return null as T
}
} else {
return deserializer.deserialize(this)
}
}

protected open fun virtualColumnAdvance() {}

private fun decodeNulls(serializer: SerialDescriptor, name: String) {
if(serializer.kind == StructureKind.CLASS) {
for(index in (0 until serializer.elementsCount)) {
val sub = serializer.getElementDescriptor(index)
if(sub.isNullable) {
println("Skipping present ${serializer.getElementName(index)}")
skipEmpty()
}
decodeNulls(sub, serializer.getElementName(index))
}
println("Skipping ${name} end")
} else {
println("Skipping $name")
skipEmpty()
}
}

override fun <T : Any> decodeNullableSerializableValue(deserializer: DeserializationStrategy<T?>): T? {
val isNullabilitySupported = deserializer.descriptor.isNullable
return if (isNullabilitySupported || decodeNotNullMark()) decodeSerializableValue(deserializer) else decodeSerializableValue((deserializer as KSerializer<T>).nullable)
}

override fun <T : Any> decodeNullableSerializableElement(
descriptor: SerialDescriptor,
index: Int,
deserializer: DeserializationStrategy<T?>,
previousValue: T?
): T? {
val isNullabilitySupported = deserializer.descriptor.isNullable
return if (isNullabilitySupported) decodeSerializableValue(deserializer, previousValue) else decodeSerializableValue((deserializer as KSerializer<T>).nullable, previousValue)
}
}

Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package kotlinx.serialization.csv.decode

import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.csv.Csv
import kotlinx.serialization.descriptors.PolymorphicKind
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.StructureKind
import kotlinx.serialization.encoding.CompositeDecoder
Expand Down Expand Up @@ -70,4 +72,19 @@ internal class RecordListCsvDecoder(
reader.reset()
}
}

override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T {
println("DECODING")
if (config.deferToFormatWhenVariableColumns != null) {
when (deserializer.descriptor.kind) {
is StructureKind.LIST,
is StructureKind.MAP,
is PolymorphicKind.OPEN -> {
return config.deferToFormatWhenVariableColumns!!.decodeFromString(deserializer, decodeColumn())
}
else -> {}
}
}
return deserializer.deserialize(this)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package kotlinx.serialization.csv.decode

import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.nullable
import kotlinx.serialization.csv.Csv
import kotlinx.serialization.descriptors.PolymorphicKind
import kotlinx.serialization.descriptors.SerialDescriptor
Expand Down Expand Up @@ -29,6 +31,7 @@ internal class RootCsvDecoder(
}

override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
println("BEGIN STRUCT ${descriptor.kind}")
return when (descriptor.kind) {
StructureKind.LIST ->
// Top level list is treated as list of multiple records
Expand All @@ -55,13 +58,25 @@ internal class RootCsvDecoder(
}

override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T {
return if (config.deferToFormatWhenVariableColumns != null) {
if (config.deferToFormatWhenVariableColumns != null) {
when (deserializer.descriptor.kind) {
is PolymorphicKind.OPEN -> {
config.deferToFormatWhenVariableColumns!!.decodeFromString(deserializer, decodeColumn())
}
else -> deserializer.deserialize(this)
else -> {}
}
} else deserializer.deserialize(this)
}
if(deserializer.descriptor.isNullable && deserializer.descriptor.kind == StructureKind.CLASS) {
val isPresent = decodeBoolean()
println("Decoded present boolean ${isPresent}")
if(isPresent) {
return deserializer.deserialize(this)
} else {
@Suppress("UNCHECKED_CAST")
return null as T
}
} else {
return deserializer.deserialize(this)
}
}
}
Loading

0 comments on commit e1e737c

Please sign in to comment.