bson.AbstractBSONCodec.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of baku Show documentation
Show all versions of baku Show documentation
helps you focus your REST API back-end on the business logic
package com.github.fluidsonic.baku
import org.bson.BsonReader
import org.bson.BsonType
import org.bson.BsonWriter
import org.bson.codecs.DecoderContext
import org.bson.codecs.Encoder
import org.bson.codecs.EncoderContext
import org.bson.codecs.configuration.CodecRegistry
import java.lang.reflect.ParameterizedType
import kotlin.reflect.KClass
abstract class AbstractBSONCodec(
private val additionalProviders: List> = emptyList(),
valueClass: Class? = null,
private val includesSubclasses: Boolean = false
) : BSONCodec {
private var context: Context? = null
private var rootRegistry: CodecRegistry? = null
private val valueClass = valueClass ?: defaultValueClass(this::class)
abstract fun BsonReader.decode(context: Context): Value
abstract fun BsonWriter.encode(value: Value, context: Context)
final override fun codecForClass(valueClass: KClass): BSONCodec? {
super.codecForClass(valueClass)?.let { return it }
@Suppress("UNCHECKED_CAST")
if (includesSubclasses && this.valueClass.isAssignableFrom(valueClass.java))
return this as BSONCodec
for (provider in additionalProviders)
provider.codecForClass(valueClass)?.let { return it }
return null
}
internal fun configure(context: Context, rootRegistry: CodecRegistry) {
this.context = context
this.rootRegistry = rootRegistry
}
final override fun decode(reader: BsonReader, decoderContext: DecoderContext) =
reader.decode(context = requireContext())
final override fun encode(writer: BsonWriter, value: Value, encoderContext: EncoderContext) =
writer.encode(value = value, context = requireContext())
final override fun getEncoderClass() =
valueClass
fun BsonReader.readValueOfType(name: String, `class`: KClass): Value {
readName(name)
return readValueOfType(`class`)
}
fun BsonReader.readValueOfType(`class`: KClass) =
requireRootRegistry()[`class`.java].decode(this, decoderContext)!!
fun BsonReader.readValueOfTypeOrNull(name: String, `class`: KClass): Value? {
readName(name)
return readValueOfTypeOrNull(`class`)
}
fun BsonReader.readValueOfTypeOrNull(`class`: KClass): Value? {
expectValue("readValueOfTypeOrNull")
if (currentBsonType == BsonType.NULL) {
skipValue()
return null
}
return readValueOfType(`class`)
}
fun BsonReader.readValuesOfType(`class`: KClass): List =
readValuesOfType(`class`, container = mutableListOf())
fun BsonReader.readValuesOfType(`class`: KClass, container: Container): Container where Value : Any, Container : MutableCollection {
readArrayWithValues {
container.add(readValueOfType(`class`))
}
return container
}
fun BsonReader.readValuesOfTypeOrNull(`class`: KClass): List? {
expectValue("readValuesOfTypeOrNull")
if (currentBsonType == BsonType.NULL) {
skipValue()
return null
}
return readValuesOfType(`class`)
}
fun BsonReader.readValuesOfTypeOrNull(`class`: KClass, container: Container): Container? where Value : Any, Container : MutableCollection {
expectValue("readValuesOfTypeOrNull")
if (currentBsonType == BsonType.NULL) {
skipValue()
return null
}
return readValuesOfType(`class`, container = container)
}
fun BsonWriter.write(name: String, value: Any) {
writeName(name)
writeValue(value)
}
@JvmName("writeOrSkip")
fun BsonWriter.write(name: String, valueOrSkip: Any?) {
valueOrSkip ?: return
write(name = name, value = valueOrSkip)
}
fun BsonWriter.write(name: String, values: Iterable) {
writeName(name)
writeValues(values)
}
@JvmName("writeOrSkip")
fun BsonWriter.write(name: String, valuesOrSkip: Iterable?) {
valuesOrSkip ?: return
write(name = name, values = valuesOrSkip)
}
fun BsonWriter.writeValue(value: Any) {
@Suppress("UNCHECKED_CAST")
(requireRootRegistry()[value::class.java] as Encoder).encode(this, value, encoderContext)
}
fun BsonWriter.writeValues(values: Iterable) {
writeArray {
for (value in values) {
writeValue(value)
}
}
}
private fun requireContext() =
context ?: error("AbstractBSONCodec must be used by the CodecRegistry provided by Baku")
private fun requireRootRegistry() =
rootRegistry ?: error("AbstractBSONCodec must be used by the CodecRegistry provided by Baku")
companion object {
private val decoderContext = DecoderContext.builder().build()!!
private val encoderContext = EncoderContext.builder().build()!!
}
}
@Suppress("UNCHECKED_CAST")
private fun defaultValueClass(codecClass: KClass>): Class {
val typeArgument = (codecClass.java.genericSuperclass as ParameterizedType).actualTypeArguments.first()
return when (typeArgument) {
is Class<*> -> typeArgument as Class
is ParameterizedType -> typeArgument.rawType as Class
else -> error("unsupported type: $typeArgument")
}
}