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