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