summaryrefslogtreecommitdiff
path: root/runtime/commonMain/src/kotlinx/serialization/modules/SerialModuleImpl.kt
blob: 151f0031c5ed6d2f99b93ea29ec80f39ce841bd9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/*
 * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
 */

@file:Suppress("UNCHECKED_CAST")

package kotlinx.serialization.modules

import kotlinx.serialization.KSerializer
import kotlinx.serialization.isInstanceOf
import kotlin.collections.set
import kotlin.reflect.KClass

private typealias SerializersMap = MutableMap<KClass<*>, KSerializer<*>>

/**
 * A default implementation of [SerialModule]
 * which uses hash maps to store serializers associated with KClasses.
 */
internal class SerialModuleImpl : SerialModule {

    private val classMap: SerializersMap = hashMapOf()

    private val polyMap: MutableMap<KClass<*>, SerializersMap> = hashMapOf()
    private val inverseClassNameMap: MutableMap<KClass<*>, MutableMap<String, KSerializer<*>>> = hashMapOf()

    internal fun <T : Any> registerSerializer(
        forClass: KClass<T>,
        serializer: KSerializer<T>,
        allowOverwrite: Boolean = false
    ) {
        if (!allowOverwrite && forClass in classMap) throw SerializerAlreadyRegisteredException(forClass)
        classMap[forClass] = serializer
    }

    internal fun <Base : Any, Sub : Base> registerPolymorphicSerializer(
        baseClass: KClass<Base>,
        concreteClass: KClass<Sub>,
        concreteSerializer: KSerializer<Sub>,
        allowOverwrite: Boolean = false
    ) {
        val name = concreteSerializer.descriptor.name
        polyMap.getOrPut(baseClass, ::hashMapOf).let { baseClassMap ->
            if (!allowOverwrite && concreteClass in baseClassMap) throw SerializerAlreadyRegisteredException(
                baseClass,
                concreteClass
            )
            baseClassMap[concreteClass] = concreteSerializer
        }
        inverseClassNameMap.getOrPut(baseClass, ::hashMapOf)[name] = concreteSerializer
    }

    override fun <T : Any> getPolymorphic(baseClass: KClass<T>, value: T): KSerializer<out T>? {
        if (!value.isInstanceOf(baseClass)) return null
        (if (baseClass == Any::class) StandardSubtypesOfAny.getSubclassSerializer(value) else null)?.let { return it as KSerializer<out T> }
        return polyMap[baseClass]?.get(value::class) as? KSerializer<out T>
    }

    override fun <T : Any> getPolymorphic(baseClass: KClass<T>, serializedClassName: String): KSerializer<out T>? {
        (if (baseClass == Any::class) StandardSubtypesOfAny.getDefaultDeserializer(
            serializedClassName
        ) else null)?.let { return it as KSerializer<out T> }
        return inverseClassNameMap[baseClass]?.get(serializedClassName) as? KSerializer<out T>
    }

    override fun <T: Any> getContextual(kclass: KClass<T>): KSerializer<T>? = classMap[kclass] as? KSerializer<T>

    override fun dumpTo(collector: SerialModuleCollector) {
        classMap.forEach { (kclass, serial) ->
            collector.contextual(
                kclass as KClass<Any>,
                serial as KSerializer<Any>
            )
        }

        polyMap.forEach { (baseClass, classMap) ->
            classMap.forEach { (actualClass, serializer) ->
                collector.polymorphic(
                    baseClass as KClass<Any>,
                    actualClass as KClass<Any>,
                    serializer as KSerializer<Any>
                )
            }
        }
    }
}