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