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.psi 18 19 import com.android.tools.metalava.compatibility 20 import com.android.tools.metalava.model.ClassItem 21 import com.android.tools.metalava.model.CompilationUnit 22 import com.android.tools.metalava.model.ConstructorItem 23 import com.android.tools.metalava.model.FieldItem 24 import com.android.tools.metalava.model.MethodItem 25 import com.android.tools.metalava.model.PackageItem 26 import com.android.tools.metalava.model.TypeItem 27 import com.android.tools.metalava.model.TypeParameterList 28 import com.intellij.lang.jvm.types.JvmReferenceType 29 import com.intellij.psi.PsiClass 30 import com.intellij.psi.PsiClassType 31 import com.intellij.psi.PsiCompiledFile 32 import com.intellij.psi.PsiModifier 33 import com.intellij.psi.PsiModifierListOwner 34 import com.intellij.psi.PsiType 35 import com.intellij.psi.PsiTypeParameter 36 import com.intellij.psi.impl.source.PsiClassReferenceType 37 import com.intellij.psi.util.PsiUtil 38 39 open class PsiClassItem( 40 override val codebase: PsiBasedCodebase, 41 val psiClass: PsiClass, 42 private val name: String, 43 private val fullName: String, 44 private val qualifiedName: String, 45 private val hasImplicitDefaultConstructor: Boolean, 46 val classType: ClassType, 47 modifiers: PsiModifierItem, 48 documentation: String 49 ) : 50 PsiItem( 51 codebase = codebase, 52 modifiers = modifiers, 53 documentation = documentation, 54 element = psiClass 55 ), ClassItem { 56 lateinit var containingPackage: PsiPackageItem 57 58 override fun containingPackage(): PackageItem = containingClass?.containingPackage() ?: containingPackage 59 override fun simpleName(): String = name 60 override fun fullName(): String = fullName 61 override fun qualifiedName(): String = qualifiedName 62 override fun isInterface(): Boolean = classType == ClassType.INTERFACE 63 override fun isAnnotationType(): Boolean = classType == ClassType.ANNOTATION_TYPE 64 override fun isEnum(): Boolean = classType == ClassType.ENUM 65 override fun hasImplicitDefaultConstructor(): Boolean = hasImplicitDefaultConstructor 66 67 private var superClass: ClassItem? = null 68 private var superClassType: TypeItem? = null 69 override fun superClass(): ClassItem? = superClass 70 override fun superClassType(): TypeItem? = superClassType 71 72 override fun setSuperClass(superClass: ClassItem?, superClassType: TypeItem?) { 73 this.superClass = superClass 74 this.superClassType = superClassType 75 } 76 77 override var defaultConstructor: ConstructorItem? = null 78 override var artifact: String? = null 79 80 private var containingClass: PsiClassItem? = null 81 override fun containingClass(): PsiClassItem? = containingClass 82 fun setContainingClass(containingClass: ClassItem?) { 83 this.containingClass = containingClass as PsiClassItem? 84 } 85 86 // TODO: Come up with a better scheme for how to compute this 87 override var included: Boolean = true 88 89 override var hasPrivateConstructor: Boolean = false 90 91 override fun interfaceTypes(): List<TypeItem> = interfaceTypes 92 93 override fun setInterfaceTypes(interfaceTypes: List<TypeItem>) { 94 @Suppress("UNCHECKED_CAST") 95 setInterfaces(interfaceTypes as List<PsiTypeItem>) 96 } 97 98 fun setInterfaces(interfaceTypes: List<PsiTypeItem>) { 99 this.interfaceTypes = interfaceTypes 100 } 101 102 private var allInterfaces: List<ClassItem>? = null 103 104 override fun allInterfaces(): Sequence<ClassItem> { 105 if (allInterfaces == null) { 106 val classes = mutableSetOf<PsiClass>() 107 var curr: PsiClass? = psiClass 108 while (curr != null) { 109 if (curr.isInterface && !classes.contains(curr)) { 110 classes.add(curr) 111 } 112 addInterfaces(classes, curr.interfaces) 113 curr = curr.superClass 114 } 115 val result = mutableListOf<ClassItem>() 116 for (cls in classes) { 117 val item = codebase.findOrCreateClass(cls) 118 result.add(item) 119 } 120 121 allInterfaces = result 122 } 123 124 return allInterfaces!!.asSequence() 125 } 126 127 private fun addInterfaces(result: MutableSet<PsiClass>, interfaces: Array<out PsiClass>) { 128 for (itf in interfaces) { 129 if (itf.isInterface && !result.contains(itf)) { 130 result.add(itf) 131 addInterfaces(result, itf.interfaces) 132 val superClass = itf.superClass 133 if (superClass != null) { 134 addInterfaces(result, arrayOf(superClass)) 135 } 136 } 137 } 138 } 139 140 private lateinit var innerClasses: List<PsiClassItem> 141 private lateinit var interfaceTypes: List<TypeItem> 142 private lateinit var constructors: List<PsiConstructorItem> 143 private lateinit var methods: List<PsiMethodItem> 144 private lateinit var fields: List<FieldItem> 145 146 /** 147 * If this item was created by filtering down a different codebase, this temporarily 148 * points to the original item during construction. This is used to let us initialize 149 * for example throws lists later, when all classes in the codebase have been 150 * initialized. 151 */ 152 internal var source: PsiClassItem? = null 153 154 override fun innerClasses(): List<PsiClassItem> = innerClasses 155 override fun constructors(): List<PsiConstructorItem> = constructors 156 override fun methods(): List<PsiMethodItem> = methods 157 override fun fields(): List<FieldItem> = fields 158 159 override fun toType(): TypeItem { 160 return PsiTypeItem.create(codebase, codebase.getClassType(psiClass)) 161 } 162 163 override fun hasTypeVariables(): Boolean = psiClass.hasTypeParameters() 164 165 override fun typeParameterList(): TypeParameterList { 166 if (psiClass.hasTypeParameters()) { 167 return PsiTypeParameterList( 168 codebase, psiClass.typeParameterList 169 ?: return TypeParameterList.NONE 170 ) 171 } else { 172 return TypeParameterList.NONE 173 } 174 } 175 176 override fun typeArgumentClasses(): List<ClassItem> { 177 return PsiTypeItem.typeParameterClasses( 178 codebase, 179 psiClass.typeParameterList 180 ) 181 } 182 183 override val isTypeParameter: Boolean 184 get() = psiClass is PsiTypeParameter 185 186 override fun getCompilationUnit(): CompilationUnit? { 187 if (isInnerClass()) { 188 return null 189 } 190 191 val containingFile = psiClass.containingFile ?: return null 192 if (containingFile is PsiCompiledFile) { 193 return null 194 } 195 196 return PsiCompilationUnit(codebase, containingFile) 197 } 198 199 override fun finishInitialization() { 200 super.finishInitialization() 201 202 for (method in methods) { 203 method.finishInitialization() 204 } 205 for (method in constructors) { 206 method.finishInitialization() 207 } 208 for (field in fields) { 209 // There may be non-Psi fields here later (thanks to addField) but not during construction 210 (field as PsiFieldItem).finishInitialization() 211 } 212 for (inner in innerClasses) { 213 inner.finishInitialization() 214 } 215 216 val extendsListTypes = psiClass.extendsListTypes 217 if (!extendsListTypes.isEmpty()) { 218 val type = PsiTypeItem.create(codebase, extendsListTypes[0]) 219 this.superClassType = type 220 this.superClass = type.asClass() 221 } else { 222 val superType = psiClass.superClassType 223 if (superType is PsiType) { 224 this.superClassType = PsiTypeItem.create(codebase, superType) 225 this.superClass = this.superClassType?.asClass() 226 } 227 } 228 229 // Add interfaces. If this class is an interface, it can implement both 230 // classes from the extends clause and from the implements clause. 231 val interfaces = psiClass.implementsListTypes 232 setInterfaces(if (interfaces.isEmpty() && extendsListTypes.size <= 1) { 233 emptyList() 234 } else { 235 val result = ArrayList<PsiTypeItem>(interfaces.size + extendsListTypes.size - 1) 236 val create: (PsiClassType) -> PsiTypeItem = { 237 val type = PsiTypeItem.create(codebase, it) 238 type.asClass() // ensure that we initialize classes eagerly too such that they're registered etc 239 type 240 } 241 (1 until extendsListTypes.size).mapTo(result) { create(extendsListTypes[it]) } 242 interfaces.mapTo(result) { create(it) } 243 result 244 }) 245 } 246 247 protected fun initialize( 248 innerClasses: List<PsiClassItem>, 249 interfaceTypes: List<TypeItem>, 250 constructors: List<PsiConstructorItem>, 251 methods: List<PsiMethodItem>, 252 fields: List<FieldItem> 253 ) { 254 this.innerClasses = innerClasses 255 this.interfaceTypes = interfaceTypes 256 this.constructors = constructors 257 this.methods = methods 258 this.fields = fields 259 } 260 261 override fun mapTypeVariables(target: ClassItem, reverse: Boolean): Map<String, String> { 262 val targetPsi = target.psi() as PsiClass 263 val maps = mapTypeVariablesToSuperclass( 264 psiClass, targetPsi, considerSuperClasses = true, 265 considerInterfaces = targetPsi.isInterface 266 ) ?: return emptyMap() 267 268 if (maps.isEmpty()) { 269 return emptyMap() 270 } 271 272 if (maps.size == 1) { 273 return maps[0] 274 } 275 276 val first = maps[0] 277 val flattened = mutableMapOf<String, String>() 278 for (key in first.keys) { 279 var variable: String? = key 280 for (map in maps) { 281 val value = map[variable] 282 variable = value 283 if (value == null) { 284 break 285 } else { 286 flattened[key] = value 287 } 288 } 289 } 290 return flattened 291 } 292 293 override fun equals(other: Any?): Boolean { 294 if (this === other) { 295 return true 296 } 297 return other is ClassItem && qualifiedName == other.qualifiedName() 298 } 299 300 /** 301 * Creates a constructor in this class 302 */ 303 override fun createDefaultConstructor(): ConstructorItem { 304 return PsiConstructorItem.createDefaultConstructor(codebase, this, psiClass) 305 } 306 307 override fun createMethod(template: MethodItem): MethodItem { 308 val method = template as PsiMethodItem 309 310 val replacementMap = mapTypeVariables(template.containingClass(), reverse = true) 311 312 val newMethod: PsiMethodItem 313 if (replacementMap.isEmpty()) { 314 newMethod = PsiMethodItem.create(codebase, this, method) 315 } else { 316 val stub = method.toStub(replacementMap) 317 val psiMethod = codebase.createPsiMethod(stub, psiClass) 318 newMethod = PsiMethodItem.create(codebase, this, psiMethod) 319 newMethod.inheritedMethod = method.inheritedMethod 320 newMethod.documentation = method.documentation 321 } 322 323 if (template.throwsTypes().isEmpty()) { 324 newMethod.setThrowsTypes(emptyList()) 325 } else { 326 val throwsTypes = mutableListOf<ClassItem>() 327 for (type in template.throwsTypes()) { 328 if (type.codebase === codebase) { 329 throwsTypes.add(type) 330 } else { 331 throwsTypes.add(codebase.findOrCreateClass(((type as PsiClassItem).psiClass))) 332 } 333 } 334 newMethod.setThrowsTypes(throwsTypes) 335 } 336 337 return newMethod 338 } 339 340 override fun addMethod(method: MethodItem) { 341 (methods as MutableList<PsiMethodItem>).add(method as PsiMethodItem) 342 } 343 344 override fun hashCode(): Int = qualifiedName.hashCode() 345 346 override fun toString(): String = "class ${qualifiedName()}" 347 348 companion object { 349 fun create(codebase: PsiBasedCodebase, psiClass: PsiClass): PsiClassItem { 350 if (psiClass is PsiTypeParameter) { 351 return PsiTypeParameterItem.create(codebase, psiClass) 352 } 353 val simpleName = psiClass.name!! 354 val fullName = computeFullClassName(psiClass) 355 val qualifiedName = psiClass.qualifiedName ?: simpleName 356 val hasImplicitDefaultConstructor = hasImplicitDefaultConstructor(psiClass) 357 val classType = ClassType.getClassType(psiClass) 358 359 val commentText = PsiItem.javadoc(psiClass) 360 val modifiers = modifiers(codebase, psiClass, commentText) 361 val item = PsiClassItem( 362 codebase = codebase, 363 psiClass = psiClass, 364 name = simpleName, 365 fullName = fullName, 366 qualifiedName = qualifiedName, 367 classType = classType, 368 hasImplicitDefaultConstructor = hasImplicitDefaultConstructor, 369 documentation = commentText, 370 modifiers = modifiers 371 ) 372 codebase.registerClass(item) 373 item.modifiers.setOwner(item) 374 375 // Construct the children 376 val psiMethods = psiClass.methods 377 val methods: MutableList<PsiMethodItem> = ArrayList(psiMethods.size) 378 379 if (classType == ClassType.ENUM) { 380 addEnumMethods(codebase, item, psiClass, methods) 381 } 382 383 val constructors: MutableList<PsiConstructorItem> = ArrayList(5) 384 for (psiMethod in psiMethods) { 385 if (psiMethod.isPrivate() || psiMethod.isPackagePrivate()) { 386 item.hasPrivateConstructor = true 387 } 388 if (psiMethod.isConstructor) { 389 val constructor = PsiConstructorItem.create(codebase, item, psiMethod) 390 constructors.add(constructor) 391 } else { 392 val method = PsiMethodItem.create(codebase, item, psiMethod) 393 methods.add(method) 394 } 395 } 396 397 if (hasImplicitDefaultConstructor) { 398 assert(constructors.isEmpty()) 399 constructors.add(PsiConstructorItem.createDefaultConstructor(codebase, item, psiClass)) 400 } 401 402 val fields: MutableList<FieldItem> = mutableListOf() 403 val psiFields = psiClass.fields 404 if (!psiFields.isEmpty()) { 405 psiFields.asSequence() 406 .mapTo(fields) { 407 PsiFieldItem.create(codebase, item, it) 408 } 409 } 410 411 if (classType == ClassType.INTERFACE) { 412 // All members are implicitly public, fields are implicitly static, non-static methods are abstract 413 // (except in Java 1.9, where they can be private 414 for (method in methods) { 415 if (!method.isPrivate) { 416 method.mutableModifiers().setPublic(true) 417 } 418 } 419 for (method in fields) { 420 val m = method.mutableModifiers() 421 m.setPublic(true) 422 m.setStatic(true) 423 } 424 } 425 426 item.constructors = constructors 427 item.methods = methods 428 item.fields = fields 429 430 val psiInnerClasses = psiClass.innerClasses 431 item.innerClasses = if (psiInnerClasses.isEmpty()) { 432 emptyList() 433 } else { 434 val result = psiInnerClasses.asSequence() 435 .map { 436 val inner = codebase.findOrCreateClass(it) 437 inner.containingClass = item 438 inner 439 } 440 .toMutableList() 441 result 442 } 443 444 return item 445 } 446 447 fun create(codebase: PsiBasedCodebase, classFilter: FilteredClassView): PsiClassItem { 448 val original = classFilter.cls 449 450 val newClass = PsiClassItem( 451 codebase = codebase, 452 psiClass = original.psiClass, 453 name = original.name, 454 fullName = original.fullName, 455 qualifiedName = original.qualifiedName, 456 classType = original.classType, 457 hasImplicitDefaultConstructor = original.hasImplicitDefaultConstructor, 458 documentation = original.documentation, 459 modifiers = PsiModifierItem.create(codebase, original.modifiers) 460 ) 461 462 newClass.modifiers.setOwner(newClass) 463 codebase.registerClass(newClass) 464 newClass.source = original 465 466 newClass.constructors = classFilter.constructors.map { 467 PsiConstructorItem.create(codebase, newClass, it as PsiConstructorItem) 468 }.toMutableList() 469 470 newClass.methods = classFilter.methods.map { 471 PsiMethodItem.create(codebase, newClass, it as PsiMethodItem) 472 }.toMutableList() 473 474 newClass.fields = classFilter.fields.asSequence() 475 // Preserve sorting order for enums 476 .sortedBy { it.sortingRank }.map { 477 PsiFieldItem.create(codebase, newClass, it as PsiFieldItem) 478 }.toMutableList() 479 480 newClass.innerClasses = classFilter.innerClasses.map { 481 val newInnerClass = codebase.findClass(it.cls.qualifiedName) ?: it.create(codebase) 482 newInnerClass.containingClass = newClass 483 codebase.registerClass(newInnerClass) 484 newInnerClass 485 }.toMutableList() 486 487 newClass.hasPrivateConstructor = classFilter.cls.hasPrivateConstructor 488 489 return newClass 490 } 491 492 private fun addEnumMethods( 493 codebase: PsiBasedCodebase, 494 classItem: PsiClassItem, 495 psiClass: PsiClass, 496 result: MutableList<PsiMethodItem> 497 ) { 498 // Add these two methods as overrides into the API; this isn't necessary but is done in the old 499 // API generator 500 // method public static android.graphics.ColorSpace.Adaptation valueOf(java.lang.String); 501 // method public static final android.graphics.ColorSpace.Adaptation[] values(); 502 503 if (compatibility.defaultAnnotationMethods) { 504 // TODO: Skip if we already have these methods here (but that shouldn't happen; nobody would 505 // type this by hand) 506 addEnumMethod( 507 codebase, classItem, 508 psiClass, result, 509 "public static ${psiClass.qualifiedName} valueOf(java.lang.String s) { return null; }" 510 ) 511 addEnumMethod( 512 codebase, classItem, 513 psiClass, result, 514 "public static final ${psiClass.qualifiedName}[] values() { return null; }" 515 ) 516 // Also add a private constructor; used when emitting the private API 517 val psiMethod = codebase.createConstructor("private ${psiClass.name}", psiClass) 518 result.add(PsiConstructorItem.create(codebase, classItem, psiMethod)) 519 } 520 } 521 522 private fun addEnumMethod( 523 codebase: PsiBasedCodebase, 524 classItem: PsiClassItem, 525 psiClass: PsiClass, 526 result: MutableList<PsiMethodItem>, 527 source: String 528 ) { 529 val psiMethod = codebase.createPsiMethod(source, psiClass) 530 result.add(PsiMethodItem.create(codebase, classItem, psiMethod)) 531 } 532 533 /** 534 * Computes the "full" class name; this is not the qualified class name (e.g. with package) 535 * but for an inner class it includes all the outer classes 536 */ 537 private fun computeFullClassName(cls: PsiClass): String { 538 if (cls.containingClass == null) { 539 val name = cls.name 540 return name!! 541 } else { 542 val list = mutableListOf<String>() 543 var curr: PsiClass? = cls 544 while (curr != null) { 545 val name = curr.name 546 curr = if (name != null) { 547 list.add(name) 548 curr.containingClass 549 } else { 550 break 551 } 552 } 553 return list.asReversed().asSequence().joinToString(separator = ".") { it } 554 } 555 } 556 557 private fun hasImplicitDefaultConstructor(psiClass: PsiClass): Boolean { 558 if (psiClass.name?.startsWith("-") == true) { 559 // Deliberately hidden; see examples like 560 // @file:JvmName("-ViewModelExtensions") // Hide from Java sources in the IDE. 561 return false 562 } 563 564 val constructors = psiClass.constructors 565 if (constructors.isEmpty() && !psiClass.isInterface && !psiClass.isAnnotationType && !psiClass.isEnum) { 566 if (PsiUtil.hasDefaultConstructor(psiClass)) { 567 return true 568 } 569 570 // The above method isn't always right; for example, for the ContactsContract.Presence class 571 // in the framework, which looks like this: 572 // @Deprecated 573 // public static final class Presence extends StatusUpdates { 574 // } 575 // javac makes a default constructor: 576 // public final class android.provider.ContactsContract$Presence extends android.provider.ContactsContract$StatusUpdates { 577 // public android.provider.ContactsContract$Presence(); 578 // } 579 // but the above method returns false. So add some of our own heuristics: 580 if (psiClass.hasModifierProperty(PsiModifier.FINAL) && !psiClass.hasModifierProperty( 581 PsiModifier.ABSTRACT 582 ) && 583 psiClass.hasModifierProperty(PsiModifier.PUBLIC) 584 ) { 585 return true 586 } 587 } 588 589 return false 590 } 591 592 fun mapTypeVariablesToSuperclass( 593 psiClass: PsiClass, 594 targetClass: PsiClass, 595 considerSuperClasses: Boolean = true, 596 considerInterfaces: Boolean = psiClass.isInterface 597 ): MutableList<Map<String, String>>? { 598 // TODO: Prune search if type doesn't have type arguments! 599 if (considerSuperClasses) { 600 val list = mapTypeVariablesToSuperclass( 601 psiClass.superClassType, targetClass, 602 considerSuperClasses, considerInterfaces 603 ) 604 if (list != null) { 605 return list 606 } 607 } 608 609 if (considerInterfaces) { 610 for (interfaceType in psiClass.interfaceTypes) { 611 val list = mapTypeVariablesToSuperclass( 612 interfaceType, targetClass, 613 considerSuperClasses, considerInterfaces 614 ) 615 if (list != null) { 616 return list 617 } 618 } 619 } 620 621 return null 622 } 623 624 private fun mapTypeVariablesToSuperclass( 625 type: JvmReferenceType?, 626 targetClass: PsiClass, 627 considerSuperClasses: Boolean = true, 628 considerInterfaces: Boolean = true 629 ): MutableList<Map<String, String>>? { 630 // TODO: Prune search if type doesn't have type arguments! 631 val superType = type as? PsiClassReferenceType 632 val superClass = superType?.resolve() 633 if (superClass != null) { 634 if (superClass == targetClass) { 635 val map = mapTypeVariablesToSuperclass(superType) 636 return if (map != null) { 637 mutableListOf(map) 638 } else { 639 null 640 } 641 } else { 642 val list = mapTypeVariablesToSuperclass( 643 superClass, targetClass, considerSuperClasses, 644 considerInterfaces 645 ) 646 if (list != null) { 647 val map = mapTypeVariablesToSuperclass(superType) 648 if (map != null) { 649 list.add(map) 650 } 651 return list 652 } 653 } 654 } 655 656 return null 657 } 658 659 private fun mapTypeVariablesToSuperclass(superType: PsiClassReferenceType?): Map<String, String>? { 660 superType ?: return null 661 662 val map = mutableMapOf<String, String>() 663 val superClass = superType.resolve() 664 if (superClass != null && superType.hasParameters()) { 665 val superTypeParameters = superClass.typeParameters 666 superType.parameters.forEachIndexed { index, parameter -> 667 if (parameter is PsiClassReferenceType) { 668 val parameterClass = parameter.resolve() 669 if (parameterClass != null) { 670 val parameterName = parameterClass.qualifiedName ?: parameterClass.name ?: parameter.name 671 if (index < superTypeParameters.size) { 672 val superTypeParameter = superTypeParameters[index] 673 val superTypeName = superTypeParameter.qualifiedName ?: superTypeParameter.name 674 if (superTypeName != null) { 675 map[superTypeName] = parameterName 676 } 677 } 678 } 679 } 680 } 681 } 682 683 return map 684 } 685 } 686 } 687 688 fun PsiModifierListOwner.isPrivate(): Boolean = modifierList?.hasExplicitModifier(PsiModifier.PRIVATE) == true 689 fun PsiModifierListOwner.isPackagePrivate(): Boolean { 690 val modifiers = modifierList ?: return false 691 return !(modifiers.hasModifierProperty(PsiModifier.PUBLIC) || 692 modifiers.hasModifierProperty(PsiModifier.PROTECTED)) 693 }