xref: /aosp_15_r20/external/kotlinx.serialization/core/commonMain/src/kotlinx/serialization/internal/Enums.kt (revision 57b5a4a64c534cf7f27ac9427ceab07f3d8ed3d8)
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.internal
6 
7 import kotlinx.serialization.*
8 import kotlinx.serialization.descriptors.*
9 import kotlinx.serialization.encoding.*
10 
11 /*
12  * Descriptor used for explicitly serializable enums by the plugin.
13  * Designed to be consistent with `EnumSerializer.descriptor` and weird plugin usage.
14  */
15 @Suppress("unused") // Used by the plugin
16 @PublishedApi
17 @OptIn(ExperimentalSerializationApi::class)
18 internal class EnumDescriptor(
19     name: String,
20     elementsCount: Int
21 ) : PluginGeneratedSerialDescriptor(name, elementsCount = elementsCount) {
22 
23     override val kind: SerialKind = SerialKind.ENUM
24     private val elementDescriptors by lazy {
25         Array(elementsCount) { buildSerialDescriptor(name + "." + getElementName(it), StructureKind.OBJECT) }
26     }
27 
28     override fun getElementDescriptor(index: Int): SerialDescriptor = elementDescriptors.getChecked(index)
29 
30     override fun equals(other: Any?): Boolean {
31         if (this === other) return true
32         if (other == null) return false
33         if (other !is SerialDescriptor) return false
34         if (other.kind !== SerialKind.ENUM) return false
35         if (serialName != other.serialName) return false
36         if (cachedSerialNames() != other.cachedSerialNames()) return false
37         return true
38     }
39 
40     override fun toString(): String {
41         return elementNames.joinToString(", ", "$serialName(", ")")
42     }
43 
44     override fun hashCode(): Int {
45         var result = serialName.hashCode()
46         val elementsHashCode = elementNames.elementsHashCodeBy { it }
47         result = 31 * result + elementsHashCode
48         return result
49     }
50 }
51 
52 @OptIn(ExperimentalSerializationApi::class)
53 @InternalSerializationApi
createSimpleEnumSerializernull54 internal fun <T : Enum<T>> createSimpleEnumSerializer(serialName: String, values: Array<T>): KSerializer<T> {
55     return EnumSerializer(serialName, values)
56 }
57 
58 /**
59  * The function has a bug (#2121) and should not be used by new (1.8.20+) plugins. It is preserved for backward compatibility with previously compiled enum classes.
60  */
61 @OptIn(ExperimentalSerializationApi::class)
62 @InternalSerializationApi
createMarkedEnumSerializernull63 internal fun <T : Enum<T>> createMarkedEnumSerializer(
64     serialName: String,
65     values: Array<T>,
66     names: Array<String?>,
67     annotations: Array<Array<Annotation>?>
68 ): KSerializer<T> {
69     val descriptor = EnumDescriptor(serialName, values.size)
70     values.forEachIndexed { i, v ->
71         val elementName = names.getOrNull(i) ?: v.name
72         descriptor.addElement(elementName)
73         annotations.getOrNull(i)?.forEach {
74             descriptor.pushAnnotation(it)
75         }
76     }
77 
78     return EnumSerializer(serialName, values, descriptor)
79 }
80 
81 @OptIn(ExperimentalSerializationApi::class)
82 @InternalSerializationApi
createAnnotatedEnumSerializernull83 internal fun <T : Enum<T>> createAnnotatedEnumSerializer(
84     serialName: String,
85     values: Array<T>,
86     names: Array<String?>,
87     entryAnnotations: Array<Array<Annotation>?>,
88     classAnnotations: Array<Annotation>?
89 ): KSerializer<T> {
90     val descriptor = EnumDescriptor(serialName, values.size)
91     classAnnotations?.forEach {
92         descriptor.pushClassAnnotation(it)
93     }
94     values.forEachIndexed { i, v ->
95         val elementName = names.getOrNull(i) ?: v.name
96         descriptor.addElement(elementName)
97         entryAnnotations.getOrNull(i)?.forEach {
98             descriptor.pushAnnotation(it)
99         }
100     }
101 
102     return EnumSerializer(serialName, values, descriptor)
103 }
104 
105 @PublishedApi
106 @OptIn(ExperimentalSerializationApi::class)
107 internal class EnumSerializer<T : Enum<T>>(
108     serialName: String,
109     private val values: Array<T>
110 ) : KSerializer<T> {
111     private var overriddenDescriptor: SerialDescriptor? = null
112 
113     internal constructor(serialName: String, values: Array<T>, descriptor: SerialDescriptor) : this(serialName, values) {
114         overriddenDescriptor = descriptor
115     }
116 
<lambda>null117     override val descriptor: SerialDescriptor by lazy {
118         overriddenDescriptor ?: createUnmarkedDescriptor(serialName)
119     }
120 
createUnmarkedDescriptornull121     private fun createUnmarkedDescriptor(serialName: String): SerialDescriptor {
122         val d = EnumDescriptor(serialName, values.size)
123         values.forEach { d.addElement(it.name) }
124         return d
125     }
126 
serializenull127     override fun serialize(encoder: Encoder, value: T) {
128         val index = values.indexOf(value)
129         if (index == -1) {
130             throw SerializationException(
131                 "$value is not a valid enum ${descriptor.serialName}, " +
132                         "must be one of ${values.contentToString()}"
133             )
134         }
135         encoder.encodeEnum(descriptor, index)
136     }
137 
deserializenull138     override fun deserialize(decoder: Decoder): T {
139         val index = decoder.decodeEnum(descriptor)
140         if (index !in values.indices) {
141             throw SerializationException(
142                 "$index is not among valid ${descriptor.serialName} enum values, " +
143                         "values size is ${values.size}"
144             )
145         }
146         return values[index]
147     }
148 
toStringnull149     override fun toString(): String = "kotlinx.serialization.internal.EnumSerializer<${descriptor.serialName}>"
150 }
151