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