1 /* <lambda>null2 * Copyright 2025 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.appfunctions.compiler.core 18 19 import androidx.appfunctions.compiler.core.AppFunctionTypeReference.AppFunctionSupportedTypeCategory.PRIMITIVE_ARRAY 20 import androidx.appfunctions.compiler.core.AppFunctionTypeReference.AppFunctionSupportedTypeCategory.PRIMITIVE_LIST 21 import androidx.appfunctions.compiler.core.AppFunctionTypeReference.AppFunctionSupportedTypeCategory.PRIMITIVE_SINGULAR 22 import androidx.appfunctions.compiler.core.AppFunctionTypeReference.AppFunctionSupportedTypeCategory.SERIALIZABLE_LIST 23 import androidx.appfunctions.compiler.core.AppFunctionTypeReference.AppFunctionSupportedTypeCategory.SERIALIZABLE_PROXY_LIST 24 import androidx.appfunctions.compiler.core.AppFunctionTypeReference.AppFunctionSupportedTypeCategory.SERIALIZABLE_PROXY_SINGULAR 25 import androidx.appfunctions.compiler.core.AppFunctionTypeReference.AppFunctionSupportedTypeCategory.SERIALIZABLE_SINGULAR 26 import androidx.appfunctions.metadata.AppFunctionPrimitiveTypeMetadata 27 import com.google.devtools.ksp.symbol.KSTypeReference 28 import com.squareup.kotlinpoet.LIST 29 import com.squareup.kotlinpoet.TypeName 30 import java.time.LocalDateTime 31 32 /** Represents a type that is supported by AppFunction and AppFunctionSerializable. */ 33 class AppFunctionTypeReference(val selfTypeReference: KSTypeReference) { 34 35 /** 36 * The category of this reference type. 37 * 38 * The category of a type is determined by its underlying type. For example, a type reference to 39 * a list of strings will have a category of PRIMITIVE_LIST. 40 */ 41 val typeCategory: AppFunctionSupportedTypeCategory by lazy { 42 when { 43 selfTypeReference.asStringWithoutNullQualifier() in SUPPORTED_SINGLE_PRIMITIVE_TYPES -> 44 PRIMITIVE_SINGULAR 45 selfTypeReference.asStringWithoutNullQualifier() in SUPPORTED_ARRAY_PRIMITIVE_TYPES -> 46 PRIMITIVE_ARRAY 47 isAppFunctionSerializableProxyType(selfTypeReference) -> SERIALIZABLE_PROXY_SINGULAR 48 isSupportedPrimitiveListType(selfTypeReference) -> PRIMITIVE_LIST 49 isAppFunctionSerializableProxyListType(selfTypeReference) -> SERIALIZABLE_PROXY_LIST 50 isAppFunctionSerializableListType(selfTypeReference) -> SERIALIZABLE_LIST 51 isAppFunctionSerializableType(selfTypeReference) -> SERIALIZABLE_SINGULAR 52 else -> 53 throw ProcessingException( 54 "Unsupported type reference ${selfTypeReference.ensureQualifiedTypeName().asString()}", 55 selfTypeReference, 56 ) 57 } 58 } 59 60 /** 61 * If this type is nullable. 62 * 63 * @return true if the type is nullable, false otherwise. 64 */ 65 val isNullable: Boolean by lazy { selfTypeReference.toTypeName().isNullable } 66 67 /** 68 * The type reference of the list element if the type reference is a list. 69 * 70 * @return the type reference of the list element if the type reference is a list. 71 * @throws IllegalArgumentException if used for a non-list type. 72 */ 73 val itemTypeReference: KSTypeReference by lazy { -> 74 require(selfTypeReference.isOfType(LIST)) { "Type reference is not a list" } 75 selfTypeReference.resolveListParameterizedType() 76 } 77 78 /** 79 * The type reference itself or the type reference of the list element if the type reference is 80 * a list. For example, if the type reference is List<String>, then the selfOrItemTypeReference 81 * will be String. 82 */ 83 val selfOrItemTypeReference: KSTypeReference by lazy { 84 if (selfTypeReference.isOfType(LIST)) { 85 itemTypeReference 86 } else { 87 selfTypeReference 88 } 89 } 90 91 /** 92 * Checks if the type reference is of the given category. 93 * 94 * @param category The category to check. 95 * @return true if the type reference is of the given category, false otherwise. 96 */ 97 fun isOfTypeCategory(category: AppFunctionSupportedTypeCategory): Boolean { 98 return this.typeCategory == category 99 } 100 101 /** 102 * The category of types that are supported by app functions. 103 * 104 * The category of a type is determined by its underlying type. For example, a type reference to 105 * a list of strings will have a category of PRIMITIVE_LIST. 106 */ 107 enum class AppFunctionSupportedTypeCategory { 108 PRIMITIVE_SINGULAR, 109 PRIMITIVE_ARRAY, 110 PRIMITIVE_LIST, 111 SERIALIZABLE_SINGULAR, 112 SERIALIZABLE_LIST, 113 SERIALIZABLE_PROXY_SINGULAR, 114 SERIALIZABLE_PROXY_LIST 115 } 116 117 companion object { 118 /** 119 * Checks if the type reference is a supported type. 120 * 121 * A supported type is a primitive type, a type annotated as @AppFunctionSerializable, or a 122 * list of a supported type. 123 */ 124 fun isSupportedType(typeReferenceArgument: KSTypeReference): Boolean { 125 return typeReferenceArgument.asStringWithoutNullQualifier() in SUPPORTED_TYPES || 126 isSupportedPrimitiveListType(typeReferenceArgument) || 127 isAppFunctionSerializableType(typeReferenceArgument) || 128 isAppFunctionSerializableListType(typeReferenceArgument) || 129 isAppFunctionSerializableProxyListType(typeReferenceArgument) 130 } 131 132 /** 133 * Converts a type reference to an AppFunction data type. 134 * 135 * @return The AppFunction data type. 136 * @throws ProcessingException If the type reference is not a supported type. 137 */ 138 fun KSTypeReference.toAppFunctionDatatype(): Int { 139 return when (this.toTypeName().ignoreNullable().toString()) { 140 String::class.ensureQualifiedName() -> AppFunctionPrimitiveTypeMetadata.TYPE_STRING 141 Int::class.ensureQualifiedName() -> AppFunctionPrimitiveTypeMetadata.TYPE_INT 142 Long::class.ensureQualifiedName() -> AppFunctionPrimitiveTypeMetadata.TYPE_LONG 143 Float::class.ensureQualifiedName() -> AppFunctionPrimitiveTypeMetadata.TYPE_FLOAT 144 Double::class.ensureQualifiedName() -> AppFunctionPrimitiveTypeMetadata.TYPE_DOUBLE 145 Boolean::class.ensureQualifiedName() -> 146 AppFunctionPrimitiveTypeMetadata.TYPE_BOOLEAN 147 Unit::class.ensureQualifiedName() -> AppFunctionPrimitiveTypeMetadata.TYPE_UNIT 148 Byte::class.ensureQualifiedName() -> AppFunctionPrimitiveTypeMetadata.TYPE_BYTES 149 IntArray::class.ensureQualifiedName() -> AppFunctionPrimitiveTypeMetadata.TYPE_INT 150 LongArray::class.ensureQualifiedName() -> AppFunctionPrimitiveTypeMetadata.TYPE_LONG 151 FloatArray::class.ensureQualifiedName() -> 152 AppFunctionPrimitiveTypeMetadata.TYPE_FLOAT 153 DoubleArray::class.ensureQualifiedName() -> 154 AppFunctionPrimitiveTypeMetadata.TYPE_DOUBLE 155 BooleanArray::class.ensureQualifiedName() -> 156 AppFunctionPrimitiveTypeMetadata.TYPE_BOOLEAN 157 ByteArray::class.ensureQualifiedName() -> 158 AppFunctionPrimitiveTypeMetadata.TYPE_BYTES 159 ANDROID_PENDING_INTENT -> AppFunctionPrimitiveTypeMetadata.TYPE_PENDING_INTENT 160 else -> 161 throw ProcessingException( 162 "Unsupported type reference " + this.ensureQualifiedTypeName().asString(), 163 this, 164 ) 165 } 166 } 167 168 private fun isSupportedPrimitiveListType(typeReferenceArgument: KSTypeReference) = 169 typeReferenceArgument.isOfType(LIST) && 170 typeReferenceArgument 171 .resolveListParameterizedType() 172 .asStringWithoutNullQualifier() in SUPPORTED_PRIMITIVE_TYPES_IN_LIST 173 174 private fun isAppFunctionSerializableListType( 175 typeReferenceArgument: KSTypeReference 176 ): Boolean { 177 return typeReferenceArgument.isOfType(LIST) && 178 isAppFunctionSerializableType(typeReferenceArgument.resolveListParameterizedType()) 179 } 180 181 private fun isAppFunctionSerializableType(typeReferenceArgument: KSTypeReference): Boolean { 182 return typeReferenceArgument 183 .resolve() 184 .declaration 185 .annotations 186 .findAnnotation(IntrospectionHelper.AppFunctionSerializableAnnotation.CLASS_NAME) != 187 null 188 } 189 190 private fun isAppFunctionSerializableProxyListType( 191 typeReferenceArgument: KSTypeReference 192 ): Boolean { 193 return typeReferenceArgument.isOfType(LIST) && 194 isAppFunctionSerializableProxyType( 195 typeReferenceArgument.resolveListParameterizedType() 196 ) 197 } 198 199 private fun isAppFunctionSerializableProxyType( 200 typeReferenceArgument: KSTypeReference 201 ): Boolean { 202 return typeReferenceArgument.asStringWithoutNullQualifier() in 203 SUPPORTED_SINGLE_SERIALIZABLE_PROXY_TYPES || 204 typeReferenceArgument 205 .resolve() 206 .declaration 207 .annotations 208 .findAnnotation( 209 IntrospectionHelper.AppFunctionSerializableProxyAnnotation.CLASS_NAME 210 ) != null 211 } 212 213 private fun TypeName.ignoreNullable(): TypeName { 214 return copy(nullable = false) 215 } 216 217 private fun KSTypeReference.asStringWithoutNullQualifier(): String = 218 toTypeName().ignoreNullable().toString() 219 220 // Android Only primitives 221 private const val ANDROID_PENDING_INTENT = "android.app.PendingIntent" 222 private const val ANDROID_URI = "android.net.Uri" 223 224 private val SUPPORTED_ARRAY_PRIMITIVE_TYPES = 225 setOf( 226 IntArray::class.ensureQualifiedName(), 227 LongArray::class.ensureQualifiedName(), 228 FloatArray::class.ensureQualifiedName(), 229 DoubleArray::class.ensureQualifiedName(), 230 BooleanArray::class.ensureQualifiedName(), 231 ByteArray::class.ensureQualifiedName(), 232 ) 233 234 private val SUPPORTED_SINGLE_PRIMITIVE_TYPES = 235 setOf( 236 Int::class.ensureQualifiedName(), 237 Long::class.ensureQualifiedName(), 238 Float::class.ensureQualifiedName(), 239 Double::class.ensureQualifiedName(), 240 Boolean::class.ensureQualifiedName(), 241 String::class.ensureQualifiedName(), 242 Unit::class.ensureQualifiedName(), 243 ANDROID_PENDING_INTENT 244 ) 245 246 private val SUPPORTED_SINGLE_SERIALIZABLE_PROXY_TYPES = 247 setOf(LocalDateTime::class.ensureQualifiedName(), ANDROID_URI) 248 249 private val SUPPORTED_PRIMITIVE_TYPES_IN_LIST = setOf(String::class.ensureQualifiedName()) 250 251 private val SUPPORTED_TYPES = 252 SUPPORTED_SINGLE_PRIMITIVE_TYPES + 253 SUPPORTED_ARRAY_PRIMITIVE_TYPES + 254 SUPPORTED_SINGLE_SERIALIZABLE_PROXY_TYPES 255 256 val SUPPORTED_TYPES_STRING: String = 257 SUPPORTED_TYPES.joinToString(",\n") + 258 "\nLists of ${SUPPORTED_PRIMITIVE_TYPES_IN_LIST.joinToString(", ")}" 259 } 260 } 261