1 /* <lambda>null2 * 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.text 18 19 import com.android.tools.metalava.model.AnnotationRetention 20 import com.android.tools.metalava.model.ClassItem 21 import com.android.tools.metalava.model.ConstructorItem 22 import com.android.tools.metalava.model.DefaultModifierList 23 import com.android.tools.metalava.model.FieldItem 24 import com.android.tools.metalava.model.Item 25 import com.android.tools.metalava.model.MethodItem 26 import com.android.tools.metalava.model.PackageItem 27 import com.android.tools.metalava.model.PropertyItem 28 import com.android.tools.metalava.model.TypeItem 29 import com.android.tools.metalava.model.TypeParameterItem 30 import com.android.tools.metalava.model.TypeParameterList 31 import com.android.tools.metalava.model.TypeParameterListOwner 32 import java.util.function.Predicate 33 34 open class TextClassItem( 35 override val codebase: TextCodebase, 36 position: SourcePositionInfo = SourcePositionInfo.UNKNOWN, 37 modifiers: TextModifiers, 38 private var isInterface: Boolean = false, 39 private var isEnum: Boolean = false, 40 private var isAnnotation: Boolean = false, 41 val qualifiedName: String = "", 42 private val qualifiedTypeName: String = qualifiedName, 43 var name: String = qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1), 44 val annotations: List<String>? = null 45 ) : TextItem( 46 codebase = codebase, 47 position = position, 48 modifiers = modifiers 49 ), 50 ClassItem, 51 TypeParameterListOwner { 52 53 init { 54 @Suppress("LeakingThis") 55 modifiers.setOwner(this) 56 } 57 58 override val isTypeParameter: Boolean = false 59 60 override var artifact: String? = null 61 62 override fun equals(other: Any?): Boolean { 63 if (this === other) return true 64 if (other !is ClassItem) return false 65 66 return qualifiedName == other.qualifiedName() 67 } 68 69 override fun hashCode(): Int { 70 return qualifiedName.hashCode() 71 } 72 73 override fun interfaceTypes(): List<TypeItem> = interfaceTypes 74 override fun allInterfaces(): Sequence<ClassItem> { 75 return interfaceTypes.asSequence().map { it.asClass() }.filterNotNull() 76 } 77 78 private var innerClasses: MutableList<ClassItem> = mutableListOf() 79 80 override var stubConstructor: ConstructorItem? = null 81 82 override var hasPrivateConstructor: Boolean = false 83 84 override fun innerClasses(): List<ClassItem> = innerClasses 85 86 override fun hasImplicitDefaultConstructor(): Boolean { 87 return false 88 } 89 90 override fun isInterface(): Boolean = isInterface 91 override fun isAnnotationType(): Boolean = isAnnotation 92 override fun isEnum(): Boolean = isEnum 93 94 var containingClass: TextClassItem? = null 95 override fun containingClass(): ClassItem? = containingClass 96 97 private var containingPackage: PackageItem? = null 98 99 fun setContainingPackage(containingPackage: TextPackageItem) { 100 this.containingPackage = containingPackage 101 } 102 103 fun setIsAnnotationType(isAnnotation: Boolean) { 104 this.isAnnotation = isAnnotation 105 } 106 107 fun setIsEnum(isEnum: Boolean) { 108 this.isEnum = isEnum 109 } 110 111 override fun containingPackage(): PackageItem = 112 containingClass?.containingPackage() ?: containingPackage ?: error(this) 113 114 override fun toType(): TypeItem { 115 val typeParameterListString = typeParameterList().toString() 116 return codebase.obtainTypeFromString( 117 if (typeParameterListString.isNotEmpty()) { 118 // TODO: No, handle List<String>[], though this is highly unlikely in a class 119 qualifiedName() + typeParameterListString 120 } else qualifiedName() 121 ) 122 } 123 124 override fun hasTypeVariables(): Boolean { 125 return typeInfo?.hasTypeArguments() ?: false 126 } 127 128 private var typeParameterList: TypeParameterList? = null 129 130 override fun typeParameterList(): TypeParameterList { 131 if (typeParameterList == null) { 132 val s = typeInfo.toString() 133 // TODO: No, handle List<String>[] (though it's not likely for type parameters) 134 val index = s.indexOf('<') 135 typeParameterList = if (index != -1) { 136 TextTypeParameterList.create(codebase, this, s.substring(index)) 137 } else { 138 TypeParameterList.NONE 139 } 140 } 141 142 return typeParameterList!! 143 } 144 145 override fun typeParameterListOwnerParent(): TypeParameterListOwner? { 146 return containingClass 147 } 148 149 override fun resolveParameter(variable: String): TypeParameterItem? { 150 if (hasTypeVariables()) { 151 for (t in typeParameterList().typeParameters()) { 152 if (t.simpleName() == variable) { 153 return t 154 } 155 } 156 } 157 158 return null 159 } 160 161 private var superClass: ClassItem? = null 162 private var superClassType: TypeItem? = null 163 164 override fun superClass(): ClassItem? = superClass 165 override fun superClassType(): TypeItem? = superClassType 166 167 override fun setSuperClass(superClass: ClassItem?, superClassType: TypeItem?) { 168 this.superClass = superClass 169 this.superClassType = superClassType 170 } 171 172 override fun setInterfaceTypes(interfaceTypes: List<TypeItem>) { 173 this.interfaceTypes = interfaceTypes.toMutableList() 174 } 175 176 private var typeInfo: TextTypeItem? = null 177 fun setTypeInfo(typeInfo: TextTypeItem) { 178 this.typeInfo = typeInfo 179 } 180 181 fun asTypeInfo(): TextTypeItem { 182 if (typeInfo == null) { 183 typeInfo = codebase.obtainTypeFromString(qualifiedTypeName) 184 } 185 return typeInfo!! 186 } 187 188 private var interfaceTypes = mutableListOf<TypeItem>() 189 private val constructors = mutableListOf<ConstructorItem>() 190 private val methods = mutableListOf<MethodItem>() 191 private val fields = mutableListOf<FieldItem>() 192 private val properties = mutableListOf<PropertyItem>() 193 194 override fun constructors(): List<ConstructorItem> = constructors 195 override fun methods(): List<MethodItem> = methods 196 override fun fields(): List<FieldItem> = fields 197 override fun properties(): List<PropertyItem> = properties 198 199 fun addInterface(itf: TypeItem) { 200 interfaceTypes.add(itf) 201 } 202 203 fun addConstructor(constructor: TextConstructorItem) { 204 constructors += constructor 205 } 206 207 fun addMethod(method: TextMethodItem) { 208 methods += method 209 } 210 211 fun addField(field: TextFieldItem) { 212 fields += field 213 } 214 215 fun addProperty(property: TextPropertyItem) { 216 properties += property 217 } 218 219 fun addEnumConstant(field: TextFieldItem) { 220 field.setEnumConstant(true) 221 fields += field 222 } 223 224 fun addInnerClass(cls: TextClassItem) { 225 innerClasses.add(cls) 226 } 227 228 fun isCompatible(cls: TextClassItem): Boolean { 229 if (this === cls) { 230 return true 231 } 232 if (fullName != cls.fullName) { 233 return false 234 } 235 236 return modifiers.toString() == cls.modifiers.toString() && 237 isInterface == cls.isInterface && 238 isEnum == cls.isEnum && 239 isAnnotation == cls.isAnnotation && 240 superClass == cls.superClass && 241 allInterfaces().toSet() == cls.allInterfaces().toSet() 242 } 243 244 override fun filteredSuperClassType(predicate: Predicate<Item>): TypeItem? { 245 // No filtering in signature files: we assume signature APIs 246 // have already been filtered and all items should match. 247 // This lets us load signature files and rewrite them using updated 248 // output formats etc. 249 return superClassType 250 } 251 252 private var retention: AnnotationRetention? = null 253 254 override fun getRetention(): AnnotationRetention { 255 retention?.let { return it } 256 257 if (!isAnnotationType()) { 258 error("getRetention() should only be called on annotation classes") 259 } 260 261 retention = ClassItem.findRetention(this) 262 return retention!! 263 } 264 265 private var fullName: String = name 266 override fun simpleName(): String = name.substring(name.lastIndexOf('.') + 1) 267 override fun fullName(): String = fullName 268 override fun qualifiedName(): String = qualifiedName 269 override fun isDefined(): Boolean { 270 assert(emit == (position != SourcePositionInfo.UNKNOWN)) 271 return emit 272 } 273 override fun toString(): String = "class ${qualifiedName()}" 274 275 override fun mapTypeVariables(target: ClassItem): Map<String, String> { 276 return emptyMap() 277 } 278 279 override fun createDefaultConstructor(): ConstructorItem { 280 return TextConstructorItem.createDefaultConstructor(codebase, this, position) 281 } 282 283 companion object { 284 fun createClassStub(codebase: TextCodebase, name: String): TextClassItem = 285 createStub(codebase, name, isInterface = false) 286 287 fun createInterfaceStub(codebase: TextCodebase, name: String): TextClassItem = 288 createStub(codebase, name, isInterface = true) 289 290 private fun createStub(codebase: TextCodebase, name: String, isInterface: Boolean): TextClassItem { 291 val index = if (name.endsWith(">")) name.indexOf('<') else -1 292 val qualifiedName = if (index == -1) name else name.substring(0, index) 293 val fullName = getFullName(qualifiedName) 294 val cls = TextClassItem( 295 codebase = codebase, 296 name = fullName, 297 qualifiedName = qualifiedName, 298 isInterface = isInterface, 299 modifiers = TextModifiers(codebase, DefaultModifierList.PUBLIC) 300 ) 301 cls.emit = false // it's a stub 302 303 if (index != -1) { 304 cls.typeParameterList = TextTypeParameterList.create(codebase, cls, name.substring(index)) 305 } 306 307 return cls 308 } 309 310 private fun getFullName(qualifiedName: String): String { 311 var end = -1 312 val length = qualifiedName.length 313 var prev = qualifiedName[length - 1] 314 for (i in length - 2 downTo 0) { 315 val c = qualifiedName[i] 316 if (c == '.' && prev.isUpperCase()) { 317 end = i + 1 318 } 319 prev = c 320 } 321 if (end != -1) { 322 return qualifiedName.substring(end) 323 } 324 325 return qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1) 326 } 327 328 private fun hasEqualTypeVar( 329 type1: TypeItem, 330 class1: ClassItem, 331 type2: TypeItem, 332 class2: ClassItem 333 ): Boolean { 334 335 // Given a type and its containing class, 336 // find the interface types that contains the type. 337 // For instance, for a method that looks like: 338 // class SomeClass implements InterfaceA<some.return.Type>, InterfaceB<some.return.Type> 339 // Type foo() 340 // this function will return [InterfaceA, InterfaceB] when Type and SomeClass 341 // are passed as inputs. 342 val typeContainingInterfaces = { 343 t: TypeItem, cl: ClassItem -> 344 val interfaceTypes = cl.interfaceTypes() 345 .plus(cl.toType()) 346 .plus(cl.superClassType()) 347 .filterNotNull() 348 interfaceTypes.filter { 349 val typeArgs = it.typeArguments(simplified = true) 350 t.toString() in typeArgs || 351 t.toElementType() in typeArgs || 352 t.asClass()?.superClass()?.qualifiedName() in typeArgs 353 } 354 } 355 356 val typeContainingInterfaces1 = typeContainingInterfaces(type1, class1) 357 val typeContainingInterfaces2 = typeContainingInterfaces(type2, class2) 358 359 if (typeContainingInterfaces1.isEmpty() || typeContainingInterfaces2.isEmpty()) { 360 return false 361 } 362 363 val interfaceTypesAreCovariant = { 364 t1: TypeItem, t2: TypeItem -> 365 t1.toErasedTypeString() == t2.toErasedTypeString() || 366 t1.asClass()?.superClass()?.qualifiedName() == t2.asClass()?.qualifiedName() || 367 t2.asClass()?.superClass()?.qualifiedName() == t1.asClass()?.qualifiedName() 368 } 369 370 // Check if the return type containing interfaces of the two methods have an intersection. 371 return typeContainingInterfaces1.any { 372 typeInterface1 -> 373 typeContainingInterfaces2.any { 374 typeInterface2 -> 375 interfaceTypesAreCovariant(typeInterface1, typeInterface2) 376 } 377 } 378 } 379 380 private fun hasEqualTypeBounds(method1: MethodItem, method2: MethodItem): Boolean { 381 val typeInTypeParams = { 382 t: TypeItem, m: MethodItem -> 383 t in m.typeParameterList().typeParameters().map { it.toType() } 384 } 385 386 val getTypeBounds = { 387 t: TypeItem, m: MethodItem -> 388 m.typeParameterList().typeParameters().single { it.toType() == t }.typeBounds().toSet() 389 } 390 391 val returnType1 = method1.returnType() 392 val returnType2 = method2.returnType() 393 394 // The following two methods are considered equal: 395 // method public <A extends some.package.SomeClass> A foo (Class<A>); 396 // method public <T extends some.package.SomeClass> T foo (Class<T>); 397 // This can be verified by converting return types to bounds ([some.package.SomeClass]) 398 // and compare equivalence. 399 return typeInTypeParams(returnType1, method1) && typeInTypeParams(returnType2, method2) && 400 getTypeBounds(returnType1, method1) == getTypeBounds(returnType2, method2) 401 } 402 403 /** 404 * Compares two [MethodItem]s and determines if the two methods have equal return types. 405 * The two methods' return types are considered equal even if the two are not identical, 406 * but are compatible in compiler level. For instance, return types in a same hierarchy tree 407 * are considered equal. 408 * 409 * @param method1 first [MethodItem] to compare the return type 410 * @param method2 second [MethodItem] to compare the return type 411 * @return a [Boolean] value representing if the two methods' return types are equal 412 */ 413 fun hasEqualReturnType(method1: MethodItem, method2: MethodItem): Boolean { 414 val returnType1 = method1.returnType() 415 val returnType2 = method2.returnType() 416 val class1 = method1.containingClass() 417 val class2 = method2.containingClass() 418 419 if (returnType1 == returnType2) return true 420 421 if (hasEqualTypeVar(returnType1, class1, returnType2, class2)) return true 422 423 if (hasEqualTypeBounds(method1, method2)) return true 424 425 return false 426 } 427 } 428 } 429