1 /*
2  * 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.tools.lint.detector.api.getInternalName
20 import com.android.tools.metalava.JAVA_LANG_STRING
21 import com.android.tools.metalava.compatibility
22 import com.android.tools.metalava.model.AnnotationItem
23 import com.android.tools.metalava.model.ClassItem
24 import com.android.tools.metalava.model.Item
25 import com.android.tools.metalava.model.MemberItem
26 import com.android.tools.metalava.model.MethodItem
27 import com.android.tools.metalava.model.ParameterItem
28 import com.android.tools.metalava.model.TypeItem
29 import com.android.tools.metalava.model.TypeParameterItem
30 import com.intellij.psi.JavaTokenType
31 import com.intellij.psi.PsiArrayType
32 import com.intellij.psi.PsiCapturedWildcardType
33 import com.intellij.psi.PsiClass
34 import com.intellij.psi.PsiClassType
35 import com.intellij.psi.PsiCompiledElement
36 import com.intellij.psi.PsiDisjunctionType
37 import com.intellij.psi.PsiElement
38 import com.intellij.psi.PsiEllipsisType
39 import com.intellij.psi.PsiIntersectionType
40 import com.intellij.psi.PsiJavaCodeReferenceElement
41 import com.intellij.psi.PsiJavaToken
42 import com.intellij.psi.PsiLambdaExpressionType
43 import com.intellij.psi.PsiPrimitiveType
44 import com.intellij.psi.PsiRecursiveElementVisitor
45 import com.intellij.psi.PsiReferenceList
46 import com.intellij.psi.PsiType
47 import com.intellij.psi.PsiTypeElement
48 import com.intellij.psi.PsiTypeParameter
49 import com.intellij.psi.PsiTypeParameterList
50 import com.intellij.psi.PsiTypeVisitor
51 import com.intellij.psi.PsiWhiteSpace
52 import com.intellij.psi.PsiWildcardType
53 import com.intellij.psi.util.PsiTypesUtil
54 import com.intellij.psi.util.TypeConversionUtil
55 import java.util.function.Predicate
56 
57 /** Represents a type backed by PSI */
58 class PsiTypeItem private constructor(
59     private val codebase: PsiBasedCodebase,
60     var psiType: PsiType
61 ) : TypeItem {
62     private var toString: String? = null
63     private var toAnnotatedString: String? = null
64     private var toInnerAnnotatedString: String? = null
65     private var toErasedString: String? = null
66     private var asClass: PsiClassItem? = null
67 
toStringnull68     override fun toString(): String {
69         return toTypeString()
70     }
71 
toTypeStringnull72     override fun toTypeString(
73         outerAnnotations: Boolean,
74         innerAnnotations: Boolean,
75         erased: Boolean,
76         kotlinStyleNulls: Boolean,
77         context: Item?,
78         filter: Predicate<Item>?
79     ): String {
80         assert(innerAnnotations || !outerAnnotations) // Can't supply outer=true,inner=false
81 
82         if (filter != null) {
83             // No caching when specifying filter.
84             // TODO: When we support type use annotations, here we need to deal with markRecent
85             //  and clearAnnotations not really having done their job.
86             return toTypeString(
87                 codebase = codebase,
88                 type = psiType,
89                 outerAnnotations = outerAnnotations,
90                 innerAnnotations = innerAnnotations,
91                 erased = erased,
92                 kotlinStyleNulls = kotlinStyleNulls,
93                 context = context,
94                 filter = filter
95             )
96         }
97 
98         return if (erased) {
99             if (kotlinStyleNulls && (innerAnnotations || outerAnnotations)) {
100                 // Not cached: Not common
101                 toTypeString(
102                     codebase = codebase,
103                     type = psiType,
104                     outerAnnotations = outerAnnotations,
105                     innerAnnotations = innerAnnotations,
106                     erased = erased,
107                     kotlinStyleNulls = kotlinStyleNulls,
108                     context = context,
109                     filter = filter
110                 )
111             } else {
112                 if (toErasedString == null) {
113                     toErasedString = toTypeString(
114                         codebase = codebase,
115                         type = psiType,
116                         outerAnnotations = outerAnnotations,
117                         innerAnnotations = innerAnnotations,
118                         erased = erased,
119                         kotlinStyleNulls = kotlinStyleNulls,
120                         context = context,
121                         filter = filter
122                     )
123                 }
124                 toErasedString!!
125             }
126         } else {
127             when {
128                 kotlinStyleNulls && outerAnnotations -> {
129                     if (toAnnotatedString == null) {
130                         toAnnotatedString = toTypeString(
131                             codebase = codebase,
132                             type = psiType,
133                             outerAnnotations = outerAnnotations,
134                             innerAnnotations = innerAnnotations,
135                             erased = erased,
136                             kotlinStyleNulls = kotlinStyleNulls,
137                             context = context,
138                             filter = filter
139                         )
140                     }
141                     toAnnotatedString!!
142                 }
143                 kotlinStyleNulls && innerAnnotations -> {
144                     if (toInnerAnnotatedString == null) {
145                         toInnerAnnotatedString = toTypeString(
146                             codebase = codebase,
147                             type = psiType,
148                             outerAnnotations = outerAnnotations,
149                             innerAnnotations = innerAnnotations,
150                             erased = erased,
151                             kotlinStyleNulls = kotlinStyleNulls,
152                             context = context,
153                             filter = filter
154                         )
155                     }
156                     toInnerAnnotatedString!!
157                 }
158                 else -> {
159                     if (toString == null) {
160                         toString = TypeItem.formatType(
161                             getCanonicalText(
162                                 codebase = codebase,
163                                 owner = context,
164                                 type = psiType,
165                                 annotated = false,
166                                 mapAnnotations = false,
167                                 kotlinStyleNulls = kotlinStyleNulls,
168                                 filter = filter
169                             )
170                         )
171                     }
172                     toString!!
173                 }
174             }
175         }
176     }
177 
toErasedTypeStringnull178     override fun toErasedTypeString(context: Item?): String {
179         return toTypeString(
180             outerAnnotations = false,
181             innerAnnotations = false,
182             erased = true,
183             kotlinStyleNulls = false,
184             context = context
185         )
186     }
187 
arrayDimensionsnull188     override fun arrayDimensions(): Int {
189         return psiType.arrayDimensions
190     }
191 
internalNamenull192     override fun internalName(): String {
193         if (primitive) {
194             val signature = getPrimitiveSignature(toString())
195             if (signature != null) {
196                 return signature
197             }
198         }
199         val sb = StringBuilder()
200         appendJvmSignature(sb, psiType)
201         return sb.toString()
202     }
203 
equalsnull204     override fun equals(other: Any?): Boolean {
205         if (this === other) return true
206 
207         return when (other) {
208             is TypeItem -> TypeItem.equalsWithoutSpace(toTypeString(), other.toTypeString())
209             else -> false
210         }
211     }
212 
asClassnull213     override fun asClass(): PsiClassItem? {
214         if (primitive) {
215             return null
216         }
217         if (asClass == null) {
218             asClass = codebase.findClass(psiType)
219         }
220         return asClass
221     }
222 
asTypeParameternull223     override fun asTypeParameter(context: MemberItem?): TypeParameterItem? {
224         val cls = asClass() ?: return null
225         return cls as? PsiTypeParameterItem
226     }
227 
hashCodenull228     override fun hashCode(): Int {
229         return psiType.hashCode()
230     }
231 
232     override val primitive: Boolean
233         get() = psiType is PsiPrimitiveType
234 
defaultValuenull235     override fun defaultValue(): Any? {
236         return PsiTypesUtil.getDefaultValue(psiType)
237     }
238 
defaultValueStringnull239     override fun defaultValueString(): String {
240         return PsiTypesUtil.getDefaultValueOfType(psiType)
241     }
242 
typeArgumentClassesnull243     override fun typeArgumentClasses(): List<ClassItem> {
244         if (primitive) {
245             return emptyList()
246         }
247 
248         val classes = mutableListOf<ClassItem>()
249         psiType.accept(object : PsiTypeVisitor<PsiType>() {
250             override fun visitType(type: PsiType): PsiType {
251                 return type
252             }
253 
254             override fun visitClassType(classType: PsiClassType): PsiType? {
255                 codebase.findClass(classType)?.let {
256                     if (!it.isTypeParameter && !classes.contains(it)) {
257                         classes.add(it)
258                     }
259                 }
260                 for (type in classType.parameters) {
261                     type.accept(this)
262                 }
263                 return classType
264             }
265 
266             override fun visitWildcardType(wildcardType: PsiWildcardType): PsiType? {
267                 if (wildcardType.isExtends) {
268                     wildcardType.extendsBound.accept(this)
269                 }
270                 if (wildcardType.isSuper) {
271                     wildcardType.superBound.accept(this)
272                 }
273                 if (wildcardType.isBounded) {
274                     wildcardType.bound?.accept(this)
275                 }
276                 return wildcardType
277             }
278 
279             override fun visitPrimitiveType(primitiveType: PsiPrimitiveType): PsiType? {
280                 return primitiveType
281             }
282 
283             override fun visitEllipsisType(ellipsisType: PsiEllipsisType): PsiType? {
284                 ellipsisType.componentType.accept(this)
285                 return ellipsisType
286             }
287 
288             override fun visitArrayType(arrayType: PsiArrayType): PsiType? {
289                 arrayType.componentType.accept(this)
290                 return arrayType
291             }
292 
293             override fun visitLambdaExpressionType(lambdaExpressionType: PsiLambdaExpressionType): PsiType? {
294                 for (superType in lambdaExpressionType.superTypes) {
295                     superType.accept(this)
296                 }
297                 return lambdaExpressionType
298             }
299 
300             override fun visitCapturedWildcardType(capturedWildcardType: PsiCapturedWildcardType): PsiType? {
301                 capturedWildcardType.upperBound.accept(this)
302                 return capturedWildcardType
303             }
304 
305             override fun visitDisjunctionType(disjunctionType: PsiDisjunctionType): PsiType? {
306                 for (type in disjunctionType.disjunctions) {
307                     type.accept(this)
308                 }
309                 return disjunctionType
310             }
311 
312             override fun visitIntersectionType(intersectionType: PsiIntersectionType): PsiType? {
313                 for (type in intersectionType.conjuncts) {
314                     type.accept(this)
315                 }
316                 return intersectionType
317             }
318         })
319 
320         return classes
321     }
322 
convertTypenull323     override fun convertType(replacementMap: Map<String, String>?, owner: Item?): TypeItem {
324         val s = convertTypeString(replacementMap)
325         return create(codebase, codebase.createPsiType(s, owner?.psi()))
326     }
327 
hasTypeArgumentsnull328     override fun hasTypeArguments(): Boolean {
329         val type = psiType
330         return type is PsiClassType && type.hasParameters()
331     }
332 
markRecentnull333     override fun markRecent() {
334         val source = toTypeString(outerAnnotations = true, innerAnnotations = true)
335             .replace(".NonNull", ".RecentlyNonNull")
336         // TODO: Pass in a context!
337         psiType = codebase.createPsiType(source)
338         toAnnotatedString = null
339         toInnerAnnotatedString = null
340     }
341 
scrubAnnotationsnull342     override fun scrubAnnotations() {
343         toAnnotatedString = toTypeString(outerAnnotations = false, innerAnnotations = false)
344         toInnerAnnotatedString = toAnnotatedString
345     }
346 
347     companion object {
getPrimitiveSignaturenull348         private fun getPrimitiveSignature(typeName: String): String? = when (typeName) {
349             "boolean" -> "Z"
350             "byte" -> "B"
351             "char" -> "C"
352             "short" -> "S"
353             "int" -> "I"
354             "long" -> "J"
355             "float" -> "F"
356             "double" -> "D"
357             "void" -> "V"
358             else -> null
359         }
360 
appendJvmSignaturenull361         private fun appendJvmSignature(
362             buffer: StringBuilder,
363             type: PsiType?
364         ): Boolean {
365             if (type == null) {
366                 return false
367             }
368 
369             when (val psiType = TypeConversionUtil.erasure(type)) {
370                 is PsiArrayType -> {
371                     buffer.append('[')
372                     appendJvmSignature(buffer, psiType.componentType)
373                 }
374                 is PsiClassType -> {
375                     val resolved = psiType.resolve() ?: return false
376                     if (!appendJvmTypeName(buffer, resolved)) {
377                         return false
378                     }
379                 }
380                 is PsiPrimitiveType -> buffer.append(getPrimitiveSignature(psiType.canonicalText))
381                 else -> return false
382             }
383             return true
384         }
385 
appendJvmTypeNamenull386         private fun appendJvmTypeName(
387             signature: StringBuilder,
388             outerClass: PsiClass
389         ): Boolean {
390             val className = getInternalName(outerClass) ?: return false
391             signature.append('L').append(className).append(';')
392             return true
393         }
394 
toTypeStringnull395         fun toTypeString(
396             codebase: PsiBasedCodebase,
397             type: PsiType,
398             outerAnnotations: Boolean,
399             innerAnnotations: Boolean,
400             erased: Boolean,
401             kotlinStyleNulls: Boolean,
402             context: Item?,
403             filter: Predicate<Item>?
404         ): String {
405             if (erased) {
406                 // Recurse with raw type and erase=false
407                 return toTypeString(
408                     codebase,
409                     TypeConversionUtil.erasure(type),
410                     outerAnnotations,
411                     innerAnnotations,
412                     false,
413                     kotlinStyleNulls,
414                     context,
415                     filter
416                 )
417             }
418 
419             val typeString =
420                 if (kotlinStyleNulls && (innerAnnotations || outerAnnotations)) {
421                     try {
422                         getCanonicalText(
423                             codebase = codebase,
424                             owner = context,
425                             type = type,
426                             annotated = true,
427                             mapAnnotations = true,
428                             kotlinStyleNulls = kotlinStyleNulls,
429                             filter = filter
430                         )
431                     } catch (ignore: Throwable) {
432                         type.canonicalText
433                     }
434                 } else {
435                     type.canonicalText
436                 }
437 
438             return TypeItem.formatType(typeString)
439         }
440 
getCanonicalTextnull441         private fun getCanonicalText(
442             codebase: PsiBasedCodebase,
443             owner: Item?,
444             type: PsiType,
445             annotated: Boolean,
446             mapAnnotations: Boolean,
447             kotlinStyleNulls: Boolean,
448             filter: Predicate<Item>?
449         ): String {
450             return try {
451                 if (annotated && kotlinStyleNulls) {
452                     // Any nullness annotations on the element to merge in? When we have something like
453                     //  @Nullable String foo
454                     // the Nullable annotation can be on the element itself rather than the type,
455                     // so if we print the type without knowing the nullness annotation on the
456                     // element, we'll think it's unannotated and we'll display it as "String!".
457                     val nullness = owner?.modifiers?.annotations()?.firstOrNull { it.isNullnessAnnotation() }
458                     var elementAnnotations = if (nullness != null) { listOf(nullness) } else null
459 
460                     val implicitNullness = if (owner != null) AnnotationItem.getImplicitNullness(owner) else null
461                     val annotatedType = if (implicitNullness != null) {
462                         val provider = if (implicitNullness == true) {
463                             codebase.getNullableAnnotationProvider()
464                         } else {
465                             codebase.getNonNullAnnotationProvider()
466                         }
467 
468                         if (implicitNullness == false &&
469                             owner is MethodItem &&
470                             (owner.containingClass().isAnnotationType() ||
471                                 owner.containingClass().isEnum() && owner.name() == "values") &&
472                             type is PsiArrayType
473                         ) {
474                             // For arrays in annotations not only is the method itself non null but so
475                             // is the component type
476                             type.componentType.annotate(provider).createArrayType()
477                                 .annotate(provider)
478                         } else if (implicitNullness == false &&
479                             owner is ParameterItem &&
480                             owner.containingMethod().isEnumSyntheticMethod()
481                         ) {
482                             // Workaround the fact that the Kotlin synthetic enum methods
483                             // do not have nullness information; this must be the parameter
484                             // to the valueOf(String) method.
485                             // See https://youtrack.jetbrains.com/issue/KT-39667.
486                             return JAVA_LANG_STRING
487                         } else {
488                             type.annotate(provider)
489                         }
490                     } else if (nullness != null && owner.modifiers.isVarArg() && owner.isKotlin() && type is PsiEllipsisType) {
491                         // Varargs the annotation applies to the component type instead
492                         val nonNullProvider = codebase.getNonNullAnnotationProvider()
493                         val provider = if (nullness.isNonNull()) {
494                             nonNullProvider
495                         } else codebase.getNullableAnnotationProvider()
496                         val componentType = type.componentType.annotate(provider)
497                         elementAnnotations = null
498                         PsiEllipsisType(componentType, nonNullProvider)
499                     } else {
500                         type
501                     }
502                     val printer = PsiTypePrinter(codebase, filter, mapAnnotations, kotlinStyleNulls)
503 
504                     printer.getAnnotatedCanonicalText(
505                         annotatedType,
506                         elementAnnotations
507                     )
508                 } else if (annotated) {
509                     type.getCanonicalText(true)
510                 } else {
511                     type.getCanonicalText(false)
512                 }
513             } catch (e: Throwable) {
514                 return type.getCanonicalText(false)
515             }
516         }
517 
createnull518         fun create(codebase: PsiBasedCodebase, psiType: PsiType): PsiTypeItem {
519             return PsiTypeItem(codebase, psiType)
520         }
521 
createnull522         fun create(codebase: PsiBasedCodebase, original: PsiTypeItem): PsiTypeItem {
523             return PsiTypeItem(codebase, original.psiType)
524         }
525 
typeParameterListnull526         fun typeParameterList(typeList: PsiTypeParameterList?): String? {
527             if (typeList != null && typeList.typeParameters.isNotEmpty()) {
528                 // TODO: Filter the type list classes? Try to construct a typelist of a private API!
529                 // We can't just use typeList.text here, because that just
530                 // uses the declaration from the source, which may not be
531                 // fully qualified - e.g. we might get
532                 //    <T extends View> instead of <T extends android.view.View>
533                 // Therefore, we'll need to compute it ourselves; I can't find
534                 // a utility for this
535                 val sb = StringBuilder()
536                 typeList.accept(object : PsiRecursiveElementVisitor() {
537                     override fun visitElement(element: PsiElement) {
538                         if (element is PsiTypeParameterList) {
539                             val typeParameters = element.typeParameters
540                             if (typeParameters.isEmpty()) {
541                                 return
542                             }
543                             sb.append("<")
544                             var first = true
545                             for (parameter in typeParameters) {
546                                 if (!first) {
547                                     sb.append(", ")
548                                 }
549                                 first = false
550                                 visitElement(parameter)
551                             }
552                             sb.append(">")
553                             return
554                         } else if (element is PsiTypeParameter) {
555                             if (PsiTypeParameterItem.isReified(element)) {
556                                 sb.append("reified ")
557                             }
558                             sb.append(element.name)
559                             // TODO: How do I get super -- e.g. "Comparable<? super T>"
560                             val extendsList = element.extendsList
561                             val refList = extendsList.referenceElements
562                             if (refList.isNotEmpty()) {
563                                 sb.append(" extends ")
564                                 var first = true
565                                 for (refElement in refList) {
566                                     if (!first) {
567                                         sb.append(" & ")
568                                     } else {
569                                         first = false
570                                     }
571 
572                                     if (refElement is PsiJavaCodeReferenceElement) {
573                                         visitElement(refElement)
574                                         continue
575                                     }
576                                     val resolved = refElement.resolve()
577                                     if (resolved is PsiClass) {
578                                         sb.append(resolved.qualifiedName ?: resolved.name)
579                                         resolved.typeParameterList?.accept(this)
580                                     } else {
581                                         sb.append(refElement.referenceName)
582                                     }
583                                 }
584                             } else {
585                                 val extendsListTypes = element.extendsListTypes
586                                 if (extendsListTypes.isNotEmpty()) {
587                                     sb.append(" extends ")
588                                     var first = true
589                                     for (type in extendsListTypes) {
590                                         if (!first) {
591                                             sb.append(" & ")
592                                         } else {
593                                             first = false
594                                         }
595                                         val resolved = type.resolve()
596                                         if (resolved == null) {
597                                             sb.append(type.className)
598                                         } else {
599                                             sb.append(resolved.qualifiedName ?: resolved.name)
600                                             resolved.typeParameterList?.accept(this)
601                                         }
602                                     }
603                                 }
604                             }
605                             return
606                         } else if (element is PsiJavaCodeReferenceElement) {
607                             val resolved = element.resolve()
608                             if (resolved is PsiClass) {
609                                 if (resolved.qualifiedName == null) {
610                                     sb.append(resolved.name)
611                                 } else {
612                                     sb.append(resolved.qualifiedName)
613                                 }
614                                 val typeParameters = element.parameterList
615                                 if (typeParameters != null) {
616                                     val typeParameterElements = typeParameters.typeParameterElements
617                                     if (typeParameterElements.isEmpty()) {
618                                         return
619                                     }
620 
621                                     // When reading in this from bytecode, the order is sometimes wrong
622                                     // (for example, for
623                                     //    public interface BaseStream<T, S extends BaseStream<T, S>>
624                                     // the extends type BaseStream<T, S> will return the typeParameterElements
625                                     // as [S,T] instead of [T,S]. However, the typeParameters.typeArguments
626                                     // list is correct, so order the elements by the typeArguments array instead
627 
628                                     // Special case: just one type argument: no sorting issue
629                                     if (typeParameterElements.size == 1) {
630                                         sb.append("<")
631                                         var first = true
632                                         for (parameter in typeParameterElements) {
633                                             if (!first) {
634                                                 sb.append(", ")
635                                             }
636                                             first = false
637                                             visitElement(parameter)
638                                         }
639                                         sb.append(">")
640                                         return
641                                     }
642 
643                                     // More than one type argument
644 
645                                     val typeArguments = typeParameters.typeArguments
646                                     if (typeArguments.isNotEmpty()) {
647                                         sb.append("<")
648                                         var first = true
649                                         for (parameter in typeArguments) {
650                                             if (!first) {
651                                                 sb.append(", ")
652                                             }
653                                             first = false
654                                             // Try to match up a type parameter element
655                                             var found = false
656                                             for (typeElement in typeParameterElements) {
657                                                 if (parameter == typeElement.type) {
658                                                     found = true
659                                                     visitElement(typeElement)
660                                                     break
661                                                 }
662                                             }
663                                             if (!found) {
664                                                 // No type element matched: use type instead
665                                                 val classType = PsiTypesUtil.getPsiClass(parameter)
666                                                 if (classType != null) {
667                                                     visitElement(classType)
668                                                 } else {
669                                                     sb.append(parameter.canonicalText)
670                                                 }
671                                             }
672                                         }
673                                         sb.append(">")
674                                     }
675                                 }
676                                 return
677                             }
678                         } else if (element is PsiTypeElement) {
679                             val type = element.type
680                             if (type is PsiWildcardType) {
681                                 sb.append("?")
682                                 if (type.isBounded) {
683                                     if (type.isExtends) {
684                                         sb.append(" extends ")
685                                         sb.append(type.extendsBound.canonicalText)
686                                     }
687                                     if (type.isSuper) {
688                                         sb.append(" super ")
689                                         sb.append(type.superBound.canonicalText)
690                                     }
691                                 }
692                                 return
693                             }
694                             sb.append(type.canonicalText)
695                             return
696                         } else if (element is PsiJavaToken && element.tokenType == JavaTokenType.COMMA) {
697                             sb.append(",")
698                             if (compatibility.spaceAfterCommaInTypes) {
699                                 if (element.nextSibling == null || element.nextSibling !is PsiWhiteSpace) {
700                                     sb.append(" ")
701                                 }
702                             }
703                             return
704                         }
705                         if (element.firstChild == null) { // leaf nodes only
706                             if (element is PsiCompiledElement) {
707                                 if (element is PsiReferenceList) {
708                                     val referencedTypes = element.referencedTypes
709                                     var first = true
710                                     for (referenceType in referencedTypes) {
711                                         if (first) {
712                                             first = false
713                                         } else {
714                                             sb.append(", ")
715                                         }
716                                         sb.append(referenceType.canonicalText)
717                                     }
718                                 }
719                             } else {
720                                 sb.append(element.text)
721                             }
722                         }
723                         super.visitElement(element)
724                     }
725                 })
726 
727                 val typeString = sb.toString()
728                 return TypeItem.cleanupGenerics(typeString)
729             }
730 
731             return null
732         }
733 
typeParameterClassesnull734         fun typeParameterClasses(codebase: PsiBasedCodebase, typeList: PsiTypeParameterList?): List<ClassItem> {
735             if (typeList != null && typeList.typeParameters.isNotEmpty()) {
736                 val list = mutableListOf<ClassItem>()
737                 typeList.accept(object : PsiRecursiveElementVisitor() {
738                     override fun visitElement(element: PsiElement) {
739                         if (element is PsiTypeParameterList) {
740                             val typeParameters = element.typeParameters
741                             for (parameter in typeParameters) {
742                                 visitElement(parameter)
743                             }
744                             return
745                         } else if (element is PsiTypeParameter) {
746                             val extendsList = element.extendsList
747                             val refList = extendsList.referenceElements
748                             if (refList.isNotEmpty()) {
749                                 for (refElement in refList) {
750                                     if (refElement is PsiJavaCodeReferenceElement) {
751                                         visitElement(refElement)
752                                         continue
753                                     }
754                                     val resolved = refElement.resolve()
755                                     if (resolved is PsiClass) {
756                                         addRealClass(
757                                             list,
758                                             codebase.findOrCreateClass(resolved)
759                                         )
760                                         resolved.typeParameterList?.accept(this)
761                                     }
762                                 }
763                             } else {
764                                 val extendsListTypes = element.extendsListTypes
765                                 if (extendsListTypes.isNotEmpty()) {
766                                     for (type in extendsListTypes) {
767                                         val resolved = type.resolve()
768                                         if (resolved != null) {
769                                             addRealClass(
770                                                 list, codebase.findOrCreateClass(resolved)
771                                             )
772                                             resolved.typeParameterList?.accept(this)
773                                         }
774                                     }
775                                 }
776                             }
777                             return
778                         } else if (element is PsiJavaCodeReferenceElement) {
779                             val resolved = element.resolve()
780                             if (resolved is PsiClass) {
781                                 addRealClass(
782                                     list,
783                                     codebase.findOrCreateClass(resolved)
784                                 )
785                                 element.parameterList?.accept(this)
786                                 return
787                             }
788                         } else if (element is PsiTypeElement) {
789                             val type = element.type
790                             if (type is PsiWildcardType) {
791                                 if (type.isBounded) {
792                                     addRealClass(
793                                         codebase,
794                                         list, type.bound
795                                     )
796                                 }
797                                 if (type.isExtends) {
798                                     addRealClass(
799                                         codebase,
800                                         list, type.extendsBound
801                                     )
802                                 }
803                                 if (type.isSuper) {
804                                     addRealClass(
805                                         codebase,
806                                         list, type.superBound
807                                     )
808                                 }
809                                 return
810                             }
811                             return
812                         }
813                         super.visitElement(element)
814                     }
815                 })
816 
817                 return list
818             } else {
819                 return emptyList()
820             }
821         }
822 
addRealClassnull823         private fun addRealClass(codebase: PsiBasedCodebase, classes: MutableList<ClassItem>, type: PsiType?) {
824             codebase.findClass(type ?: return)?.let {
825                 addRealClass(classes, it)
826             }
827         }
828 
addRealClassnull829         private fun addRealClass(classes: MutableList<ClassItem>, cls: ClassItem) {
830             if (!cls.isTypeParameter && !classes.contains(cls)) { // typically small number of items, don't need Set
831                 classes.add(cls)
832             }
833         }
834     }
835 }
836