1 /*
2  * Copyright (C) 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 com.android.tools.metalava.model.visitors
18 
19 import com.android.tools.metalava.model.CallableItem
20 import com.android.tools.metalava.model.ClassItem
21 import com.android.tools.metalava.model.ClassTypeItem
22 import com.android.tools.metalava.model.Codebase
23 import com.android.tools.metalava.model.ConstructorItem
24 import com.android.tools.metalava.model.DelegatedVisitor
25 import com.android.tools.metalava.model.ExceptionTypeItem
26 import com.android.tools.metalava.model.FieldItem
27 import com.android.tools.metalava.model.Item
28 import com.android.tools.metalava.model.ItemVisitor
29 import com.android.tools.metalava.model.MethodItem
30 import com.android.tools.metalava.model.PackageItem
31 import com.android.tools.metalava.model.ParameterItem
32 import com.android.tools.metalava.model.PropertyItem
33 import com.android.tools.metalava.model.SourceFile
34 import com.android.tools.metalava.model.TypeItem
35 import com.android.tools.metalava.model.TypeTransformer
36 import com.android.tools.metalava.model.typeUseAnnotationFilter
37 
38 /**
39  * An [ApiVisitor] that filters the input and forwards it to the [delegate] [ItemVisitor].
40  *
41  * This defines a number of `Filtering*Item` classes that will filter out any [Item] references for
42  * which [filterReference] returns false. They are not suitable for general use. Their sole purpose
43  * is to provide enough functionality for use when writing a representation of the item, e.g. for
44  * signatures, stubs, etc. That means that there may be some methods that are not use by those
45  * writers which will allow access to unfiltered `Item`s.
46  *
47  * Preserves class nesting as required by the [delegate]'s [DelegatedVisitor.requiresClassNesting]
48  * property.
49  */
50 class FilteringApiVisitor(
51     val delegate: DelegatedVisitor,
52     inlineInheritedFields: Boolean = true,
53     callableComparator: Comparator<CallableItem> = CallableItem.comparator,
54     /**
55      * Optional lambda for sorting the filtered, list of interface types from a [ClassItem].
56      *
57      * This will only be called if the filtered list contains 2 or more elements.
58      *
59      * This is provided primarily to allow usages where the interface order cannot be enforced by
60      * [interfaceListComparator]. In that case this should be provided and [interfaceListComparator]
61      * should be left unspecified so that the order of the list returned by this is unchanged.
62      *
63      * If this is `null` then it will behave as if it just returned the filtered interface types it
64      * was passed.
65      *
66      * This is mutually exclusive with [interfaceListComparator].
67      */
68     private val interfaceListSorter:
69         ((ClassItem, List<ClassTypeItem>, List<ClassTypeItem>) -> List<ClassTypeItem>)? =
70         null,
71     /**
72      * Optional comparator to use for sorting interface list types.
73      *
74      * This is mutually exclusive with [interfaceListSorter].
75      */
76     private val interfaceListComparator: Comparator<TypeItem>? = null,
77     apiFilters: ApiFilters,
78     private val preFiltered: Boolean,
79     private val filterSuperClassType: Boolean = true,
80     showUnannotated: Boolean = true,
81     private val ignoreEmit: Boolean = false,
82 ) :
83     ApiVisitor(
84         preserveClassNesting = delegate.requiresClassNesting,
85         // Only `SelectableItem`s can be filtered separately, i.e. `ParameterItem`s will be included
86         // if and only if their containing method is included.
87         visitParameterItems = false,
88         inlineInheritedFields = inlineInheritedFields,
89         callableComparator = callableComparator,
90         apiFilters = apiFilters,
91         showUnannotated = showUnannotated,
92     ),
93     ItemVisitor {
94 
95     /**
96      * A [TypeTransformer] that will remove any type annotations for which [filterReference] returns
97      * false when called against the annotation's [ClassItem].
98      */
99     private val typeAnnotationFilter = typeUseAnnotationFilter(filterReference)
100 
visitCodebasenull101     override fun visitCodebase(codebase: Codebase) {
102         // This does not create a filtering wrapper around the Codebase as the classes to which this
103         // currently delegates do not access any fields within the Codebase.
104         delegate.visitCodebase(codebase)
105     }
106 
afterVisitCodebasenull107     override fun afterVisitCodebase(codebase: Codebase) {
108         // This does not create a filtering wrapper around the Codebase as the classes to which this
109         // currently delegates do not access any fields within the Codebase.
110         delegate.afterVisitCodebase(codebase)
111     }
112 
visitPackagenull113     override fun visitPackage(pkg: PackageItem) {
114         delegate.visitPackage(pkg)
115     }
116 
afterVisitPackagenull117     override fun afterVisitPackage(pkg: PackageItem) {
118         delegate.afterVisitPackage(pkg)
119     }
120 
121     /** Stack of the containing classes. */
122     private val containingClassStack = ArrayDeque<FilteringClassItem?>()
123 
124     /** The current [ClassItem] being visited, */
125     private var currentClassItem: FilteringClassItem? = null
126 
includenull127     override fun include(cls: ClassItem): Boolean {
128         return ignoreEmit || cls.emit
129     }
130 
visitClassnull131     override fun visitClass(cls: ClassItem) {
132         // Switch the current class, if any, to be a containing class.
133         containingClassStack.addLast(currentClassItem)
134 
135         // Create a new FilteringClassItem for the current class and visit it before its contents.
136         currentClassItem = FilteringClassItem(delegate = cls)
137         delegate.visitClass(currentClassItem!!)
138     }
139 
afterVisitClassnull140     override fun afterVisitClass(cls: ClassItem) {
141         // Consistency check to make sure that the visitClass/afterVisitClass are called correctly.
142         if (currentClassItem?.delegate !== cls)
143             throw IllegalStateException("Expected ${currentClassItem?.delegate}, found ${cls}")
144 
145         // Visit the class after its contents.
146         delegate.afterVisitClass(currentClassItem!!)
147 
148         // Switch back to the containing class, if any.
149         currentClassItem = containingClassStack.removeLast()
150     }
151 
visitConstructornull152     override fun visitConstructor(constructor: ConstructorItem) {
153         val filteringConstructor = FilteringConstructorItem(constructor)
154         delegate.visitConstructor(filteringConstructor)
155     }
156 
visitMethodnull157     override fun visitMethod(method: MethodItem) {
158         val filteringMethod = FilteringMethodItem(method)
159         delegate.visitMethod(filteringMethod)
160     }
161 
visitFieldnull162     override fun visitField(field: FieldItem) {
163         val filteringField = FilteringFieldItem(field)
164         delegate.visitField(filteringField)
165     }
166 
visitPropertynull167     override fun visitProperty(property: PropertyItem) {
168         val filteringProperty = FilteringPropertyItem(property)
169         delegate.visitProperty(filteringProperty)
170     }
171 
172     /**
173      * [SourceFile] that will filter out anything which is not to be written out by the
174      * [FilteringApiVisitor.delegate].
175      */
<lambda>null176     private inner class FilteringSourceFile(val delegate: SourceFile) : SourceFile by delegate {
177 
178         override fun getImports() = delegate.getImports(filterReference)
179     }
180 
181     /**
182      * [ClassItem] that will filter out anything which is not to be written out by the
183      * [FilteringApiVisitor.delegate].
184      */
185     private inner class FilteringClassItem(
186         val delegate: ClassItem,
<lambda>null187     ) : ClassItem by delegate {
188 
189         override fun sourceFile() = delegate.sourceFile()?.let { FilteringSourceFile(it) }
190 
191         override fun superClass() = superClassType()?.asClass()
192 
193         override fun superClassType() =
194             if (!filterSuperClassType || preFiltered) delegate.superClassType()
195             else delegate.filteredSuperClassType(filterReference)?.transform(typeAnnotationFilter)
196 
197         override fun interfaceTypes(): List<ClassTypeItem> {
198             // Get the filtered list from the delegate.
199             val filtered =
200                 if (preFiltered) delegate.interfaceTypes()
201                 else delegate.filteredInterfaceTypes(filterReference).toList()
202 
203             // If the list is empty then nothing else is needed.
204             if (filtered.isEmpty()) return emptyList()
205 
206             // Order the list.
207             val ordered =
208                 when {
209                     // 0. If the list only has 1 element then it does not need sorting
210                     filtered.size == 1 -> filtered
211 
212                     // 1. Use the custom sorter, if available.
213                     interfaceListSorter != null -> {
214                         // Make sure a interfaceListComparator was not provided as well.
215                         interfaceListComparator?.let {
216                             error(
217                                 "Cannot specify both interfaceListSorter and interfaceListComparator"
218                             )
219                         }
220 
221                         // Get the unfiltered lists from the delegate.
222                         val unfiltered =
223                             if (preFiltered) {
224                                 // If pre-filtered then the filtered and unfiltered are the
225                                 // same.
226                                 filtered
227                             } else delegate.interfaceTypes()
228 
229                         interfaceListSorter.invoke(delegate, filtered, unfiltered)
230                     }
231 
232                     // 2. Sort using the comparator, if available.
233                     interfaceListComparator != null -> {
234                         filtered.sortedWith(interfaceListComparator)
235                     }
236 
237                     // 3. Preserve the input order.
238                     else -> filtered
239                 }
240 
241             // If required then filter annotation types from the ordered list before returning.
242             return if (preFiltered) ordered
243             else
244                 ordered.map {
245                     // Filter any inaccessible annotations from the interfaces
246                     it.transform(typeAnnotationFilter)
247                 }
248         }
249 
250         override fun constructors() =
251             delegate
252                 .filteredConstructors(filterReference)
253                 .map { FilteringConstructorItem(it) }
254                 .toList()
255 
256         override fun fields(): List<FieldItem> =
257             delegate.filteredFields(filterReference, showUnannotated).map { FilteringFieldItem(it) }
258     }
259 
260     /**
261      * [ParameterItem] that will filter out anything which is not to be written out by the
262      * [FilteringApiVisitor.delegate].
263      */
264     private inner class FilteringParameterItem(private val delegate: ParameterItem) :
<lambda>null265         ParameterItem by delegate {
266 
267         override fun type() = delegate.type().transform(typeAnnotationFilter)
268     }
269 
270     /** Get the [MethodItem.returnType] and apply the [typeAnnotationFilter] to it. */
filteredReturnTypenull271     fun filteredReturnType(callableItem: CallableItem) =
272         callableItem.returnType().transform(typeAnnotationFilter)
273 
274     /** Get the [MethodItem.parameters] and wrap each one in a [FilteringParameterItem]. */
275     fun filteredParameters(callableItem: CallableItem): List<ParameterItem> =
276         callableItem.parameters().map { FilteringParameterItem(it) }
277 
278     /**
279      * Get the [MethodItem.filteredThrowsTypes] and apply [typeAnnotationFilter] to each
280      * [ExceptionTypeItem] in the list.
281      */
filteredThrowsTypesnull282     private fun filteredThrowsTypes(callableItem: CallableItem) =
283         if (preFiltered) callableItem.throwsTypes()
284         else
285             callableItem.filteredThrowsTypes(filterReference).map {
286                 it.transform(typeAnnotationFilter)
287             }
288 
289     /**
290      * [ConstructorItem] that will filter out anything which is not to be written out by the
291      * [FilteringApiVisitor.delegate].
292      */
293     private inner class FilteringConstructorItem(private val delegate: ConstructorItem) :
<lambda>null294         ConstructorItem by delegate {
295 
296         override fun containingClass() = FilteringClassItem(delegate.containingClass())
297 
298         override fun returnType() = filteredReturnType(delegate) as ClassTypeItem
299 
300         override fun parameters() = filteredParameters(delegate)
301 
302         override fun throwsTypes() = filteredThrowsTypes(delegate)
303     }
304 
305     /**
306      * [MethodItem] that will filter out anything which is not to be written out by the
307      * [FilteringApiVisitor.delegate].
308      */
309     private inner class FilteringMethodItem(private val delegate: MethodItem) :
<lambda>null310         MethodItem by delegate {
311 
312         override fun returnType() = filteredReturnType(delegate)
313 
314         override fun parameters() = filteredParameters(delegate)
315 
316         override fun throwsTypes() = filteredThrowsTypes(delegate)
317     }
318 
319     /**
320      * [FieldItem] that will filter out anything which is not to be written out by the
321      * [FilteringApiVisitor.delegate].
322      */
323     private inner class FilteringFieldItem(private val delegate: FieldItem) :
<lambda>null324         FieldItem by delegate {
325 
326         override fun type() = delegate.type().transform(typeAnnotationFilter)
327     }
328 
329     /**
330      * [PropertyItem] that will filter out anything which is not to be written out by the
331      * [FilteringApiVisitor.delegate].
332      */
333     private inner class FilteringPropertyItem(private val delegate: PropertyItem) :
<lambda>null334         PropertyItem by delegate {
335 
336         override fun type() = delegate.type().transform(typeAnnotationFilter)
337     }
338 }
339