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

org.bson.codecs.kotlin.ArrayCodec.kt Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2008-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.bson.codecs.kotlin

import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import kotlin.reflect.KClass
import org.bson.BsonReader
import org.bson.BsonType
import org.bson.BsonWriter
import org.bson.codecs.Codec
import org.bson.codecs.DecoderContext
import org.bson.codecs.EncoderContext
import org.bson.codecs.configuration.CodecRegistry

@Suppress("UNCHECKED_CAST")
internal data class ArrayCodec(private val kClass: KClass, private val codec: Codec) : Codec {

    companion object {
        internal fun  create(
            kClass: KClass,
            typeArguments: List,
            codecRegistry: CodecRegistry
        ): Codec {
            assert(kClass.javaObjectType.isArray) { "$kClass must be an array type" }
            val (valueClass, nestedTypes) =
                if (typeArguments.isEmpty()) {
                    Pair(kClass.java.componentType.kotlin.javaObjectType as Class, emptyList())
                } else {
                    // Unroll the actual class and any type arguments
                    when (val pType = typeArguments[0]) {
                        is Class<*> -> Pair(pType as Class, emptyList())
                        is ParameterizedType -> Pair(pType.rawType as Class, pType.actualTypeArguments.toList())
                        else -> Pair(Object::class.java as Class, emptyList())
                    }
                }
            val codec =
                if (nestedTypes.isEmpty()) codecRegistry.get(valueClass) else codecRegistry.get(valueClass, nestedTypes)
            return ArrayCodec(kClass, codec)
        }
    }

    private val isPrimitiveArray = kClass.java.componentType != kClass.java.componentType.kotlin.javaObjectType

    override fun encode(writer: BsonWriter, arrayValue: R, encoderContext: EncoderContext) {
        writer.writeStartArray()

        boxed(arrayValue).forEach {
            if (it == null) writer.writeNull() else encoderContext.encodeWithChildContext(codec, writer, it)
        }

        writer.writeEndArray()
    }

    override fun getEncoderClass(): Class = kClass.java

    override fun decode(reader: BsonReader, decoderContext: DecoderContext): R {
        reader.readStartArray()
        val data = ArrayList()
        while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) {
            if (reader.currentBsonType == BsonType.NULL) {
                reader.readNull()
                data.add(null)
            } else {
                data.add(decoderContext.decodeWithChildContext(codec, reader))
            }
        }
        reader.readEndArray()
        return unboxed(data)
    }

    fun boxed(arrayValue: R): Iterable {
        val boxedValue =
            if (!isPrimitiveArray) {
                (arrayValue as Array).asIterable()
            } else if (arrayValue is BooleanArray) {
                arrayValue.asIterable()
            } else if (arrayValue is ByteArray) {
                arrayValue.asIterable()
            } else if (arrayValue is CharArray) {
                arrayValue.asIterable()
            } else if (arrayValue is DoubleArray) {
                arrayValue.asIterable()
            } else if (arrayValue is FloatArray) {
                arrayValue.asIterable()
            } else if (arrayValue is IntArray) {
                arrayValue.asIterable()
            } else if (arrayValue is LongArray) {
                arrayValue.asIterable()
            } else if (arrayValue is ShortArray) {
                arrayValue.asIterable()
            } else {
                throw IllegalArgumentException("Unsupported array type ${arrayValue.javaClass}")
            }
        return boxedValue as Iterable
    }

    private fun unboxed(data: ArrayList): R {
        return when (kClass) {
            BooleanArray::class -> (data as ArrayList).toBooleanArray() as R
            ByteArray::class -> (data as ArrayList).toByteArray() as R
            CharArray::class -> (data as ArrayList).toCharArray() as R
            DoubleArray::class -> (data as ArrayList).toDoubleArray() as R
            FloatArray::class -> (data as ArrayList).toFloatArray() as R
            IntArray::class -> (data as ArrayList).toIntArray() as R
            LongArray::class -> (data as ArrayList).toLongArray() as R
            ShortArray::class -> (data as ArrayList).toShortArray() as R
            else -> data.toArray(arrayOfNulls(data.size)) as R
        }
    }

    private fun arrayOfNulls(size: Int): Array {
        return java.lang.reflect.Array.newInstance(codec.encoderClass, size) as Array
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy