1 /*
<lambda>null2  * Copyright 2017-2021 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.builtins.*
9 import kotlinx.serialization.encoding.*
10 import kotlinx.serialization.internal.*
11 import kotlin.reflect.*
12 
13 /**
14  * Builder for [SerialDescriptor].
15  * The resulting descriptor will be uniquely identified by the given [serialName], [typeParameters] and
16  * elements structure described in [builderAction] function.
17  *
18  * Example:
19  * ```
20  * // Class with custom serializer and custom serial descriptor
21  * class Data(
22  *     val intField: Int, // This field is ignored by custom serializer
23  *     val longField: Long, // This field is written as long, but in serialized form is named as "_longField"
24  *     val stringList: List<String> // This field is written as regular list of strings
25  *     val nullableInt: Int?
26  * )
27  * // Descriptor for such class:
28  * buildClassSerialDescriptor("my.package.Data") {
29  *     // intField is deliberately ignored by serializer -- not present in the descriptor as well
30  *     element<Long>("_longField") // longField is named as _longField
31  *     element("stringField", listSerialDescriptor<String>()) // or ListSerializer(String.serializer()).descriptor
32  *     element("nullableInt", serialDescriptor<Int>().nullable)
33  * }
34  * ```
35  *
36  * Example for generic classes:
37  * ```
38  * import kotlinx.serialization.builtins.*
39  *
40  * @Serializable(CustomSerializer::class)
41  * class BoxedList<T>(val list: List<T>)
42  *
43  * class CustomSerializer<T>(tSerializer: KSerializer<T>): KSerializer<BoxedList<T>> {
44  *   // here we use tSerializer.descriptor because it represents T
45  *   override val descriptor = buildClassSerialDescriptor("pkg.BoxedList", tSerializer.descriptor) {
46  *     // here we have to wrap it with List first, because property has type List<T>
47  *     element("list", ListSerializer(tSerializer).descriptor) // or listSerialDescriptor(tSerializer.descriptor)
48  *   }
49  * }
50  * ```
51  */
52 @Suppress("FunctionName")
53 @OptIn(ExperimentalSerializationApi::class)
54 public fun buildClassSerialDescriptor(
55     serialName: String,
56     vararg typeParameters: SerialDescriptor,
57     builderAction: ClassSerialDescriptorBuilder.() -> Unit = {}
58 ): SerialDescriptor {
<lambda>null59     require(serialName.isNotBlank()) { "Blank serial names are prohibited" }
60     val sdBuilder = ClassSerialDescriptorBuilder(serialName)
61     sdBuilder.builderAction()
62     return SerialDescriptorImpl(
63         serialName,
64         StructureKind.CLASS,
65         sdBuilder.elementNames.size,
66         typeParameters.toList(),
67         sdBuilder
68     )
69 }
70 
71 /**
72  * Factory to create a trivial primitive descriptors.
73  * Primitive descriptors should be used when the serialized form of the data has a primitive form, for example:
74  * ```
75  * object LongAsStringSerializer : KSerializer<Long> {
76  *     override val descriptor: SerialDescriptor =
77  *         PrimitiveSerialDescriptor("kotlinx.serialization.LongAsStringSerializer", PrimitiveKind.STRING)
78  *
79  *     override fun serialize(encoder: Encoder, value: Long) {
80  *         encoder.encodeString(value.toString())
81  *     }
82  *
83  *     override fun deserialize(decoder: Decoder): Long {
84  *         return decoder.decodeString().toLong()
85  *     }
86  * }
87  * ```
88  */
PrimitiveSerialDescriptornull89 public fun PrimitiveSerialDescriptor(serialName: String, kind: PrimitiveKind): SerialDescriptor {
90     require(serialName.isNotBlank()) { "Blank serial names are prohibited" }
91     return PrimitiveDescriptorSafe(serialName, kind)
92 }
93 
94 /**
95  * Factory to create a new descriptor that is identical to [original] except that the name is equal to [serialName].
96  * Should be used when you want to serialize a type as another non-primitive type.
97  * Don't use this if you want to serialize a type as a primitive value, use [PrimitiveSerialDescriptor] instead.
98  *
99  * Example:
100  * ```
101  * @Serializable(CustomSerializer::class)
102  * class CustomType(val a: Int, val b: Int, val c: Int)
103  *
104  * class CustomSerializer: KSerializer<CustomType> {
105  *     override val descriptor = SerialDescriptor("CustomType", IntArraySerializer().descriptor)
106  *
107  *     override fun serialize(encoder: Encoder, value: CustomType) {
108  *         encoder.encodeSerializableValue(IntArraySerializer(), intArrayOf(value.a, value.b, value.c))
109  *     }
110  *
111  *     override fun deserialize(decoder: Decoder): CustomType {
112  *         val array = decoder.decodeSerializableValue(IntArraySerializer())
113  *         return CustomType(array[0], array[1], array[2])
114  *     }
115  * }
116  * ```
117  */
118 @ExperimentalSerializationApi
SerialDescriptornull119 public fun SerialDescriptor(serialName: String, original: SerialDescriptor): SerialDescriptor {
120     require(serialName.isNotBlank()) { "Blank serial names are prohibited" }
121     require(original.kind !is PrimitiveKind) { "For primitive descriptors please use 'PrimitiveSerialDescriptor' instead" }
122     require(serialName != original.serialName) { "The name of the wrapped descriptor ($serialName) cannot be the same as the name of the original descriptor (${original.serialName})" }
123 
124     return WrappedSerialDescriptor(serialName, original)
125 }
126 
127 @OptIn(ExperimentalSerializationApi::class)
128 internal class WrappedSerialDescriptor(override val serialName: String, original: SerialDescriptor) : SerialDescriptor by original
129 
130 /**
131  * An unsafe alternative to [buildClassSerialDescriptor] that supports an arbitrary [SerialKind].
132  * This function is left public only for migration of pre-release users and is not intended to be used
133  * as generally-safe and stable mechanism. Beware that it can produce inconsistent or non spec-compliant instances.
134  *
135  * If you end up using this builder, please file an issue with your use-case in kotlinx.serialization issue tracker.
136  */
137 @InternalSerializationApi
138 @OptIn(ExperimentalSerializationApi::class)
buildSerialDescriptornull139 public fun buildSerialDescriptor(
140     serialName: String,
141     kind: SerialKind,
142     vararg typeParameters: SerialDescriptor,
143     builder: ClassSerialDescriptorBuilder.() -> Unit = {}
144 ): SerialDescriptor {
<lambda>null145     require(serialName.isNotBlank()) { "Blank serial names are prohibited" }
<lambda>null146     require(kind != StructureKind.CLASS) { "For StructureKind.CLASS please use 'buildClassSerialDescriptor' instead" }
147     val sdBuilder = ClassSerialDescriptorBuilder(serialName)
148     sdBuilder.builder()
149     return SerialDescriptorImpl(serialName, kind, sdBuilder.elementNames.size, typeParameters.toList(), sdBuilder)
150 }
151 
152 
153 /**
154  * Retrieves descriptor of type [T] using reified [serializer] function.
155  */
serialDescriptornull156 public inline fun <reified T> serialDescriptor(): SerialDescriptor = serializer<T>().descriptor
157 
158 /**
159  * Retrieves descriptor of type associated with the given [KType][type]
160  */
161 public fun serialDescriptor(type: KType): SerialDescriptor = serializer(type).descriptor
162 
163 /**
164  * Creates a descriptor for the type `List<T>` where `T` is the type associated with [elementDescriptor].
165  */
166 @ExperimentalSerializationApi
167 public fun listSerialDescriptor(elementDescriptor: SerialDescriptor): SerialDescriptor {
168     return ArrayListClassDesc(elementDescriptor)
169 }
170 
171 /**
172  * Creates a descriptor for the type `List<T>`.
173  */
174 @ExperimentalSerializationApi
listSerialDescriptornull175 public inline fun <reified T> listSerialDescriptor(): SerialDescriptor {
176     return listSerialDescriptor(serializer<T>().descriptor)
177 }
178 
179 /**
180  * Creates a descriptor for the type `Map<K, V>` where `K` and `V` are types
181  * associated with [keyDescriptor] and [valueDescriptor] respectively.
182  */
183 @ExperimentalSerializationApi
mapSerialDescriptornull184 public fun mapSerialDescriptor(
185     keyDescriptor: SerialDescriptor,
186     valueDescriptor: SerialDescriptor
187 ): SerialDescriptor {
188     return HashMapClassDesc(keyDescriptor, valueDescriptor)
189 }
190 
191 /**
192  * Creates a descriptor for the type `Map<K, V>`.
193  */
194 @ExperimentalSerializationApi
mapSerialDescriptornull195 public inline fun <reified K, reified V> mapSerialDescriptor(): SerialDescriptor {
196     return mapSerialDescriptor(serializer<K>().descriptor, serializer<V>().descriptor)
197 }
198 
199 /**
200  * Creates a descriptor for the type `Set<T>` where `T` is the type associated with [elementDescriptor].
201  */
202 @ExperimentalSerializationApi
setSerialDescriptornull203 public fun setSerialDescriptor(elementDescriptor: SerialDescriptor): SerialDescriptor {
204     return HashSetClassDesc(elementDescriptor)
205 }
206 
207 /**
208  * Creates a descriptor for the type `Set<T>`.
209  */
210 @ExperimentalSerializationApi
setSerialDescriptornull211 public inline fun <reified T> setSerialDescriptor(): SerialDescriptor {
212     return setSerialDescriptor(serializer<T>().descriptor)
213 }
214 
215 /**
216  * Returns new serial descriptor for the same type with [isNullable][SerialDescriptor.isNullable]
217  * property set to `true`.
218  */
219 @OptIn(ExperimentalSerializationApi::class)
220 public val SerialDescriptor.nullable: SerialDescriptor
221     get() {
222         if (this.isNullable) return this
223         return SerialDescriptorForNullable(this)
224     }
225 
226 /**
227  * Builder for [SerialDescriptor] for user-defined serializers.
228  *
229  * Both explicit builder functions and implicit (using reified type-parameters) are present and are equivalent.
230  * For example, `element<Int?>("nullableIntField")` is indistinguishable from
231  * `element("nullableIntField", IntSerializer.descriptor.nullable)` and
232  * from `element("nullableIntField", descriptor<Int?>)`.
233  *
234  * Please refer to [SerialDescriptor] builder function for a complete example.
235  */
236 public class ClassSerialDescriptorBuilder internal constructor(
237     public val serialName: String
238 ) {
239 
240     /**
241      * Indicates that serializer associated with the current serial descriptor
242      * support nullable types, meaning that it should declare nullable type
243      * in its [KSerializer] type parameter and handle nulls during encoding and decoding.
244      */
245     @ExperimentalSerializationApi
246     @Deprecated("isNullable inside buildSerialDescriptor is deprecated. Please use SerialDescriptor.nullable extension on a builder result.", level = DeprecationLevel.ERROR)
247     public var isNullable: Boolean = false
248 
249     /**
250      * [Serial][SerialInfo] annotations on a target type.
251      */
252     @ExperimentalSerializationApi
253     public var annotations: List<Annotation> = emptyList()
254 
255     internal val elementNames: MutableList<String> = ArrayList()
256     private val uniqueNames: MutableSet<String> = HashSet()
257     internal val elementDescriptors: MutableList<SerialDescriptor> = ArrayList()
258     internal val elementAnnotations: MutableList<List<Annotation>> = ArrayList()
259     internal val elementOptionality: MutableList<Boolean> = ArrayList()
260 
261     /**
262      * Add an element with a given [name][elementName], [descriptor],
263      * type annotations and optionality the resulting descriptor.
264      *
265      * Example of usage:
266      * ```
267      * class Data(
268      *     val intField: Int? = null, // Optional, has default value
269      *     @ProtoNumber(1) val longField: Long
270      * )
271      *
272      * // Corresponding descriptor
273      * SerialDescriptor("package.Data") {
274      *     element<Int?>("intField", isOptional = true)
275      *     element<Long>("longField", annotations = listOf(protoIdAnnotationInstance))
276      * }
277      * ```
278      */
elementnull279     public fun element(
280         elementName: String,
281         descriptor: SerialDescriptor,
282         annotations: List<Annotation> = emptyList(),
283         isOptional: Boolean = false
284     ) {
285         require(uniqueNames.add(elementName)) { "Element with name '$elementName' is already registered in $serialName" }
286         elementNames += elementName
287         elementDescriptors += descriptor
288         elementAnnotations += annotations
289         elementOptionality += isOptional
290     }
291 }
292 
293 /**
294  * A reified version of [element] function that
295  * extract descriptor using `serializer<T>().descriptor` call with all the restrictions of `serializer<T>().descriptor`.
296  */
elementnull297 public inline fun <reified T> ClassSerialDescriptorBuilder.element(
298     elementName: String,
299     annotations: List<Annotation> = emptyList(),
300     isOptional: Boolean = false
301 ) {
302     val descriptor = serializer<T>().descriptor
303     element(elementName, descriptor, annotations, isOptional)
304 }
305 
306 @OptIn(ExperimentalSerializationApi::class)
307 internal class SerialDescriptorImpl(
308     override val serialName: String,
309     override val kind: SerialKind,
310     override val elementsCount: Int,
311     typeParameters: List<SerialDescriptor>,
312     builder: ClassSerialDescriptorBuilder
313 ) : SerialDescriptor, CachedNames {
314 
315     override val annotations: List<Annotation> = builder.annotations
316     override val serialNames: Set<String> = builder.elementNames.toHashSet()
317 
318     private val elementNames: Array<String> = builder.elementNames.toTypedArray()
319     private val elementDescriptors: Array<SerialDescriptor> = builder.elementDescriptors.compactArray()
320     private val elementAnnotations: Array<List<Annotation>> = builder.elementAnnotations.toTypedArray()
321     private val elementOptionality: BooleanArray = builder.elementOptionality.toBooleanArray()
<lambda>null322     private val name2Index: Map<String, Int> = elementNames.withIndex().map { it.value to it.index }.toMap()
323     private val typeParametersDescriptors: Array<SerialDescriptor> = typeParameters.compactArray()
<lambda>null324     private val _hashCode: Int by lazy { hashCodeImpl(typeParametersDescriptors) }
325 
getElementNamenull326     override fun getElementName(index: Int): String = elementNames.getChecked(index)
327     override fun getElementIndex(name: String): Int = name2Index[name] ?: CompositeDecoder.UNKNOWN_NAME
328     override fun getElementAnnotations(index: Int): List<Annotation> = elementAnnotations.getChecked(index)
329     override fun getElementDescriptor(index: Int): SerialDescriptor = elementDescriptors.getChecked(index)
330     override fun isElementOptional(index: Int): Boolean = elementOptionality.getChecked(index)
331 
332     override fun equals(other: Any?): Boolean =
333         equalsImpl(other) { otherDescriptor: SerialDescriptorImpl ->
334             typeParametersDescriptors.contentEquals(
335                 otherDescriptor.typeParametersDescriptors
336             )
337         }
338 
hashCodenull339     override fun hashCode(): Int = _hashCode
340 
341     override fun toString(): String {
342         return (0 until elementsCount).joinToString(", ", prefix = "$serialName(", postfix = ")") {
343             getElementName(it) + ": " + getElementDescriptor(it).serialName
344         }
345     }
346 }
347 
348