• 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 package com.android.tools.metalava.apilevels
17 
18 import com.google.common.collect.Iterables
19 
20 /**
21  * Represents a class or an interface and its methods/fields. This is used to write the simplified
22  * XML file containing all the public API.
23  */
24 class ApiClass(name: String) : ApiElement(name) {
25 
26     private val mSuperClasses = mutableMapOf<String, ApiElement>()
27     private val mInterfaces = mutableMapOf<String, ApiElement>()
28 
29     /** If `true`, never seen as public. */
30     var alwaysHidden = false // Package private class?
31     private val mFields = mutableMapOf<String, ApiElement>()
32     private val mMethods = mutableMapOf<String, ApiElement>()
33 
34     /**
35      * Updates the [ApiElement] for field with [name], creating and adding one if necessary.
36      *
37      * @param name the name of the field.
38      * @param updater the [ApiHistoryUpdater] that will update the element with information about
39      *   the version to which it belongs.
40      * @param deprecated the deprecated status.
41      */
updateFieldnull42     fun updateField(
43         name: String,
44         updater: ApiHistoryUpdater,
45         deprecated: Boolean,
46     ): ApiElement {
47         return updateElementInMap(mFields, name, updater, deprecated)
48     }
49 
50     val fields: Collection<ApiElement>
51         get() = mFields.values
52 
53     /**
54      * Updates the [ApiElement] for method with [signature], creating and adding one if necessary.
55      *
56      * @param signature the signature of the method, which includes the name and parameter/return
57      *   types
58      * @param updater the [ApiHistoryUpdater] that will update the element with information about
59      *   the version to which it belongs.
60      * @param deprecated the deprecated status.
61      */
updateMethodnull62     fun updateMethod(
63         signature: String,
64         updater: ApiHistoryUpdater,
65         deprecated: Boolean,
66     ): ApiElement {
67         // Correct historical mistake in android.jar files
68         var correctedName = signature
69         if (correctedName.endsWith(")Ljava/lang/AbstractStringBuilder;")) {
70             correctedName =
71                 correctedName.substring(
72                     0,
73                     correctedName.length - ")Ljava/lang/AbstractStringBuilder;".length
74                 ) + ")L" + this.name + ";"
75         }
76         return updateElementInMap(mMethods, correctedName, updater, deprecated)
77     }
78 
79     val methods: Collection<ApiElement>
80         get() = mMethods.values
81 
82     /**
83      * Updates an element for [superClassType], creating and adding one if necessary.
84      *
85      * @param superClassType the name of the super class type.
86      * @param updater the [ApiHistoryUpdater] that will update the element with information about
87      *   the version to which it belongs.
88      */
updateSuperClassnull89     fun updateSuperClass(superClassType: String, updater: ApiHistoryUpdater) =
90         updateElementInMap(
91             mSuperClasses,
92             superClassType,
93             updater,
94             // References to super classes can never be deprecated.
95             false,
96         )
97 
98     val superClasses: Collection<ApiElement>
99         get() = mSuperClasses.values
100 
101     fun updateHidden(hidden: Boolean) {
102         alwaysHidden = hidden
103     }
104 
105     /**
106      * Updates an element for [interfaceType], creating and adding one if necessary.
107      *
108      * @param interfaceType the interface type.
109      * @param updater the [ApiHistoryUpdater] that will update the element with information about
110      *   the version to which it belongs.
111      */
updateInterfacenull112     fun updateInterface(interfaceType: String, updater: ApiHistoryUpdater) =
113         updateElementInMap(
114             mInterfaces,
115             interfaceType,
116             updater,
117             // References to interfaces can never be deprecated.
118             false,
119         )
120 
121     val interfaces: Collection<ApiElement>
122         get() = mInterfaces.values
123 
124     private fun updateElementInMap(
125         elements: MutableMap<String, ApiElement>,
126         name: String,
127         updater: ApiHistoryUpdater,
128         deprecated: Boolean,
129     ): ApiElement {
130         val existing = elements[name]
131         val element = existing ?: ApiElement(name).apply { elements[name] = this }
132         updater.update(element, deprecated)
133         return element
134     }
135 
136     /**
137      * Removes all interfaces that are also implemented by superclasses or extended by interfaces
138      * this class implements.
139      *
140      * @param allClasses all classes keyed by their names.
141      */
removeImplicitInterfacesnull142     fun removeImplicitInterfaces(allClasses: Map<String, ApiClass>) {
143         if (mInterfaces.isEmpty() || mSuperClasses.isEmpty()) {
144             return
145         }
146         val iterator = mInterfaces.values.iterator()
147         while (iterator.hasNext()) {
148             val interfaceElement = iterator.next()
149             for (superClass in superClasses) {
150                 if (superClass.introducedNotLaterThan(interfaceElement)) {
151                     val cls = allClasses[superClass.name]
152                     if (cls != null && cls.implementsInterface(interfaceElement, allClasses)) {
153                         iterator.remove()
154                         break
155                     }
156                 }
157             }
158         }
159     }
160 
implementsInterfacenull161     private fun implementsInterface(
162         interfaceElement: ApiElement,
163         allClasses: Map<String, ApiClass>
164     ): Boolean {
165         for (localInterface in interfaces) {
166             if (localInterface.introducedNotLaterThan(interfaceElement)) {
167                 if (interfaceElement.name == localInterface.name) {
168                     return true
169                 }
170                 // Check parent interface.
171                 val cls = allClasses[localInterface.name]
172                 if (cls != null && cls.implementsInterface(interfaceElement, allClasses)) {
173                     return true
174                 }
175             }
176         }
177         for (superClass in superClasses) {
178             if (superClass.introducedNotLaterThan(interfaceElement)) {
179                 val cls = allClasses[superClass.name]
180                 if (cls != null && cls.implementsInterface(interfaceElement, allClasses)) {
181                     return true
182                 }
183             }
184         }
185         return false
186     }
187 
188     /**
189      * Removes all methods that override method declared by superclasses and interfaces of this
190      * class.
191      *
192      * @param allClasses all classes keyed by their names.
193      */
removeOverridingMethodsnull194     fun removeOverridingMethods(allClasses: Map<String, ApiClass>) {
195         val it: MutableIterator<Map.Entry<String, ApiElement>> = mMethods.entries.iterator()
196         while (it.hasNext()) {
197             val (_, method) = it.next()
198             if (!method.name.startsWith("<init>(") && isOverrideOfInherited(method, allClasses)) {
199                 it.remove()
200             }
201         }
202     }
203 
204     /**
205      * Checks if the given method overrides one of the methods defined by this class or its
206      * superclasses or interfaces.
207      *
208      * @param method the method to check
209      * @param allClasses the map containing all API classes
210      * @return true if the method is an override
211      */
isOverridenull212     private fun isOverride(method: ApiElement, allClasses: Map<String, ApiClass>): Boolean {
213         val name = method.name
214         val localMethod = mMethods[name]
215         return if (localMethod != null && localMethod.introducedNotLaterThan(method)) {
216             // This class has the method, and it was introduced in at the same api level
217             // as the child method, or before.
218             true
219         } else {
220             isOverrideOfInherited(method, allClasses)
221         }
222     }
223 
224     /**
225      * Checks if the given method overrides one of the methods declared by ancestors of this class.
226      */
isOverrideOfInheritednull227     private fun isOverrideOfInherited(
228         method: ApiElement,
229         allClasses: Map<String, ApiClass>
230     ): Boolean {
231         // Check this class' parents.
232         for (parent in Iterables.concat(superClasses, interfaces)) {
233             // Only check the parent if it was a parent class at the introduction of the method.
234             if (parent!!.introducedNotLaterThan(method)) {
235                 val cls = allClasses[parent.name]
236                 if (cls != null && cls.isOverride(method, allClasses)) {
237                     return true
238                 }
239             }
240         }
241         return false
242     }
243 
244     private var haveInlined = false
245 
inlineFromHiddenSuperClassesnull246     fun inlineFromHiddenSuperClasses(hidden: Map<String, ApiClass>) {
247         if (haveInlined) {
248             return
249         }
250         haveInlined = true
251         for (superClass in superClasses) {
252             val hiddenSuper = hidden[superClass.name]
253             if (hiddenSuper != null) {
254                 hiddenSuper.inlineFromHiddenSuperClasses(hidden)
255                 val myMethods = mMethods
256                 val myFields = mFields
257                 for ((name, value) in hiddenSuper.mMethods) {
258                     if (!myMethods.containsKey(name)) {
259                         myMethods[name] = value
260                     }
261                 }
262                 for ((name, value) in hiddenSuper.mFields) {
263                     if (!myFields.containsKey(name)) {
264                         myFields[name] = value
265                     }
266                 }
267             }
268         }
269     }
270 
removeHiddenSuperClassesnull271     fun removeHiddenSuperClasses(api: Map<String, ApiClass>) {
272         // If we've included a package private class in the super class map (from the older
273         // android.jar files)
274         // remove these here and replace with the filtered super classes, updating API levels in the
275         // process
276         val iterator = mSuperClasses.values.iterator()
277         var min = ApiVersion.HIGHEST
278         while (iterator.hasNext()) {
279             val next = iterator.next()
280             min = minOf(min, next.since)
281             val extendsClass = api[next.name]
282             if (extendsClass != null && extendsClass.alwaysHidden) {
283                 val since = extendsClass.since
284                 iterator.remove()
285                 for (other in superClasses) {
286                     if (other.since >= since) {
287                         other.update(min)
288                     }
289                 }
290                 break
291             }
292         }
293     }
294 
295     // Ensure this class doesn't extend/implement any other classes/interfaces that are
296     // not in the provided api. This can happen when a class in an android.jar file
297     // encodes the inheritance, but the class that is inherited is not present in any
298     // android.jar file. The class would instead be present in an apex's stub jar file.
299     // An example of this is the QosSessionAttributes interface being provided by the
300     // Connectivity apex, but being implemented by NrQosSessionAttributes from
301     // frameworks/base/telephony.
removeMissingClassesnull302     fun removeMissingClasses(api: Map<String, ApiClass>) {
303         val superClassIter = mSuperClasses.values.iterator()
304         while (superClassIter.hasNext()) {
305             val superClass = superClassIter.next()
306             if (!api.containsKey(superClass.name)) {
307                 superClassIter.remove()
308             }
309         }
310         val interfacesIter = mInterfaces.values.iterator()
311         while (interfacesIter.hasNext()) {
312             val intf = interfacesIter.next()
313             if (!api.containsKey(intf.name)) {
314                 interfacesIter.remove()
315             }
316         }
317     }
318 
319     // Returns the set of superclasses or interfaces are not present in the provided api map
findMissingClassesnull320     fun findMissingClasses(api: Map<String, ApiClass>): Set<ApiElement> {
321         val result: MutableSet<ApiElement> = HashSet()
322         for (superClass in superClasses) {
323             if (!api.containsKey(superClass.name)) {
324                 result.add(superClass)
325             }
326         }
327         for (intf in interfaces) {
328             if (!api.containsKey(intf.name)) {
329                 result.add(intf)
330             }
331         }
332         return result
333     }
334 }
335