• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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