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