1 /* 2 * Copyright (C) 2017 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.ApiPredicate 20 import com.android.tools.metalava.Options 21 import com.android.tools.metalava.model.ClassItem 22 import com.android.tools.metalava.model.FieldItem 23 import com.android.tools.metalava.model.Item 24 import com.android.tools.metalava.model.MethodItem 25 import com.android.tools.metalava.model.VisitCandidate 26 import com.android.tools.metalava.options 27 import java.util.function.Predicate 28 29 open class ApiVisitor( 30 /** 31 * Whether constructors should be visited as part of a [#visitMethod] call 32 * instead of just a [#visitConstructor] call. Helps simplify visitors that 33 * don't care to distinguish between the two cases. Defaults to true. 34 */ 35 visitConstructorsAsMethods: Boolean = true, 36 /** 37 * Whether inner classes should be visited "inside" a class; when this property 38 * is true, inner classes are visited before the [#afterVisitClass] method is 39 * called; when false, it's done afterwards. Defaults to false. 40 */ 41 nestInnerClasses: Boolean = false, 42 43 /** 44 * Whether to include inherited fields too 45 */ 46 val inlineInheritedFields: Boolean = true, 47 48 /** Comparator to sort methods with, or null to use natural (source) order */ 49 val methodComparator: Comparator<MethodItem>? = null, 50 51 /** Comparator to sort fields with, or null to use natural (source) order */ 52 val fieldComparator: Comparator<FieldItem>? = null, 53 54 /** The filter to use to determine if we should emit an item */ 55 val filterEmit: Predicate<Item>, 56 57 /** The filter to use to determine if we should emit a reference to an item */ 58 val filterReference: Predicate<Item>, 59 60 /** 61 * Whether the visitor should include visiting top-level classes that have 62 * nothing other than non-empty inner classes within. 63 * Typically these are not included in signature files, but when generating 64 * stubs we need to include them. 65 */ 66 val includeEmptyOuterClasses: Boolean = false, 67 68 /** 69 * Whether this visitor should visit elements that have not been 70 * annotated with one of the annotations passed in using the 71 * --show-annotation flag. This is normally true, but signature files 72 * sometimes sets this to false to make the signature file only contain 73 * the "diff" of the annotated API relative to the base API. 74 */ 75 val showUnannotated: Boolean = true 76 ) : ItemVisitor(visitConstructorsAsMethods, nestInnerClasses) { 77 constructor( 78 /** 79 * Whether constructors should be visited as part of a [#visitMethod] call 80 * instead of just a [#visitConstructor] call. Helps simplify visitors that 81 * don't care to distinguish between the two cases. Defaults to true. 82 */ 83 visitConstructorsAsMethods: Boolean = true, 84 /** 85 * Whether inner classes should be visited "inside" a class; when this property 86 * is true, inner classes are visited before the [#afterVisitClass] method is 87 * called; when false, it's done afterwards. Defaults to false. 88 */ 89 nestInnerClasses: Boolean = false, 90 91 /** Whether to ignore APIs with annotations in the --show-annotations list */ 92 ignoreShown: Boolean = true, 93 94 /** Whether to match APIs marked for removal instead of the normal API */ 95 remove: Boolean = false, 96 97 /** Comparator to sort methods with, or null to use natural (source) order */ 98 methodComparator: Comparator<MethodItem>? = null, 99 100 /** Comparator to sort fields with, or null to use natural (source) order */ 101 fieldComparator: Comparator<FieldItem>? = null, 102 103 /** Whether to include "for stub purposes" APIs. See [Options.showForStubPurposesAnnotations] */ 104 includeApisForStubPurposes: Boolean = true 105 ) : this( 106 visitConstructorsAsMethods, nestInnerClasses, 107 true, methodComparator, 108 fieldComparator, 109 ApiPredicate(ignoreShown = ignoreShown, matchRemoved = remove, includeApisForStubPurposes = includeApisForStubPurposes), 110 ApiPredicate(ignoreShown = true, ignoreRemoved = remove) 111 ) 112 113 // The API visitor lazily visits packages only when there's a match within at least one class; 114 // this property keeps track of whether we've already visited the current package 115 var visitingPackage = false 116 117 /** 118 * @return Whether this class is generally one that we want to recurse into 119 */ includenull120 open fun include(cls: ClassItem): Boolean { 121 if (skip(cls)) { 122 return false 123 } 124 val filter = options.stubPackages 125 if (filter != null && !filter.matches(cls.containingPackage())) { 126 return false 127 } 128 129 return cls.emit || cls.codebase.preFiltered 130 } 131 132 /** 133 * @return Whether the given VisitCandidate's visitor should recurse into the given 134 * VisitCandidate's class 135 */ includenull136 fun include(vc: VisitCandidate): Boolean { 137 if (!include(vc.cls)) { 138 return false 139 } 140 return shouldEmitClassBody(vc) || shouldEmitInnerClasses(vc) 141 } 142 143 /** 144 * @return Whether this class should be visited 145 * Note that if [include] returns true then we will still visit classes that are contained by this one 146 */ shouldEmitClassnull147 open fun shouldEmitClass(vc: VisitCandidate): Boolean { 148 return vc.cls.emit && (includeEmptyOuterClasses || shouldEmitClassBody(vc)) 149 } 150 151 /** 152 * @return Whether the body of this class (everything other than the inner classes) emits anything 153 */ shouldEmitClassBodynull154 private fun shouldEmitClassBody(vc: VisitCandidate): Boolean { 155 return when { 156 filterEmit.test(vc.cls) -> true 157 vc.nonEmpty() -> filterReference.test(vc.cls) 158 else -> false 159 } 160 } 161 162 /** 163 * @return Whether the inner classes of this class will emit anything 164 */ shouldEmitInnerClassesnull165 fun shouldEmitInnerClasses(vc: VisitCandidate): Boolean { 166 return vc.innerClasses.any { shouldEmitAnyClass(it) } 167 } 168 169 /** 170 * @return Whether this class will emit anything 171 */ shouldEmitAnyClassnull172 private fun shouldEmitAnyClass(vc: VisitCandidate): Boolean { 173 return shouldEmitClassBody(vc) || shouldEmitInnerClasses(vc) 174 } 175 } 176