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.analysis.impl
18 
19 import com.android.libraries.pcc.chronicle.analysis.CantripFactory
20 import com.android.libraries.pcc.chronicle.analysis.ChronicleContext
21 import com.android.libraries.pcc.chronicle.api.ConnectionRequest
22 import com.android.libraries.pcc.chronicle.api.DataTypeDescriptor
23 import com.android.libraries.pcc.chronicle.api.DataTypeDescriptorSet
24 import com.android.libraries.pcc.chronicle.api.ProcessorNode
25 import com.android.libraries.pcc.chronicle.api.cantrip.Cantrip
26 import com.android.libraries.pcc.chronicle.api.cantrip.MultiCantrip
27 import com.android.libraries.pcc.chronicle.api.cantrip.OpticalCantrip
28 import com.android.libraries.pcc.chronicle.api.operation.Operation
29 import com.android.libraries.pcc.chronicle.api.operation.OperationLibrary
30 import com.android.libraries.pcc.chronicle.api.optics.OpticalAccessPath
31 import com.android.libraries.pcc.chronicle.api.optics.OpticsManifest
32 import com.android.libraries.pcc.chronicle.api.policy.Policy
33 import com.android.libraries.pcc.chronicle.api.policy.PolicyField
34 import com.android.libraries.pcc.chronicle.api.policy.builder.UsageType
35 
36 /**
37  * Implementation of [CantripFactory] which composes its [Cantrips][Cantrip] using the provided
38  * [OpticsManifest] and [OperationLibrary].
39  *
40  * The [ChronicleContext] provides a means to look up the [DataTypeDescriptor] associated with the
41  * [ConnectionRequest.connectionType] of the [ConnectionRequest] passed to [buildCantrip].
42  */
43 class CantripFactoryImpl(
44   private val optics: OpticsManifest,
45   private val operations: OperationLibrary,
46   private val dtds: DataTypeDescriptorSet
47 ) : CantripFactory {
48 
49   @Suppress("UNCHECKED_CAST") // Types are checked via alternate mechanisms.
50   override fun <Data> buildCantrip(
51     dtd: DataTypeDescriptor,
52     requester: ProcessorNode,
53     policy: Policy?,
54     usageType: UsageType
55   ): Cantrip<Data> {
56     val noOp = MultiCantrip<Data>()
57     val policyTarget = policy?.targets?.find { it.schemaName == dtd.name } ?: return noOp
58 
59     val conditionalUsages =
60       policyTarget.fields.flatMap { collectConditionalUsages(dtd, it, usageType) }
61 
62     val innerCantrips =
63       conditionalUsages.map {
64         val dtdClass = dtd.cls.java
65         val traversal =
66           optics.composeTraversal(it.accessPath, dtdClass, dtdClass, it.fieldType, it.fieldType)
67         val op =
68           requireNotNull(operations.findOperation(it.tag, it.fieldType, it.fieldType)) {
69             "No Operation found with name: ${it.tag} for type: ${it.fieldType}"
70           }
71         OpticalCantrip(traversal, op as Operation<Any, Any>) as Cantrip<Data>
72       }
73 
74     return MultiCantrip(innerCantrips)
75   }
76 
77   /**
78    * Does a traversal to collect [ConditionalUsage] from the [PolicyField], composing a pre-order
79    * list as a result.
80    */
81   private fun collectConditionalUsages(
82     baseDtd: DataTypeDescriptor,
83     field: PolicyField,
84     usageType: UsageType
85   ): List<ConditionalUsage> {
86     val innerUsages = field.subfields.flatMap { collectConditionalUsages(baseDtd, it, usageType) }
87 
88     val pathToField = OpticalAccessPath(baseDtd, field.fieldPath)
89     val localUsages =
90       field.redactedUsages.entries
91         .filter { (_, types) -> usageType in types || UsageType.ANY in types }
92         .map {
93           ConditionalUsage(
94             accessPath = pathToField,
95             tag = it.key,
96             fieldType = dtds.findFieldTypeAsClass(baseDtd, field.fieldPath)
97           )
98         }
99 
100     return localUsages + innerUsages
101   }
102 
103   private data class ConditionalUsage(
104     val accessPath: OpticalAccessPath,
105     val tag: String,
106     val fieldType: Class<*>,
107   )
108 }
109