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 18 19 open class BaseItemVisitor( 20 /** 21 * Whether nested classes should be visited "inside" a class; when this property is true, nested 22 * classes are visited before the [#afterVisitClass] method is called; when false, it's done 23 * afterwards. Defaults to false. 24 */ 25 val preserveClassNesting: Boolean = false, 26 27 /** 28 * Determines whether this will visit [ParameterItem]s or not. 29 * 30 * If this is `true` then [ParameterItem]s will be visited, and passed to [visitItem], 31 * [visitParameter] and [afterVisitItem] in that order. Otherwise, they will not be visited. 32 * 33 * Defaults to `true` as that is the safest option which avoids inadvertently ignoring them. 34 */ 35 protected val visitParameterItems: Boolean = true, 36 ) : ItemVisitor { 37 /** Calls [visitItem] before invoking [body] after which it calls [afterVisitItem]. */ wrapBodyWithCallsToVisitMethodsForItemnull38 protected inline fun <T : Item> wrapBodyWithCallsToVisitMethodsForItem( 39 item: T, 40 body: () -> Unit 41 ) { 42 visitItem(item) 43 body() 44 afterVisitItem(item) 45 } 46 47 /** 48 * Calls [visitItem], then [visitSelectableItem] before invoking [body] after which it calls 49 * [afterVisitSelectableItem] and finally [afterVisitItem]. 50 */ wrapBodyWithCallsToVisitMethodsForSelectableItemnull51 protected inline fun <T : SelectableItem> wrapBodyWithCallsToVisitMethodsForSelectableItem( 52 item: T, 53 body: () -> Unit 54 ) { 55 wrapBodyWithCallsToVisitMethodsForItem(item) { 56 visitSelectableItem(item) 57 body() 58 afterVisitSelectableItem(item) 59 } 60 } 61 visitnull62 override fun visit(cls: ClassItem) { 63 if (skip(cls)) { 64 return 65 } 66 67 wrapBodyWithCallsToVisitMethodsForSelectableItem(cls) { 68 visitClass(cls) 69 70 for (constructor in cls.constructors()) { 71 constructor.accept(this) 72 } 73 74 for (method in cls.methods()) { 75 method.accept(this) 76 } 77 78 for (property in cls.properties()) { 79 property.accept(this) 80 } 81 82 if (cls.isEnum()) { 83 // In enums, visit the enum constants first, then the fields 84 for (field in cls.fields()) { 85 if (field.isEnumConstant()) { 86 field.accept(this) 87 } 88 } 89 for (field in cls.fields()) { 90 if (!field.isEnumConstant()) { 91 field.accept(this) 92 } 93 } 94 } else { 95 for (field in cls.fields()) { 96 field.accept(this) 97 } 98 } 99 100 if (preserveClassNesting) { 101 for (nestedCls in cls.nestedClasses()) { 102 nestedCls.accept(this) 103 } 104 } // otherwise done in visit(PackageItem) 105 106 afterVisitClass(cls) 107 } 108 } 109 visitnull110 override fun visit(field: FieldItem) { 111 if (skip(field)) { 112 return 113 } 114 115 wrapBodyWithCallsToVisitMethodsForSelectableItem(field) { visitField(field) } 116 } 117 visitnull118 override fun visit(constructor: ConstructorItem) { 119 visitMethodOrConstructor(constructor) { visitConstructor(it) } 120 } 121 visitnull122 override fun visit(method: MethodItem) { 123 visitMethodOrConstructor(method) { visitMethod(it) } 124 } 125 visitMethodOrConstructornull126 private inline fun <T : CallableItem> visitMethodOrConstructor( 127 callable: T, 128 dispatch: (T) -> Unit 129 ) { 130 if (skip(callable)) { 131 return 132 } 133 134 wrapBodyWithCallsToVisitMethodsForSelectableItem(callable) { 135 visitCallable(callable) 136 137 // Call the specific visitX method for the CallableItem subclass. 138 dispatch(callable) 139 140 if (visitParameterItems) { 141 for (parameter in callable.parameters()) { 142 parameter.accept(this) 143 } 144 } 145 } 146 } 147 148 /** 149 * Get the package's classes to visit directly. 150 * 151 * If nested classes are to appear as nested within their containing classes then this will just 152 * return the package's top level classes. It will then be the responsibility of 153 * `visit(ClassItem)` to visit the nested classes. Otherwise, this will return a flattened 154 * sequence of each class followed by its nested classes. 155 */ packageClassesAsSequencenull156 protected fun packageClassesAsSequence(pkg: PackageItem) = 157 if (preserveClassNesting) pkg.topLevelClasses().asSequence() else pkg.allClasses() 158 159 override fun visit(codebase: Codebase) { 160 visitCodebase(codebase) 161 codebase.getPackages().packages.forEach { it.accept(this) } 162 afterVisitCodebase(codebase) 163 } 164 visitnull165 override fun visit(pkg: PackageItem) { 166 if (skipPackage(pkg)) { 167 return 168 } 169 170 if (skip(pkg)) { 171 return 172 } 173 174 wrapBodyWithCallsToVisitMethodsForSelectableItem(pkg) { 175 visitPackage(pkg) 176 177 for (cls in packageClassesAsSequence(pkg)) { 178 cls.accept(this) 179 } 180 181 for (typeAlias in pkg.typeAliases()) { 182 typeAlias.accept(this) 183 } 184 185 afterVisitPackage(pkg) 186 } 187 } 188 189 /** 190 * Ignore any packages whose `emit` property is `false`. That is basically any package that does 191 * not contain at least one class that could be emitted as part of the API. 192 */ skipPackagenull193 open fun skipPackage(pkg: PackageItem) = !pkg.emit 194 195 override fun visit(parameter: ParameterItem) { 196 if (skip(parameter)) { 197 return 198 } 199 200 wrapBodyWithCallsToVisitMethodsForItem(parameter) { visitParameter(parameter) } 201 } 202 visitnull203 override fun visit(property: PropertyItem) { 204 if (skip(property)) { 205 return 206 } 207 208 wrapBodyWithCallsToVisitMethodsForSelectableItem(property) { visitProperty(property) } 209 } 210 visitnull211 override fun visit(typeAlias: TypeAliasItem) { 212 if (skip(typeAlias)) { 213 return 214 } 215 216 wrapBodyWithCallsToVisitMethodsForSelectableItem(typeAlias) { visitTypeAlias(typeAlias) } 217 } 218 skipnull219 open fun skip(item: Item): Boolean = false 220 221 /** 222 * Visits any [Item]. 223 * 224 * This is always called BEFORE other more specialized visit methods, such as [visitClass]. 225 */ 226 open fun visitItem(item: Item) {} 227 228 /** 229 * Visits any [SelectableItem], i.e. everything for which [visitItem] is called except 230 * [ParameterItem]s. 231 * 232 * This is always called BEFORE other more specialized visit methods, such as [visitClass]. 233 */ visitSelectableItemnull234 open fun visitSelectableItem(item: SelectableItem) {} 235 visitCodebasenull236 open fun visitCodebase(codebase: Codebase) {} 237 visitPackagenull238 open fun visitPackage(pkg: PackageItem) {} 239 visitClassnull240 open fun visitClass(cls: ClassItem) {} 241 visitCallablenull242 open fun visitCallable(callable: CallableItem) {} 243 visitConstructornull244 open fun visitConstructor(constructor: ConstructorItem) {} 245 visitMethodnull246 open fun visitMethod(method: MethodItem) {} 247 visitFieldnull248 open fun visitField(field: FieldItem) {} 249 250 /** Visits a [ParameterItem]. */ visitParameternull251 open fun visitParameter(parameter: ParameterItem) {} 252 visitPropertynull253 open fun visitProperty(property: PropertyItem) {} 254 visitTypeAliasnull255 open fun visitTypeAlias(typeAlias: TypeAliasItem) {} 256 257 /** 258 * Visits any [SelectableItem], i.e. everything for which [afterVisitItem] is called except 259 * [ParameterItem]s. 260 * 261 * This is always called AFTER other more specialized visit methods, such as [afterVisitClass]. 262 */ afterVisitSelectableItemnull263 open fun afterVisitSelectableItem(item: SelectableItem) {} 264 265 /** 266 * Visits any [Item], except for [TypeParameterItem]. 267 * 268 * This is always called AFTER other more specialized visit methods, such as [afterVisitClass]. 269 */ afterVisitItemnull270 open fun afterVisitItem(item: Item) {} 271 afterVisitCodebasenull272 open fun afterVisitCodebase(codebase: Codebase) {} 273 afterVisitPackagenull274 open fun afterVisitPackage(pkg: PackageItem) {} 275 afterVisitClassnull276 open fun afterVisitClass(cls: ClassItem) {} 277 } 278