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