1 /* 2 * Copyright 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.navigation.serialization 18 19 import androidx.annotation.RestrictTo 20 import androidx.navigation.CollectionNavType 21 import androidx.navigation.NavType 22 import kotlinx.serialization.ExperimentalSerializationApi 23 import kotlinx.serialization.KSerializer 24 import kotlinx.serialization.SerializationStrategy 25 import kotlinx.serialization.descriptors.SerialDescriptor 26 import kotlinx.serialization.encoding.AbstractEncoder 27 import kotlinx.serialization.encoding.Encoder 28 import kotlinx.serialization.modules.EmptySerializersModule 29 import kotlinx.serialization.modules.SerializersModule 30 31 /** Encodes KClass of type T into a route filled with arguments */ 32 @OptIn(ExperimentalSerializationApi::class) 33 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 34 public class RouteEncoder<T : Any>( 35 private val serializer: KSerializer<T>, 36 private val typeMap: Map<String, NavType<Any?>> 37 ) : AbstractEncoder() { 38 override val serializersModule: SerializersModule = EmptySerializersModule() 39 private val map: MutableMap<String, List<String>> = mutableMapOf() 40 private var elementIndex: Int = -1 41 42 /** 43 * Entry point to set up and start encoding [T]. 44 * 45 * The default entry point is [encodeSerializableValue] but we need to override it to handle 46 * primitive and non-primitive values by converting them directly to string (instead of the 47 * default implementation which further serializes nested non-primitive values). So we delegate 48 * to the default entry by directly calling [super.encodeSerializableValue]. 49 */ 50 @Suppress("UNCHECKED_CAST") encodeToArgMapnull51 public fun encodeToArgMap(value: Any): Map<String, List<String>> { 52 super.encodeSerializableValue(serializer, value as T) 53 return map.toMap() 54 } 55 56 /** 57 * Can handle both primitives and non-primitives. This method is called in three possible 58 * scenarios: 59 * 1. nullable primitive type with non-null value 60 * 2. nullable non-primitive type with non-null value 61 * 3. non-nullable non-primitive type 62 * 63 * String literal "null" is considered non-null value. 64 */ encodeSerializableValuenull65 override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) { 66 internalEncodeValue(value) 67 } 68 69 /** Essentially called for every single argument. */ encodeElementnull70 override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean { 71 elementIndex = index 72 return true 73 } 74 75 /** 76 * Called for non-nullable primitives of non-null value. 77 * 78 * String literal "null" is considered non-null value. 79 */ encodeValuenull80 override fun encodeValue(value: Any) { 81 internalEncodeValue(value) 82 } 83 84 /** Called for primitive / non-primitives of null value */ encodeNullnull85 override fun encodeNull() { 86 internalEncodeValue(null) 87 } 88 encodeInlinenull89 override fun encodeInline(descriptor: SerialDescriptor): Encoder { 90 if (descriptor.isValueClass()) elementIndex = 0 91 return super.encodeInline(descriptor) 92 } 93 internalEncodeValuenull94 private fun internalEncodeValue(value: Any?) { 95 val argName = serializer.descriptor.getElementName(elementIndex) 96 val navType = typeMap[argName] 97 checkNotNull(navType) { 98 "Cannot find NavType for argument $argName. Please provide NavType through typeMap." 99 } 100 val parsedValue = 101 if (navType is CollectionNavType) { 102 navType.serializeAsValues(value) 103 } else { 104 listOf(navType.serializeAsValue(value)) 105 } 106 map[argName] = parsedValue 107 } 108 } 109