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