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