• 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.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