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 // Ignore any packages whose `emit` property is `false`. That is basically any package that 167 // does not contain at least one class that could be emitted as part of the API. 168 if (!pkg.emit) { 169 return 170 } 171 172 if (skip(pkg)) { 173 return 174 } 175 176 wrapBodyWithCallsToVisitMethodsForSelectableItem(pkg) { 177 visitPackage(pkg) 178 179 for (cls in packageClassesAsSequence(pkg)) { 180 cls.accept(this) 181 } 182 183 afterVisitPackage(pkg) 184 } 185 } 186 visitnull187 override fun visit(parameter: ParameterItem) { 188 if (skip(parameter)) { 189 return 190 } 191 192 wrapBodyWithCallsToVisitMethodsForItem(parameter) { visitParameter(parameter) } 193 } 194 visitnull195 override fun visit(property: PropertyItem) { 196 if (skip(property)) { 197 return 198 } 199 200 wrapBodyWithCallsToVisitMethodsForSelectableItem(property) { visitProperty(property) } 201 } 202 skipnull203 open fun skip(item: Item): Boolean = false 204 205 /** 206 * Visits any [Item]. 207 * 208 * This is always called BEFORE other more specialized visit methods, such as [visitClass]. 209 */ 210 open fun visitItem(item: Item) {} 211 212 /** 213 * Visits any [SelectableItem], i.e. everything for which [visitItem] is called except 214 * [ParameterItem]s. 215 * 216 * This is always called BEFORE other more specialized visit methods, such as [visitClass]. 217 */ visitSelectableItemnull218 open fun visitSelectableItem(item: SelectableItem) {} 219 visitCodebasenull220 open fun visitCodebase(codebase: Codebase) {} 221 visitPackagenull222 open fun visitPackage(pkg: PackageItem) {} 223 visitClassnull224 open fun visitClass(cls: ClassItem) {} 225 visitCallablenull226 open fun visitCallable(callable: CallableItem) {} 227 visitConstructornull228 open fun visitConstructor(constructor: ConstructorItem) {} 229 visitMethodnull230 open fun visitMethod(method: MethodItem) {} 231 visitFieldnull232 open fun visitField(field: FieldItem) {} 233 234 /** Visits a [ParameterItem]. */ visitParameternull235 open fun visitParameter(parameter: ParameterItem) {} 236 visitPropertynull237 open fun visitProperty(property: PropertyItem) {} 238 239 /** 240 * Visits any [SelectableItem], i.e. everything for which [afterVisitItem] is called except 241 * [ParameterItem]s. 242 * 243 * This is always called AFTER other more specialized visit methods, such as [afterVisitClass]. 244 */ afterVisitSelectableItemnull245 open fun afterVisitSelectableItem(item: SelectableItem) {} 246 247 /** 248 * Visits any [Item], except for [TypeParameterItem]. 249 * 250 * This is always called AFTER other more specialized visit methods, such as [afterVisitClass]. 251 */ afterVisitItemnull252 open fun afterVisitItem(item: Item) {} 253 afterVisitCodebasenull254 open fun afterVisitCodebase(codebase: Codebase) {} 255 afterVisitPackagenull256 open fun afterVisitPackage(pkg: PackageItem) {} 257 afterVisitClassnull258 open fun afterVisitClass(cls: ClassItem) {} 259 } 260