1 /* <lambda>null2 * 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.appfunctions.metadata 18 19 import android.annotation.SuppressLint 20 import androidx.annotation.IntDef 21 import androidx.annotation.RestrictTo 22 import androidx.appsearch.annotation.Document 23 24 @IntDef( 25 AppFunctionDataTypeMetadata.TYPE_UNIT, 26 AppFunctionDataTypeMetadata.TYPE_BOOLEAN, 27 AppFunctionDataTypeMetadata.TYPE_BYTES, 28 AppFunctionDataTypeMetadata.TYPE_OBJECT, 29 AppFunctionDataTypeMetadata.TYPE_DOUBLE, 30 AppFunctionDataTypeMetadata.TYPE_FLOAT, 31 AppFunctionDataTypeMetadata.TYPE_LONG, 32 AppFunctionDataTypeMetadata.TYPE_INT, 33 AppFunctionDataTypeMetadata.TYPE_STRING, 34 AppFunctionDataTypeMetadata.TYPE_ARRAY, 35 AppFunctionDataTypeMetadata.TYPE_REFERENCE, 36 AppFunctionDataTypeMetadata.TYPE_ALL_OF, 37 AppFunctionDataTypeMetadata.TYPE_PENDING_INTENT, 38 ) 39 @Retention(AnnotationRetention.SOURCE) 40 internal annotation class AppFunctionDataType 41 42 @IntDef( 43 AppFunctionDataTypeMetadata.TYPE_UNIT, 44 AppFunctionDataTypeMetadata.TYPE_BOOLEAN, 45 AppFunctionDataTypeMetadata.TYPE_BYTES, 46 AppFunctionDataTypeMetadata.TYPE_DOUBLE, 47 AppFunctionDataTypeMetadata.TYPE_FLOAT, 48 AppFunctionDataTypeMetadata.TYPE_LONG, 49 AppFunctionDataTypeMetadata.TYPE_INT, 50 AppFunctionDataTypeMetadata.TYPE_STRING, 51 AppFunctionDataTypeMetadata.TYPE_PENDING_INTENT, 52 ) 53 @Retention(AnnotationRetention.SOURCE) 54 internal annotation class AppFunctionPrimitiveType 55 56 /** Base class for defining the schema of an input or output type. */ 57 public abstract class AppFunctionDataTypeMetadata 58 internal constructor( 59 /** Whether the data type is nullable. */ 60 public val isNullable: Boolean, 61 ) { 62 /** Converts this [AppFunctionDataTypeMetadata] to an [AppFunctionDataTypeMetadataDocument]. */ 63 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 64 public abstract fun toAppFunctionDataTypeMetadataDocument(): AppFunctionDataTypeMetadataDocument 65 66 public companion object { 67 /** Void type. */ 68 internal const val TYPE_UNIT: Int = 0 69 /** Boolean type. */ 70 internal const val TYPE_BOOLEAN: Int = 1 71 /** Byte array type. */ 72 internal const val TYPE_BYTES: Int = 2 73 /** 74 * Object type. The schema of the object is defined in a [AppFunctionObjectTypeMetadata]. 75 */ 76 internal const val TYPE_OBJECT: Int = 3 77 /** Double type. */ 78 internal const val TYPE_DOUBLE: Int = 4 79 /** Float type. */ 80 internal const val TYPE_FLOAT: Int = 5 81 /** Long type. */ 82 internal const val TYPE_LONG: Int = 6 83 /** Integer type. */ 84 internal const val TYPE_INT: Int = 7 85 /** String type. */ 86 internal const val TYPE_STRING: Int = 8 87 /** Array type. The schema of the array is defined in a [AppFunctionArrayTypeMetadata] */ 88 internal const val TYPE_ARRAY: Int = 10 89 /** 90 * Reference type. The schema of the reference is defined in a 91 * [AppFunctionReferenceTypeMetadata] 92 */ 93 internal const val TYPE_REFERENCE: Int = 11 94 /** 95 * All of type. The schema of the all of type is defined in a [AppFunctionAllOfTypeMetadata] 96 */ 97 internal const val TYPE_ALL_OF: Int = 12 98 /** Pending Intent type. */ 99 internal const val TYPE_PENDING_INTENT: Int = 13 100 101 /** All primitive types used in [AppFunctionPrimitiveType] @IntDef annotation. */ 102 internal val PRIMITIVE_TYPES = 103 setOf( 104 TYPE_UNIT, 105 TYPE_BOOLEAN, 106 TYPE_BYTES, 107 TYPE_DOUBLE, 108 TYPE_FLOAT, 109 TYPE_LONG, 110 TYPE_INT, 111 TYPE_STRING, 112 TYPE_PENDING_INTENT 113 ) 114 } 115 116 override fun equals(other: Any?): Boolean { 117 if (this === other) return true 118 if (javaClass != other?.javaClass) return false 119 120 other as AppFunctionDataTypeMetadata 121 122 return isNullable == other.isNullable 123 } 124 125 override fun hashCode(): Int { 126 return isNullable.hashCode() 127 } 128 129 override fun toString(): String { 130 return "AppFunctionDataTypeMetadata(isNullable=$isNullable)" 131 } 132 } 133 134 /** Defines the schema of an array data type. */ 135 public class AppFunctionArrayTypeMetadata( 136 /** The type of items in the array. */ 137 public val itemType: AppFunctionDataTypeMetadata, 138 /** Whether this data type is nullable. */ 139 isNullable: Boolean, 140 ) : AppFunctionDataTypeMetadata(isNullable = isNullable) { equalsnull141 override fun equals(other: Any?): Boolean { 142 if (!super.equals(other)) return false 143 if (other !is AppFunctionArrayTypeMetadata) return false 144 return itemType == other.itemType 145 } 146 hashCodenull147 override fun hashCode(): Int { 148 var result = super.hashCode() 149 result = 31 * result + itemType.hashCode() 150 return result 151 } 152 toStringnull153 override fun toString(): String { 154 return "AppFunctionArrayTypeMetadataDocument(" + 155 "itemType=$itemType, " + 156 "isNullable=$isNullable" + 157 ")" 158 } 159 160 /** Converts this [AppFunctionArrayTypeMetadata] to an [AppFunctionDataTypeMetadataDocument]. */ 161 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) toAppFunctionDataTypeMetadataDocumentnull162 override fun toAppFunctionDataTypeMetadataDocument(): AppFunctionDataTypeMetadataDocument { 163 return AppFunctionDataTypeMetadataDocument( 164 itemType = itemType.toAppFunctionDataTypeMetadataDocument(), 165 type = TYPE, 166 isNullable = isNullable, 167 ) 168 } 169 170 public companion object { 171 /** Array type. The schema of the array is defined in a [AppFunctionArrayTypeMetadata] */ 172 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public const val TYPE: Int = TYPE_ARRAY 173 } 174 } 175 176 /** 177 * Defines the schema of a single object data type that is a composition of all of the other types 178 * in the [matchAll] list. 179 * 180 * An object of this type must match all of the [AppFunctionDataTypeMetadata] in the [matchAll] 181 * list. [matchAll] takes an array of object definitions that are composed together to a single 182 * object to form this type. Note that while this composition offers object type extensibility, it 183 * does not imply a hierarchy between the objects matched in [matchAll] i.e. the resulting single 184 * object is a flattened representation of all the other matched objects. 185 * 186 * For example, consider the following objects: 187 * ``` 188 * open class Address ( 189 * open val street: String, 190 * open val city: String, 191 * open val state: String, 192 * open val zipCode: String, 193 * ) 194 * 195 * class PersonWithAddress ( 196 * override val street: String, 197 * override val city: String, 198 * override val state: String, 199 * override val zipCode: String, 200 * val name: String, 201 * val age: Int, 202 * ) : Address(street, city, state, zipCode) 203 * ``` 204 * 205 * The following [AppFunctionAllOfTypeMetadata] can be used to define a data type that matches 206 * PersonWithAddress. 207 * 208 * ``` 209 * val personWithAddressType = AppFunctionAllOfTypeMetadata( 210 * qualifiedName = "androidx.appfunctions.metadata.PersonWithAddress", 211 * matchAll = listOf( 212 * AppFunctionObjectTypeMetadata( 213 * properties = mapOf( 214 * "street" to AppFunctionPrimitiveTypeMetadata(...), 215 * "city" to AppFunctionPrimitiveTypeMetadata(...), 216 * "state" to AppFunctionPrimitiveTypeMetadata(...), 217 * "zipCode" to AppFunctionPrimitiveTypeMetadata(...), 218 * ), 219 * required = listOf("street", "city", "state", "zipCode"), 220 * qualifiedName = "androidx.appfunctions.metadata.Address", 221 * isNullable = false, 222 * ), 223 * AppFunctionObjectTypeMetadata( 224 * properties = mapOf( 225 * "name" to AppFunctionPrimitiveTypeMetadata(...), 226 * "age" to AppFunctionPrimitiveTypeMetadata(...), 227 * ), 228 * required = listOf("name", "age"), 229 * qualifiedName = "androidx.appfunctions.metadata.PersonWithAddress", 230 * isNullable = false, 231 * ), 232 * ), 233 * isNullable = false, 234 * ) 235 * ``` 236 * 237 * This data type can be used to define the schema of an input or output type. 238 */ 239 public class AppFunctionAllOfTypeMetadata( 240 /** The list of data types that are composed. */ 241 public val matchAll: List<AppFunctionDataTypeMetadata>, 242 /** 243 * The composed object's qualified name if available. For example, 244 * "androidx.appfunctions.metadata.PersonWithAddress". 245 * 246 * Use this value to set [androidx.appfunctions.AppFunctionData.qualifiedName] when trying to 247 * build the parameters for [androidx.appfunctions.ExecuteAppFunctionRequest]. 248 */ 249 public val qualifiedName: String?, 250 251 /** Whether this data type is nullable. */ 252 isNullable: Boolean, 253 ) : AppFunctionDataTypeMetadata(isNullable = isNullable) { 254 equalsnull255 override fun equals(other: Any?): Boolean { 256 if (!super.equals(other)) return false 257 if (other !is AppFunctionAllOfTypeMetadata) return false 258 if (qualifiedName != other.qualifiedName) return false 259 return matchAll == other.matchAll 260 } 261 hashCodenull262 override fun hashCode(): Int { 263 var result = super.hashCode() 264 result = 31 * result + matchAll.hashCode() 265 if (qualifiedName != null) { 266 result = 31 * result + qualifiedName.hashCode() 267 } 268 return result 269 } 270 toStringnull271 override fun toString(): String { 272 return "AppFunctionAllOfTypeMetadata(matchAll=$matchAll, isNullable=$isNullable)" 273 } 274 275 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) toAppFunctionDataTypeMetadataDocumentnull276 override fun toAppFunctionDataTypeMetadataDocument(): AppFunctionDataTypeMetadataDocument { 277 val allOfDocuments = matchAll.map { it.toAppFunctionDataTypeMetadataDocument() } 278 return AppFunctionDataTypeMetadataDocument( 279 type = TYPE, 280 allOf = allOfDocuments, 281 isNullable = isNullable, 282 objectQualifiedName = qualifiedName 283 ) 284 } 285 286 public companion object { 287 /** 288 * All Of type. 289 * 290 * The [AppFunctionAllOfTypeMetadata] is used to define a component only data type object 291 * that is a composition of all of the types in the list. 292 * 293 * The [AppFunctionAllOfTypeMetadata] can contain either: 294 * * Top level [AppFunctionObjectTypeMetadata] 295 * * An [AppFunctionReferenceTypeMetadata] to an outer object metadata. 296 */ 297 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public const val TYPE: Int = TYPE_ALL_OF 298 } 299 } 300 301 /** Defines the schema of a object type. */ 302 public class AppFunctionObjectTypeMetadata( 303 /** The schema of the properties of the object. */ 304 public val properties: Map<String, AppFunctionDataTypeMetadata>, 305 /** A list of required properties' names. */ 306 public val required: List<String>, 307 /** 308 * The object's qualified name if available. 309 * 310 * Use this value to set [androidx.appfunctions.AppFunctionData.qualifiedName] when trying to 311 * build the parameters for [androidx.appfunctions.ExecuteAppFunctionRequest]. 312 */ 313 public val qualifiedName: String?, 314 /** Whether this data type is nullable. */ 315 isNullable: Boolean, 316 ) : AppFunctionDataTypeMetadata(isNullable = isNullable) { equalsnull317 override fun equals(other: Any?): Boolean { 318 if (!super.equals(other)) return false 319 if (other !is AppFunctionObjectTypeMetadata) return false 320 321 if (properties != other.properties) return false 322 if (required != other.required) return false 323 if (qualifiedName != other.qualifiedName) return false 324 325 return true 326 } 327 hashCodenull328 override fun hashCode(): Int { 329 var result = super.hashCode() 330 result = 31 * result + properties.hashCode() 331 result = 31 * result + required.hashCode() 332 if (qualifiedName != null) { 333 result = 31 * result + qualifiedName.hashCode() 334 } 335 return result 336 } 337 toStringnull338 override fun toString(): String { 339 return "AppFunctionObjectTypeMetadata(" + 340 "properties=$properties, " + 341 "required=$required, " + 342 "qualifiedName=$qualifiedName, " + 343 "isNullable=$isNullable" + 344 ")" 345 } 346 347 /** 348 * Converts this [AppFunctionObjectTypeMetadata] to an [AppFunctionDataTypeMetadataDocument]. 349 */ 350 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) toAppFunctionDataTypeMetadataDocumentnull351 override fun toAppFunctionDataTypeMetadataDocument(): AppFunctionDataTypeMetadataDocument { 352 val properties = 353 properties.map { (name, dataType) -> 354 AppFunctionNamedDataTypeMetadataDocument( 355 name = checkNotNull(name), 356 dataTypeMetadata = dataType.toAppFunctionDataTypeMetadataDocument() 357 ) 358 } 359 return AppFunctionDataTypeMetadataDocument( 360 type = TYPE, 361 properties = properties, 362 required = required, 363 objectQualifiedName = qualifiedName, 364 isNullable = isNullable, 365 ) 366 } 367 368 public companion object { 369 /** 370 * Object type. The schema of the object is defined in a [AppFunctionObjectTypeMetadata]. 371 */ 372 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public const val TYPE: Int = TYPE_OBJECT 373 } 374 } 375 376 /** 377 * Represents a type that reference a data type that is defined in [AppFunctionComponentsMetadata]. 378 */ 379 public class AppFunctionReferenceTypeMetadata( 380 /** The string referencing a data type defined in [AppFunctionComponentsMetadata]. */ 381 public val referenceDataType: String, 382 /** Whether the data type is nullable. */ 383 isNullable: Boolean 384 ) : AppFunctionDataTypeMetadata(isNullable = isNullable) { equalsnull385 override fun equals(other: Any?): Boolean { 386 if (!super.equals(other)) return false 387 if (other !is AppFunctionReferenceTypeMetadata) return false 388 return referenceDataType == other.referenceDataType 389 } 390 hashCodenull391 override fun hashCode(): Int { 392 var result = super.hashCode() 393 result = 31 * result + referenceDataType.hashCode() 394 return result 395 } 396 toStringnull397 override fun toString(): String { 398 return "AppFunctionReferenceTypeMetadata(" + 399 "referenceDataType=$referenceDataType, " + 400 "isNullable=$isNullable" + 401 ")" 402 } 403 404 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) toAppFunctionDataTypeMetadataDocumentnull405 override fun toAppFunctionDataTypeMetadataDocument(): AppFunctionDataTypeMetadataDocument { 406 return AppFunctionDataTypeMetadataDocument( 407 type = TYPE, 408 dataTypeReference = referenceDataType, 409 isNullable = isNullable, 410 ) 411 } 412 413 public companion object { 414 /** 415 * Object type. The schema of the object is defined in a [AppFunctionObjectTypeMetadata]. 416 */ 417 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public const val TYPE: Int = TYPE_REFERENCE 418 } 419 } 420 421 /** Defines the schema of a primitive data type. */ 422 public class AppFunctionPrimitiveTypeMetadata( 423 @AppFunctionPrimitiveType public val type: Int, 424 isNullable: Boolean 425 ) : AppFunctionDataTypeMetadata(isNullable) { equalsnull426 override fun equals(other: Any?): Boolean { 427 if (!super.equals(other)) return false 428 if (other !is AppFunctionPrimitiveTypeMetadata) return false 429 430 if (type != other.type) return false 431 432 return true 433 } 434 hashCodenull435 override fun hashCode(): Int { 436 var result = super.hashCode() 437 result = 31 * result * type.hashCode() 438 return result 439 } 440 toStringnull441 override fun toString(): String { 442 return "AppFunctionPrimitiveTypeMetadata(type=$type, isNullable=$isNullable)" 443 } 444 445 /** 446 * Converts this [AppFunctionPrimitiveTypeMetadata] to an [AppFunctionDataTypeMetadataDocument]. 447 */ 448 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) toAppFunctionDataTypeMetadataDocumentnull449 override fun toAppFunctionDataTypeMetadataDocument(): AppFunctionDataTypeMetadataDocument { 450 return AppFunctionDataTypeMetadataDocument(type = type, isNullable = isNullable) 451 } 452 453 public companion object { 454 /** Void type. */ 455 public const val TYPE_UNIT: Int = AppFunctionDataTypeMetadata.TYPE_UNIT 456 /** Boolean type. */ 457 public const val TYPE_BOOLEAN: Int = AppFunctionDataTypeMetadata.TYPE_BOOLEAN 458 /** Byte array type. */ 459 public const val TYPE_BYTES: Int = AppFunctionDataTypeMetadata.TYPE_BYTES 460 /** Double type. */ 461 public const val TYPE_DOUBLE: Int = AppFunctionDataTypeMetadata.TYPE_DOUBLE 462 /** Float type. */ 463 public const val TYPE_FLOAT: Int = AppFunctionDataTypeMetadata.TYPE_FLOAT 464 /** Long type. */ 465 public const val TYPE_LONG: Int = AppFunctionDataTypeMetadata.TYPE_LONG 466 /** Integer type. */ 467 public const val TYPE_INT: Int = AppFunctionDataTypeMetadata.TYPE_INT 468 /** String type. */ 469 public const val TYPE_STRING: Int = AppFunctionDataTypeMetadata.TYPE_STRING 470 /** Pending Intent type. */ 471 public const val TYPE_PENDING_INTENT: Int = AppFunctionDataTypeMetadata.TYPE_PENDING_INTENT 472 } 473 } 474 475 /** Represents the persistent storage format of the schema of a data type and its name. */ 476 @Document 477 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 478 public data class AppFunctionNamedDataTypeMetadataDocument( 479 @Document.Namespace public val namespace: String = APP_FUNCTION_NAMESPACE, 480 /** The id of the data type. */ 481 @Document.Id public val id: String = APP_FUNCTION_ID_EMPTY, 482 /** The name of the data type. */ 483 @Document.StringProperty public val name: String, 484 /** The data type metadata. */ 485 @Document.DocumentProperty public val dataTypeMetadata: AppFunctionDataTypeMetadataDocument, 486 ) 487 488 /** Represents the persistent storage format of [AppFunctionDataTypeMetadata]. */ 489 @Document 490 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 491 public data class AppFunctionDataTypeMetadataDocument( 492 @Document.Namespace public val namespace: String = APP_FUNCTION_NAMESPACE, 493 /** The id of the data type. */ 494 @Document.Id public val id: String = APP_FUNCTION_ID_EMPTY, 495 /** The data type. */ 496 @Document.LongProperty @AppFunctionDataType public val type: Int, 497 498 /** 499 * If the [type] is [AppFunctionDataTypeMetadata.TYPE_ARRAY], this specifies the array content 500 * data type. 501 */ 502 @Document.DocumentProperty public val itemType: AppFunctionDataTypeMetadataDocument? = null, 503 /** 504 * If the [type] is [AppFunctionDataTypeMetadata.TYPE_OBJECT], this specified the object's 505 * properties. 506 */ 507 @Document.DocumentProperty 508 public val properties: List<AppFunctionNamedDataTypeMetadataDocument> = emptyList(), 509 510 /** 511 * If the [type] is [AppFunctionDataTypeMetadata.TYPE_ALL_OF], this specified the object's 512 * properties. 513 */ 514 @Document.DocumentProperty 515 public val allOf: List<AppFunctionDataTypeMetadataDocument> = emptyList(), 516 517 /** 518 * If the [type] is [AppFunctionDataTypeMetadata.TYPE_OBJECT], this specified the object's 519 * required properties' names. 520 */ 521 @Document.StringProperty public val required: List<String> = emptyList(), 522 /** 523 * If the [type] is [AppFunctionDataTypeMetadata.TYPE_REFERENCE], this specified the reference. 524 */ 525 @Document.StringProperty public val dataTypeReference: String? = null, 526 /** Whether the type is nullable. */ 527 @Document.BooleanProperty public val isNullable: Boolean = false, 528 /** 529 * If the [type] is [AppFunctionDataTypeMetadata.TYPE_OBJECT], this specified the object's 530 * qualified name if available. 531 */ 532 @Document.StringProperty public val objectQualifiedName: String? = null, 533 ) { 534 @SuppressLint( 535 // When doesn't handle @IntDef correctly. 536 "WrongConstant" 537 ) toAppFunctionDataTypeMetadatanull538 public fun toAppFunctionDataTypeMetadata(): AppFunctionDataTypeMetadata = 539 when (type) { 540 AppFunctionDataTypeMetadata.TYPE_ARRAY -> { 541 val itemType = checkNotNull(itemType) { "Item type must be present for array type" } 542 AppFunctionArrayTypeMetadata( 543 itemType = itemType.toAppFunctionDataTypeMetadata(), 544 isNullable = isNullable 545 ) 546 } 547 AppFunctionDataTypeMetadata.TYPE_OBJECT -> { 548 check(properties.isNotEmpty()) { 549 "Properties must be present for object type can't be empty" 550 } 551 val propertiesMap = 552 properties.associate { 553 it.name to it.dataTypeMetadata.toAppFunctionDataTypeMetadata() 554 } 555 AppFunctionObjectTypeMetadata( 556 properties = propertiesMap, 557 required = required, 558 qualifiedName = objectQualifiedName, 559 isNullable = isNullable 560 ) 561 } 562 AppFunctionDataTypeMetadata.TYPE_REFERENCE -> 563 AppFunctionReferenceTypeMetadata( 564 referenceDataType = 565 checkNotNull(dataTypeReference) { 566 "Data type reference must be present for reference type" 567 }, 568 isNullable = isNullable 569 ) 570 AppFunctionDataTypeMetadata.TYPE_ALL_OF -> 571 AppFunctionAllOfTypeMetadata( 572 matchAll = allOf.map { it.toAppFunctionDataTypeMetadata() }, 573 qualifiedName = objectQualifiedName, 574 isNullable = isNullable 575 ) 576 in AppFunctionDataTypeMetadata.PRIMITIVE_TYPES -> 577 AppFunctionPrimitiveTypeMetadata(type = type, isNullable = isNullable) 578 else -> throw IllegalArgumentException("Unknown type: $type") 579 } 580 } 581