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.psi 18 19 import com.android.SdkConstants 20 import com.android.tools.metalava.ANDROIDX_NONNULL 21 import com.android.tools.metalava.ANDROIDX_NULLABLE 22 import com.android.tools.metalava.doclava1.Issues 23 import com.android.tools.metalava.model.ClassItem 24 import com.android.tools.metalava.model.DefaultCodebase 25 import com.android.tools.metalava.model.Item 26 import com.android.tools.metalava.model.MethodItem 27 import com.android.tools.metalava.model.PackageDocs 28 import com.android.tools.metalava.model.PackageItem 29 import com.android.tools.metalava.model.PackageList 30 import com.android.tools.metalava.model.TypeItem 31 import com.android.tools.metalava.options 32 import com.android.tools.metalava.reporter 33 import com.android.tools.metalava.tick 34 import com.intellij.openapi.project.Project 35 import com.intellij.openapi.util.Disposer 36 import com.intellij.psi.JavaPsiFacade 37 import com.intellij.psi.JavaRecursiveElementVisitor 38 import com.intellij.psi.PsiAnnotation 39 import com.intellij.psi.PsiArrayType 40 import com.intellij.psi.PsiClass 41 import com.intellij.psi.PsiClassOwner 42 import com.intellij.psi.PsiClassType 43 import com.intellij.psi.PsiElement 44 import com.intellij.psi.PsiErrorElement 45 import com.intellij.psi.PsiField 46 import com.intellij.psi.PsiFile 47 import com.intellij.psi.PsiImportStatement 48 import com.intellij.psi.PsiJavaCodeReferenceElement 49 import com.intellij.psi.PsiJavaFile 50 import com.intellij.psi.PsiMethod 51 import com.intellij.psi.PsiPackage 52 import com.intellij.psi.PsiSubstitutor 53 import com.intellij.psi.PsiType 54 import com.intellij.psi.TypeAnnotationProvider 55 import com.intellij.psi.javadoc.PsiDocComment 56 import com.intellij.psi.javadoc.PsiDocTag 57 import com.intellij.psi.search.GlobalSearchScope 58 import com.intellij.psi.util.PsiTreeUtil 59 import org.jetbrains.kotlin.resolve.BindingContext 60 import org.jetbrains.uast.UFile 61 import org.jetbrains.uast.UastFacade 62 import java.io.File 63 import java.io.IOException 64 import java.util.ArrayList 65 import java.util.HashMap 66 import java.util.zip.ZipFile 67 68 const val PACKAGE_ESTIMATE = 500 69 const val CLASS_ESTIMATE = 15000 70 const val METHOD_ESTIMATE = 1000 71 72 open class PsiBasedCodebase(location: File, override var description: String = "Unknown") : DefaultCodebase(location) { 73 lateinit var project: Project 74 75 var bindingContext: BindingContext? = null 76 77 /** Map from class name to class item */ 78 private val classMap: MutableMap<String, PsiClassItem> = HashMap(CLASS_ESTIMATE) 79 80 /** Map from psi type to type item */ 81 private val typeMap: MutableMap<PsiType, TypeItem> = HashMap(400) 82 83 /** 84 * Map from classes to the set of methods for each (but only for classes where we've 85 * called [findMethod] 86 */ 87 private lateinit var methodMap: MutableMap<PsiClassItem, MutableMap<PsiMethod, PsiMethodItem>> 88 89 /** Map from package name to the corresponding package item */ 90 private lateinit var packageMap: MutableMap<String, PsiPackageItem> 91 92 /** Map from package name to list of classes in that package */ 93 private lateinit var packageClasses: MutableMap<String, MutableList<PsiClassItem>> 94 95 /** A set of packages to hide */ 96 private lateinit var hiddenPackages: MutableMap<String, Boolean?> 97 98 /** 99 * A list of the top-level classes declared in the codebase's source (rather than on its 100 * classpath). 101 */ 102 private lateinit var topLevelClassesFromSource: MutableList<ClassItem> 103 104 private var initializing = false 105 106 override fun trustedApi(): Boolean = false 107 108 private var packageDocs: PackageDocs? = null 109 110 private var hideClassesFromJars = true 111 112 private lateinit var emptyPackage: PsiPackageItem 113 114 fun initialize(project: Project, units: List<PsiFile>, packages: PackageDocs) { 115 initializing = true 116 this.units = units 117 packageDocs = packages 118 119 this.project = project 120 // there are currently ~230 packages in the public SDK, but here we need to account for internal ones too 121 val hiddenPackages: MutableSet<String> = packages.hiddenPackages 122 val packageDocs: MutableMap<String, String> = packages.packageDocs 123 this.hiddenPackages = HashMap(100) 124 for (pkgName in hiddenPackages) { 125 this.hiddenPackages[pkgName] = true 126 } 127 128 packageMap = HashMap(PACKAGE_ESTIMATE) 129 packageClasses = HashMap(PACKAGE_ESTIMATE) 130 packageClasses[""] = ArrayList() 131 this.methodMap = HashMap(METHOD_ESTIMATE) 132 topLevelClassesFromSource = ArrayList(CLASS_ESTIMATE) 133 134 // Make sure we only process the units once; sometimes there's overlap in the source lists 135 for (unit in units.asSequence().distinct()) { 136 tick() // show progress 137 138 unit.accept(object : JavaRecursiveElementVisitor() { 139 override fun visitImportStatement(element: PsiImportStatement) { 140 super.visitImportStatement(element) 141 if (element.resolve() == null) { 142 reporter.report( 143 Issues.UNRESOLVED_IMPORT, 144 element, 145 "Unresolved import: `${element.qualifiedName}`" 146 ) 147 } 148 } 149 }) 150 151 var classes = (unit as? PsiClassOwner)?.classes?.toList() ?: emptyList() 152 if (classes.isEmpty()) { 153 val uFile = UastFacade.convertElementWithParent(unit, UFile::class.java) as? UFile? 154 classes = uFile?.classes?.map { it }?.toList() ?: emptyList() 155 } 156 var packageName: String? = null 157 if (classes.isEmpty() && unit is PsiJavaFile) { 158 // package-info.java ? 159 val packageStatement = unit.packageStatement 160 // Look for javadoc on the package statement; this is NOT handed to us on 161 // the PsiPackage! 162 if (packageStatement != null) { 163 packageName = packageStatement.packageName 164 val comment = PsiTreeUtil.getPrevSiblingOfType(packageStatement, PsiDocComment::class.java) 165 if (comment != null) { 166 val text = comment.text 167 if (text.contains("@hide")) { 168 this.hiddenPackages[packageName] = true 169 } 170 if (packageDocs[packageName] != null) { 171 reporter.report( 172 Issues.BOTH_PACKAGE_INFO_AND_HTML, 173 unit, 174 "It is illegal to provide both a package-info.java file and a " + 175 "package.html file for the same package" 176 ) 177 } 178 packageDocs[packageName] = text 179 } 180 } 181 } else { 182 for (psiClass in classes) { 183 psiClass.accept(object : JavaRecursiveElementVisitor() { 184 override fun visitErrorElement(element: PsiErrorElement) { 185 super.visitErrorElement(element) 186 reporter.report( 187 Issues.INVALID_SYNTAX, 188 element, 189 "Syntax error: `${element.errorDescription}`" 190 ) 191 } 192 }) 193 194 val classItem = createClass(psiClass) 195 topLevelClassesFromSource.add(classItem) 196 197 if (packageName == null) { 198 packageName = getPackageName(psiClass) 199 } 200 } 201 } 202 } 203 204 // Next construct packages 205 for ((pkgName, classes) in packageClasses) { 206 tick() // show progress 207 val psiPackage = JavaPsiFacade.getInstance(project).findPackage(pkgName) 208 if (psiPackage == null) { 209 println("Could not find package $pkgName") 210 continue 211 } 212 213 val sortedClasses = classes.toMutableList().sortedWith(ClassItem.fullNameComparator) 214 registerPackage(psiPackage, sortedClasses, packageDocs[pkgName], pkgName) 215 } 216 217 initializing = false 218 219 emptyPackage = findPackage("")!! 220 221 // Finish initialization 222 val initialPackages = ArrayList(packageMap.values) 223 var registeredCount = packageMap.size // classes added after this point will have indices >= original 224 for (cls in initialPackages) { 225 cls.finishInitialization() 226 } 227 228 // Finish initialization of any additional classes that were registered during 229 // the above initialization (recursively) 230 while (registeredCount < packageMap.size) { 231 val added = packageMap.values.minus(initialPackages) 232 registeredCount = packageMap.size 233 for (pkg in added) { 234 pkg.finishInitialization() 235 } 236 } 237 238 // Point to "parent" packages, since doclava treats packages as nested (e.g. an @hide on 239 // android.foo will also apply to android.foo.bar) 240 addParentPackages(packageMap.values) 241 } 242 243 override fun dispose() { 244 Disposer.dispose(project) 245 super.dispose() 246 } 247 248 private fun addParentPackages(packages: Collection<PsiPackageItem>) { 249 val missingPackages = packages.mapNotNull { 250 val name = it.qualifiedName() 251 val index = name.lastIndexOf('.') 252 val parent = if (index != -1) { 253 name.substring(0, index) 254 } else { 255 "" 256 } 257 if (packageMap.containsKey(parent)) { 258 // Already registered 259 null 260 } else { 261 parent 262 } 263 }.toSet() 264 265 // Create PackageItems for any packages that weren't in the source 266 for (pkgName in missingPackages) { 267 val psiPackage = JavaPsiFacade.getInstance(project).findPackage(pkgName) ?: continue 268 val sortedClasses = emptyList<PsiClassItem>() 269 val packageHtml = null 270 val pkg = registerPackage(psiPackage, sortedClasses, packageHtml, pkgName) 271 pkg.emit = false // don't expose these packages in the API signature files, stubs, etc 272 } 273 274 // Connect up all the package items 275 for (pkg in packageMap.values) { 276 var name = pkg.qualifiedName() 277 // Find parent package; we have to loop since we don't always find a PSI package 278 // for intermediate elements; e.g. we may jump from java.lang straight up to the default 279 // package 280 while (name.isNotEmpty()) { 281 val index = name.lastIndexOf('.') 282 name = if (index != -1) { 283 name.substring(0, index) 284 } else { 285 "" 286 } 287 val parent = findPackage(name) ?: continue 288 pkg.containingPackageField = parent 289 break 290 } 291 } 292 } 293 294 private fun registerPackage( 295 psiPackage: PsiPackage, 296 sortedClasses: List<PsiClassItem>?, 297 packageHtml: String?, 298 pkgName: String 299 ): PsiPackageItem { 300 val packageItem = PsiPackageItem.create(this, psiPackage, packageHtml) 301 packageMap[pkgName] = packageItem 302 if (isPackageHidden(pkgName)) { 303 packageItem.hidden = true 304 } 305 306 sortedClasses?.let { packageItem.addClasses(it) } 307 return packageItem 308 } 309 310 fun initialize(project: Project, jarFile: File, preFiltered: Boolean = false) { 311 this.preFiltered = preFiltered 312 initializing = true 313 hideClassesFromJars = false 314 315 this.project = project 316 317 // Find all classes referenced from the class 318 val facade = JavaPsiFacade.getInstance(project) 319 val scope = GlobalSearchScope.allScope(project) 320 321 hiddenPackages = HashMap(100) 322 packageMap = HashMap(PACKAGE_ESTIMATE) 323 packageClasses = HashMap(PACKAGE_ESTIMATE) 324 packageClasses[""] = ArrayList() 325 this.methodMap = HashMap(1000) 326 val packageToClasses: MutableMap<String, MutableList<PsiClassItem>> = HashMap( 327 PACKAGE_ESTIMATE 328 ) 329 packageToClasses[""] = ArrayList() // ensure we construct one for the default package 330 331 topLevelClassesFromSource = ArrayList(CLASS_ESTIMATE) 332 333 try { 334 ZipFile(jarFile).use { jar -> 335 val enumeration = jar.entries() 336 while (enumeration.hasMoreElements()) { 337 val entry = enumeration.nextElement() 338 val fileName = entry.name 339 if (fileName.contains("$")) { 340 // skip inner classes 341 continue 342 } 343 if (fileName.endsWith(SdkConstants.DOT_CLASS)) { 344 val qualifiedName = fileName.removeSuffix(SdkConstants.DOT_CLASS).replace('/', '.') 345 if (qualifiedName.endsWith(".package-info")) { 346 // Ensure we register a package for this, even if empty 347 val packageName = qualifiedName.removeSuffix(".package-info") 348 var list = packageToClasses[packageName] 349 if (list == null) { 350 list = mutableListOf() 351 packageToClasses[packageName] = list 352 } 353 continue 354 } else { 355 val psiClass = facade.findClass(qualifiedName, scope) ?: continue 356 357 val classItem = createClass(psiClass) 358 topLevelClassesFromSource.add(classItem) 359 360 val packageName = getPackageName(psiClass) 361 var list = packageToClasses[packageName] 362 if (list == null) { 363 list = mutableListOf(classItem) 364 packageToClasses[packageName] = list 365 } else { 366 list.add(classItem) 367 } 368 } 369 } 370 } 371 } 372 } catch (e: IOException) { 373 reporter.report(Issues.IO_ERROR, jarFile, e.message ?: e.toString()) 374 } 375 376 // Next construct packages 377 for ((pkgName, packageClasses) in packageToClasses) { 378 val psiPackage = JavaPsiFacade.getInstance(project).findPackage(pkgName) 379 if (psiPackage == null) { 380 println("Could not find package $pkgName") 381 continue 382 } 383 384 packageClasses.sortWith(ClassItem.fullNameComparator) 385 // TODO: How do we obtain the package docs? We generally don't have them, but it *would* be 386 // nice if we picked up "overview.html" bundled files and added them. But since the docs 387 // are generally missing for all elements *anyway*, let's not bother. 388 val docs = packageDocs?.packageDocs 389 val packageHtml: String? = 390 if (docs != null) { 391 docs[pkgName] 392 } else { 393 null 394 } 395 registerPackage(psiPackage, packageClasses, packageHtml, pkgName) 396 } 397 398 emptyPackage = findPackage("")!! 399 400 initializing = false 401 hideClassesFromJars = true 402 403 // Finish initialization 404 for (pkg in packageMap.values) { 405 pkg.finishInitialization() 406 } 407 } 408 409 fun dumpStats() { 410 options.stdout.println( 411 "INTERNAL STATS: Size of classMap=${classMap.size} and size of " + 412 "methodMap=${methodMap.size} and size of packageMap=${packageMap.size}, and the " + 413 "typemap size is ${typeMap.size}, and the packageClasses size is ${packageClasses.size} " 414 ) 415 } 416 417 private fun registerPackageClass(packageName: String, cls: PsiClassItem) { 418 var list = packageClasses[packageName] 419 if (list == null) { 420 list = ArrayList() 421 packageClasses[packageName] = list 422 } 423 424 list.add(cls) 425 } 426 427 private fun isPackageHidden(packageName: String): Boolean { 428 val hidden = hiddenPackages[packageName] 429 if (hidden == true) { 430 return true 431 } else if (hidden == null) { 432 // Compute for all prefixes of this package 433 var pkg = packageName 434 while (true) { 435 if (hiddenPackages[pkg] != null) { 436 hiddenPackages[packageName] = hiddenPackages[pkg] 437 if (hiddenPackages[pkg] == true) { 438 return true 439 } 440 } 441 val last = pkg.lastIndexOf('.') 442 if (last == -1) { 443 hiddenPackages[packageName] = false 444 break 445 } else { 446 pkg = pkg.substring(0, last) 447 } 448 } 449 } 450 451 return false 452 } 453 454 private fun createClass(clz: PsiClass): PsiClassItem { 455 val classItem = PsiClassItem.create(this, clz) 456 457 if (!initializing) { 458 // This class is found while we're no longer initializing all the source units: 459 // that means it must be found on the classpath instead. These should be treated 460 // as hidden; we don't want to generate code for them. 461 classItem.emit = false 462 463 // Workaround: we're pulling in .aidl files from .jar files. These are 464 // marked @hide, but since we only see the .class files we don't know that. 465 if (classItem.simpleName().startsWith("I") && 466 classItem.isFromClassPath() && 467 clz.interfaces.any { it.qualifiedName == "android.os.IInterface" } 468 ) { 469 classItem.hidden = true 470 } 471 } 472 473 if (classItem.classType == ClassType.TYPE_PARAMETER) { 474 // Don't put PsiTypeParameter classes into the registry; e.g. when we're visiting 475 // java.util.stream.Stream<R> 476 // we come across "R" and would try to place it here. 477 classItem.containingPackage = emptyPackage 478 classItem.finishInitialization() 479 return classItem 480 } 481 val qualifiedName: String = clz.qualifiedName ?: clz.name!! 482 classMap[qualifiedName] = classItem 483 484 // TODO: Cache for adjacent files! 485 val packageName = getPackageName(clz) 486 registerPackageClass(packageName, classItem) 487 488 if (!initializing) { 489 classItem.emit = false 490 classItem.finishInitialization() 491 val pkgName = getPackageName(clz) 492 val pkg = findPackage(pkgName) 493 if (pkg == null) { 494 // val packageHtml: String? = packageDocs?.packageDocs!![pkgName] 495 // dynamically discovered packages should NOT be included 496 // val packageHtml = "/** @hide */" 497 val packageHtml = null 498 val psiPackage = JavaPsiFacade.getInstance(project).findPackage(pkgName) 499 if (psiPackage != null) { 500 val packageItem = registerPackage(psiPackage, null, packageHtml, pkgName) 501 // Don't include packages from API that isn't directly included in the API 502 packageItem.emit = false 503 packageItem.addClass(classItem) 504 } 505 } else { 506 pkg.addClass(classItem) 507 } 508 } 509 510 return classItem 511 } 512 513 override fun getPackages(): PackageList { 514 // TODO: Sorting is probably not necessary here! 515 return PackageList(this, packageMap.values.toMutableList().sortedWith(PackageItem.comparator)) 516 } 517 518 override fun getPackageDocs(): PackageDocs? { 519 return packageDocs 520 } 521 522 override fun size(): Int { 523 return packageMap.size 524 } 525 526 override fun findPackage(pkgName: String): PsiPackageItem? { 527 return packageMap[pkgName] 528 } 529 530 override fun findClass(className: String): PsiClassItem? { 531 return classMap[className] 532 } 533 534 open fun findClass(psiClass: PsiClass): PsiClassItem? { 535 val qualifiedName: String = psiClass.qualifiedName ?: psiClass.name!! 536 return classMap[qualifiedName] 537 } 538 539 open fun findOrCreateClass(qualifiedName: String): PsiClassItem? { 540 val finder = JavaPsiFacade.getInstance(project) 541 val psiClass = finder.findClass(qualifiedName, GlobalSearchScope.allScope(project)) ?: return null 542 return findOrCreateClass(psiClass) 543 } 544 545 open fun findOrCreateClass(psiClass: PsiClass): PsiClassItem { 546 val existing = findClass(psiClass) 547 if (existing != null) { 548 return existing 549 } 550 551 var curr = psiClass.containingClass 552 if (curr != null && findClass(curr) == null) { 553 // Make sure we construct outer/top level classes first 554 if (findClass(curr) == null) { 555 while (true) { 556 val containing = curr?.containingClass 557 if (containing == null) { 558 break 559 } else { 560 curr = containing 561 } 562 } 563 curr!! 564 createClass(curr) // this will also create inner classes, which should now be in the map 565 val inner = findClass(psiClass) 566 inner!! // should be there now 567 return inner 568 } 569 } 570 571 return existing ?: return createClass(psiClass) 572 } 573 574 fun findClass(psiType: PsiType): PsiClassItem? { 575 if (psiType is PsiClassType) { 576 val cls = psiType.resolve() ?: return null 577 return findOrCreateClass(cls) 578 } else if (psiType is PsiArrayType) { 579 var componentType = psiType.componentType 580 // We repeatedly get the component type because the array may have multiple dimensions 581 while (componentType is PsiArrayType) { 582 componentType = componentType.componentType 583 } 584 if (componentType is PsiClassType) { 585 val cls = componentType.resolve() ?: return null 586 return findOrCreateClass(cls) 587 } 588 } 589 return null 590 } 591 592 fun getClassType(cls: PsiClass): PsiClassType = getFactory().createType(cls, PsiSubstitutor.EMPTY) 593 594 fun getComment(string: String, parent: PsiElement? = null): PsiDocComment = 595 getFactory().createDocCommentFromText(string, parent) 596 597 fun getType(psiType: PsiType): PsiTypeItem { 598 // Note: We do *not* cache these; it turns out that storing PsiType instances 599 // in a map is bad for performance; it has a very expensive equals operation 600 // for some type comparisons (and we sometimes end up with unexpected results, 601 // e.g. where we fetch an "equals" type from the map but its representation 602 // is slightly different than we intended 603 return PsiTypeItem.create(this, psiType) 604 } 605 606 fun getType(psiClass: PsiClass): PsiTypeItem { 607 return PsiTypeItem.create(this, getFactory().createType(psiClass)) 608 } 609 610 private fun getPackageName(clz: PsiClass): String { 611 var top: PsiClass? = clz 612 while (top?.containingClass != null) { 613 top = top.containingClass 614 } 615 top ?: return "" 616 617 val name = top.name 618 val fullName = top.qualifiedName ?: return "" 619 620 if (name == fullName) { 621 return "" 622 } 623 624 return fullName.substring(0, fullName.length - 1 - name!!.length) 625 } 626 627 fun findMethod(method: PsiMethod): PsiMethodItem { 628 val containingClass = method.containingClass 629 val cls = findOrCreateClass(containingClass!!) 630 631 // Ensure initialized/registered via [#registerMethods] 632 if (methodMap[cls] == null) { 633 val map = HashMap<PsiMethod, PsiMethodItem>(40) 634 registerMethods(cls.methods(), map) 635 registerMethods(cls.constructors(), map) 636 methodMap[cls] = map 637 } 638 639 val methods = methodMap[cls]!! 640 val methodItem = methods[method] 641 if (methodItem == null) { 642 // Probably switched psi classes (e.g. used source PsiClass in registry but 643 // found duplicate class in .jar library and we're now pointing to it; in that 644 // case, find the equivalent method by signature 645 val psiClass = cls.psiClass 646 val updatedMethod = psiClass.findMethodBySignature(method, true) 647 val result = methods[updatedMethod!!] 648 if (result == null) { 649 val extra = PsiMethodItem.create(this, cls, updatedMethod) 650 methods[method] = extra 651 methods[updatedMethod] = extra 652 if (!initializing) { 653 extra.finishInitialization() 654 } 655 656 return extra 657 } 658 return result 659 } 660 661 return methodItem 662 } 663 664 fun findField(field: PsiField): Item? { 665 val containingClass = field.containingClass ?: return null 666 val cls = findOrCreateClass(containingClass) 667 return cls.findField(field.name) 668 } 669 670 private fun registerMethods(methods: List<MethodItem>, map: MutableMap<PsiMethod, PsiMethodItem>) { 671 for (method in methods) { 672 val psiMethod = (method as PsiMethodItem).psiMethod 673 map[psiMethod] = method 674 } 675 } 676 677 /** 678 * Returns a list of the top-level classes declared in the codebase's source (rather than on its 679 * classpath). 680 */ 681 fun getTopLevelClassesFromSource(): List<ClassItem> { 682 return topLevelClassesFromSource 683 } 684 685 fun createReferenceFromText(s: String, parent: PsiElement? = null): PsiJavaCodeReferenceElement = 686 getFactory().createReferenceFromText(s, parent) 687 688 fun createPsiMethod(s: String, parent: PsiElement? = null): PsiMethod = 689 getFactory().createMethodFromText(s, parent) 690 691 fun createConstructor(s: String, parent: PsiElement? = null): PsiMethod = 692 getFactory().createConstructor(s, parent) 693 694 fun createPsiType(s: String, parent: PsiElement? = null): PsiType = 695 getFactory().createTypeFromText(s, parent) 696 697 fun createPsiAnnotation(s: String, parent: PsiElement? = null): PsiAnnotation = 698 getFactory().createAnnotationFromText(s, parent) 699 700 fun createDocTagFromText(s: String): PsiDocTag = getFactory().createDocTagFromText(s) 701 702 private fun getFactory() = JavaPsiFacade.getElementFactory(project) 703 704 private var nonNullAnnotationProvider: TypeAnnotationProvider? = null 705 private var nullableAnnotationProvider: TypeAnnotationProvider? = null 706 707 /** Type annotation provider which provides androidx.annotation.NonNull */ 708 fun getNonNullAnnotationProvider(): TypeAnnotationProvider { 709 return nonNullAnnotationProvider ?: run { 710 val provider = TypeAnnotationProvider.Static.create(arrayOf(createPsiAnnotation("@$ANDROIDX_NONNULL"))) 711 nonNullAnnotationProvider 712 provider 713 } 714 } 715 716 /** Type annotation provider which provides androidx.annotation.Nullable */ 717 fun getNullableAnnotationProvider(): TypeAnnotationProvider { 718 return nullableAnnotationProvider ?: run { 719 val provider = TypeAnnotationProvider.Static.create(arrayOf(createPsiAnnotation("@$ANDROIDX_NULLABLE"))) 720 nullableAnnotationProvider 721 provider 722 } 723 } 724 725 override fun createAnnotation( 726 source: String, 727 context: Item?, 728 mapName: Boolean 729 ): PsiAnnotationItem { 730 val psiAnnotation = createPsiAnnotation(source, context?.psi()) 731 return PsiAnnotationItem.create(this, psiAnnotation) 732 } 733 734 override fun supportsDocumentation(): Boolean = true 735 736 override fun toString(): String = description 737 738 fun registerClass(cls: PsiClassItem) { 739 assert(classMap[cls.qualifiedName()] == null || classMap[cls.qualifiedName()] == cls) 740 741 classMap[cls.qualifiedName()] = cls 742 } 743 } 744