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