1 /*
<lambda>null2  * Copyright (C) 2022 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 com.android.libraries.pcc.chronicle.api.integration
18 
19 import com.android.libraries.pcc.chronicle.api.DataTypeDescriptor
20 import com.android.libraries.pcc.chronicle.api.DataTypeDescriptorSet
21 import com.android.libraries.pcc.chronicle.api.FieldType
22 import java.time.Duration
23 import java.time.Instant
24 import kotlin.reflect.KClass
25 
26 /** Default implementation of [DataTypeDescriptorSet]. */
27 class DefaultDataTypeDescriptorSet(private val dtds: Set<DataTypeDescriptor>) :
28   DataTypeDescriptorSet {
29   private val dtdsByName: Map<String, DataTypeDescriptor> =
30     dtds.flatMap { collectNestedDataTypeDescriptors(it) }.associateBy { it.name }
31 
32   private val dtdsByKClass: Map<KClass<*>, DataTypeDescriptor> =
33     dtdsByName.values.associateBy { it.cls }
34 
35   private fun collectNestedDataTypeDescriptors(dtd: DataTypeDescriptor): List<DataTypeDescriptor> {
36     return dtd.innerTypes.fold(listOf(dtd)) { acc, t -> acc + collectNestedDataTypeDescriptors(t) }
37   }
38 
39   override fun getOrNull(name: String): DataTypeDescriptor? = dtdsByName[name]
40 
41   override tailrec fun findFieldTypeOrThrow(
42     dtd: DataTypeDescriptor,
43     accessPath: List<String>,
44   ): FieldType {
45     require(accessPath.isNotEmpty()) { "Cannot find field type for empty access path." }
46 
47     val field = accessPath[0]
48     val fieldType =
49       requireNotNull(dtd.fields[field]) { "Field \"$field\" not found in ${dtd.name}" }
50 
51     if (accessPath.size == 1) return fieldType
52 
53     val nextDtd = requireNotNull(findDataTypeDescriptor(fieldType))
54     return findFieldTypeOrThrow(nextDtd, accessPath.drop(1))
55   }
56 
57   override fun findDataTypeDescriptor(fieldType: FieldType): DataTypeDescriptor? {
58     return when (fieldType) {
59       is FieldType.Array -> findDataTypeDescriptor(fieldType.itemFieldType)
60       is FieldType.List -> findDataTypeDescriptor(fieldType.itemFieldType)
61       is FieldType.Nullable -> findDataTypeDescriptor(fieldType.itemFieldType)
62       is FieldType.Nested -> getOrNull(fieldType.name)
63       is FieldType.Reference -> getOrNull(fieldType.name)
64       FieldType.Boolean,
65       FieldType.Byte,
66       FieldType.ByteArray,
67       FieldType.Char,
68       FieldType.Double,
69       FieldType.Duration,
70       FieldType.Float,
71       FieldType.Instant,
72       FieldType.Integer,
73       FieldType.Long,
74       FieldType.Short,
75       FieldType.String,
76       is FieldType.Enum,
77       is FieldType.Opaque,
78       // We would need to know which item within the tuple we are interested in.
79       is FieldType.Tuple -> null
80     }
81   }
82 
83   override fun findDataTypeDescriptor(cls: KClass<*>): DataTypeDescriptor? = dtdsByKClass[cls]
84 
85   override fun fieldTypeAsClass(fieldType: FieldType): Class<*> {
86     return when (fieldType) {
87       FieldType.Boolean -> Boolean::class.javaObjectType
88       FieldType.Byte -> Byte::class.javaObjectType
89       FieldType.ByteArray -> ByteArray::class.java
90       FieldType.Char -> Char::class.javaObjectType
91       FieldType.Double -> Double::class.javaObjectType
92       FieldType.Duration -> Duration::class.java
93       FieldType.Float -> Float::class.javaObjectType
94       FieldType.Instant -> Instant::class.java
95       FieldType.Integer -> Int::class.javaObjectType
96       FieldType.Long -> Long::class.javaObjectType
97       FieldType.Short -> Short::class.javaObjectType
98       FieldType.String -> String::class.java
99       is FieldType.Enum -> Enum::class.java
100       is FieldType.Array -> Array::class.java
101       is FieldType.List -> List::class.java
102       is FieldType.Reference -> this[fieldType.name].cls.java
103       is FieldType.Nested -> this[fieldType.name].cls.java
104       is FieldType.Opaque -> Class.forName(fieldType.name)
105       is FieldType.Nullable -> fieldTypeAsClass(fieldType.itemFieldType)
106       is FieldType.Tuple ->
107         // TODO(b/208662121): Tuples could be android.util.Pair or kotlin.Pair (or similar)
108         throw IllegalArgumentException("Tuple is too ambiguous to return a field type.")
109     }
110   }
111 
112   override fun toSet(): Set<DataTypeDescriptor> = dtds
113 }
114