1 /*
<lambda>null2  * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.serialization.descriptors
6 
7 import kotlinx.serialization.*
8 import kotlinx.serialization.internal.*
9 import kotlinx.serialization.modules.*
10 import kotlin.jvm.*
11 import kotlin.reflect.*
12 
13 
14 /**
15  * Retrieves [KClass] associated with serializer and its descriptor, if it was captured.
16  *
17  * For schema introspection purposes, [capturedKClass] can be used in [SerializersModule] as a key
18  * to retrieve registered descriptor at runtime.
19  * This property is intended to be used on [SerialKind.CONTEXTUAL] and [PolymorphicKind.OPEN] kinds of descriptors,
20  * where actual serializer used for a property can be determined only at runtime.
21  * Serializers which represent contextual serialization and open polymorphism (namely, [ContextualSerializer] and
22  * [PolymorphicSerializer]) capture statically known KClass in a descriptor and can expose it via this property.
23  *
24  * This property is `null` for descriptors that are not of [SerialKind.CONTEXTUAL] or [PolymorphicKind.OPEN] kinds.
25  * It _may_ be `null` for descriptors of these kinds, if captured class information is unavailable for various reasons.
26  * It means that schema introspection should be performed in an application-specific manner.
27  *
28  * ### Example
29  * Imagine we need to find all distinct properties names, which may occur in output after serializing a given class
30  * with respect to [`@Contextual`][Contextual] annotation and all possible inheritors when the class is
31  * serialized polymorphically.
32  * Then we can write following function:
33  * ```
34  * fun allDistinctNames(descriptor: SerialDescriptor, module: SerialModule) = when (descriptor.kind) {
35  *   is PolymorphicKind.OPEN -> module.getPolymorphicDescriptors(descriptor)
36  *     .map { it.elementNames() }.flatten().toSet()
37  *   is SerialKind.CONTEXTUAL -> module.getContextualDescriptor(descriptor)
38  *     ?.elementNames().orEmpty().toSet()
39  *   else -> descriptor.elementNames().toSet()
40  * }
41  * ```
42  * @see SerializersModule.getContextualDescriptor
43  * @see SerializersModule.getPolymorphicDescriptors
44  */
45 @ExperimentalSerializationApi
46 public val SerialDescriptor.capturedKClass: KClass<*>?
47     get() = when (this) {
48         is ContextDescriptor -> kClass
49         is SerialDescriptorForNullable -> original.capturedKClass
50         else -> null
51     }
52 
53 /**
54  * Looks up a descriptor of serializer registered for contextual serialization in [this],
55  * using [SerialDescriptor.capturedKClass] as a key.
56  *
57  * @see SerializersModuleBuilder.contextual
58  */
59 @ExperimentalSerializationApi
getContextualDescriptornull60 public fun SerializersModule.getContextualDescriptor(descriptor: SerialDescriptor): SerialDescriptor? =
61     descriptor.capturedKClass?.let { klass -> getContextual(klass)?.descriptor }
62 
63 /**
64  * Retrieves a collection of descriptors which serializers are registered for polymorphic serialization in [this]
65  * with base class equal to [descriptor]'s [SerialDescriptor.capturedKClass].
66  * This method does not retrieve serializers registered with [PolymorphicModuleBuilder.defaultDeserializer]
67  * or [PolymorphicModuleBuilder.defaultSerializer].
68  *
69  * @see SerializersModule.getPolymorphic
70  * @see SerializersModuleBuilder.polymorphic
71  */
72 @ExperimentalSerializationApi
getPolymorphicDescriptorsnull73 public fun SerializersModule.getPolymorphicDescriptors(descriptor: SerialDescriptor): List<SerialDescriptor> {
74     val kClass = descriptor.capturedKClass ?: return emptyList()
75     // SerializersModule is sealed class with the only implementation
76     return (this as SerialModuleImpl).polyBase2Serializers[kClass]?.values.orEmpty().map { it.descriptor }
77 }
78 
79 /**
80  * Wraps [this] in [ContextDescriptor].
81  */
withContextnull82 internal fun SerialDescriptor.withContext(context: KClass<*>): SerialDescriptor =
83     ContextDescriptor(this, context)
84 
85 /**
86  * Descriptor that captures [kClass] and allows retrieving additional runtime information,
87  * if proper [SerializersModule] is provided.
88  */
89 @OptIn(ExperimentalSerializationApi::class)
90 private class ContextDescriptor(
91     private val original: SerialDescriptor,
92     @JvmField val kClass: KClass<*>
93 ) : SerialDescriptor by original {
94     override val serialName = "${original.serialName}<${kClass.simpleName}>"
95 
96     override fun equals(other: Any?): Boolean {
97         val another = other as? ContextDescriptor ?: return false
98         return original == another.original && another.kClass == this.kClass
99     }
100 
101     override fun hashCode(): Int {
102         var result = kClass.hashCode()
103         result = 31 * result + serialName.hashCode()
104         return result
105     }
106 
107     override fun toString(): String {
108         return "ContextDescriptor(kClass: $kClass, original: $original)"
109     }
110 }
111