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