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