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

bson.AbstractBSONCodec.kt Maven / Gradle / Ivy

There is a newer version: 0.9.27
Show newest version
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")
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy