• 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
18 
19 /**
20  * Represents a {@link https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html Class}
21  *
22  * If you need to model array dimensions or resolved type parameters, see {@link
23  * com.android.tools.metalava.model.TypeItem} instead
24  */
25 @MetalavaApi
26 interface ClassItem : ClassContentItem, SelectableItem, TypeParameterListOwner {
27     /**
28      * The qualified name of a class. In class foo.bar.Outer.Inner, the qualified name is the whole
29      * thing.
30      */
31     @MetalavaApi fun qualifiedName(): String
32 
33     /** The simple name of a class. In class foo.bar.Outer.Inner, the simple name is "Inner" */
34     fun simpleName(): String
35 
36     /** The full name of a class. In class foo.bar.Outer.Inner, the full name is "Outer.Inner" */
37     fun fullName(): String
38 
39     /** Is this a nested class? */
40     @MetalavaApi fun isNestedClass() = containingClass() != null
41 
42     /** Is this a top level class? */
43     fun isTopLevelClass(): Boolean = containingClass() == null
44 
45     /** The origin of this class. */
46     override val origin: ClassOrigin
47 
48     /** This [ClassItem] and all of its nested classes, recursively */
49     fun allClasses(): Sequence<ClassItem> {
50         return sequenceOf(this).plus(nestedClasses().asSequence().flatMap { it.allClasses() })
51     }
52 
53     override fun parent(): SelectableItem? = containingClass() ?: containingPackage()
54 
55     override val effectivelyDeprecated: Boolean
56         get() = originallyDeprecated || containingClass()?.effectivelyDeprecated == true
57 
58     /** Returns the internal name of the class, as seen in bytecode */
59     fun internalName(): String {
60         var curr: ClassItem? = this
61         while (curr?.containingClass() != null) {
62             curr = curr.containingClass()
63         }
64 
65         if (curr == null) {
66             return fullName().replace('.', '$')
67         }
68 
69         return curr.containingPackage().qualifiedName().replace('.', '/') +
70             "/" +
71             fullName().replace('.', '$')
72     }
73 
74     /**
75      * The super class of this class, if any.
76      *
77      * Interfaces always return `null` for this.
78      */
79     @MetalavaApi fun superClass() = superClassType()?.asClass()
80 
81     /** All super classes, if any */
82     fun allSuperClasses(): Sequence<ClassItem> {
83         return generateSequence(superClass()) { it.superClass() }
84     }
85 
86     /**
87      * The super class type of this class, if any. The difference between this and [superClass] is
88      * that the type reference can include type arguments; e.g. in "class MyList extends
89      * List<String>" the super class is java.util.List and the super class type is
90      * java.util.List<java.lang.String>.
91      */
92     fun superClassType(): ClassTypeItem?
93 
94     /** Returns true if this class extends the given class (includes self) */
95     fun extends(qualifiedName: String): Boolean {
96         if (qualifiedName() == qualifiedName) {
97             return true
98         }
99 
100         val superClass = superClass()
101         return superClass?.extends(qualifiedName)
102             ?: when {
103                 isEnum() -> qualifiedName == JAVA_LANG_ENUM
104                 isAnnotationType() -> qualifiedName == JAVA_LANG_ANNOTATION
105                 else -> qualifiedName == JAVA_LANG_OBJECT
106             }
107     }
108 
109     /** Returns true if this class implements the given interface (includes self) */
110     fun implements(qualifiedName: String): Boolean {
111         if (qualifiedName() == qualifiedName) {
112             return true
113         }
114 
115         interfaceTypes().forEach {
116             val cls = it.asClass()
117             if (cls != null && cls.implements(qualifiedName)) {
118                 return true
119             }
120         }
121 
122         // Might be implementing via superclass
123         if (superClass()?.implements(qualifiedName) == true) {
124             return true
125         }
126 
127         return false
128     }
129 
130     /** Returns true if this class extends or implements the given class or interface */
131     fun extendsOrImplements(qualifiedName: String): Boolean =
132         extends(qualifiedName) || implements(qualifiedName)
133 
134     /** Any interfaces implemented by this class */
135     @MetalavaApi fun interfaceTypes(): List<ClassTypeItem>
136 
137     /**
138      * All classes and interfaces implemented (by this class and its super classes and the
139      * interfaces themselves)
140      */
141     fun allInterfaces(): Sequence<ClassItem>
142 
143     /**
144      * Any classes nested in this class, that includes inner classes which are just non-static
145      * nested classes.
146      */
147     fun nestedClasses(): List<ClassItem>
148 
149     /** The constructors in this class */
150     @MetalavaApi fun constructors(): List<ConstructorItem>
151 
152     /** Whether this class has an implicit default constructor */
153     fun hasImplicitDefaultConstructor(): Boolean
154 
155     /** The non-constructor methods in this class */
156     @MetalavaApi fun methods(): List<MethodItem>
157 
158     /** The properties in this class */
159     fun properties(): List<PropertyItem>
160 
161     /** The fields in this class */
162     @MetalavaApi fun fields(): List<FieldItem>
163 
164     /** The members in this class: constructors, methods, fields/enum constants */
165     fun members(): Sequence<MemberItem> {
166         return fields().asSequence().plus(constructors().asSequence()).plus(methods().asSequence())
167     }
168 
169     val classKind: ClassKind
170 
171     /** Whether this class is an interface */
172     fun isInterface() = classKind == ClassKind.INTERFACE
173 
174     /** Whether this class is an annotation type */
175     fun isAnnotationType() = classKind == ClassKind.ANNOTATION_TYPE
176 
177     /** Whether this class is an enum */
178     fun isEnum() = classKind == ClassKind.ENUM
179 
180     /** Whether this class is a regular class (not an interface, not an enum, etc) */
181     fun isClass() = classKind == ClassKind.CLASS
182 
183     /**
184      * Whether this class is a File Facade class, i.e. a `*Kt` class that contains declarations
185      * which do not belong to a Kotlin class, e.g. top-level functions, properties, etc.
186      */
187     fun isFileFacade() = false
188 
189     /** The containing class, for nested classes */
190     @MetalavaApi override fun containingClass(): ClassItem?
191 
192     /** The containing package */
193     override fun containingPackage(): PackageItem
194 
195     /** Gets the type for this class */
196     override fun type(): ClassTypeItem
197 
198     override fun setType(type: TypeItem) =
199         error("Cannot call setType(TypeItem) on PackageItem: $this")
200 
201     /** True if [freeze] has been called on this, false otherwise. */
202     val frozen: Boolean
203 
204     /**
205      * Freeze this [ClassItem] so it cannot be mutated.
206      *
207      * A frozen [ClassItem] cannot have new members (including nested classes) added or its
208      * modifiers mutated.
209      *
210      * Freezing a [ClassItem] will also freeze its super types.
211      */
212     fun freeze()
213 
214     override fun findCorrespondingItemIn(
215         codebase: Codebase,
216         superMethods: Boolean,
217         duplicate: Boolean,
218     ) = codebase.findClass(qualifiedName())
219 
220     /** Returns true if this class has type parameters */
221     fun hasTypeVariables(): Boolean
222 
223     fun isJavaLangObject(): Boolean {
224         return qualifiedName() == JAVA_LANG_OBJECT
225     }
226 
227     // Mutation APIs: Used to "fix up" the API hierarchy to only expose visible parts of the API.
228 
229     // This replaces the interface types implemented by this class
230     fun setInterfaceTypes(interfaceTypes: List<ClassTypeItem>)
231 
232     /** The primary constructor for this class in Kotlin, if present. */
233     val primaryConstructor: ConstructorItem?
234         get() = constructors().singleOrNull { it.isPrimary }
235 
236     override fun baselineElementId() = qualifiedName()
237 
238     override fun accept(visitor: ItemVisitor) {
239         visitor.visit(this)
240     }
241 
242     override fun equalsToItem(other: Any?): Boolean {
243         if (this === other) return true
244         if (other !is ClassItem) return false
245 
246         return qualifiedName() == other.qualifiedName()
247     }
248 
249     override fun hashCodeForItem(): Int {
250         return qualifiedName().hashCode()
251     }
252 
253     override fun toStringForItem() = "class ${qualifiedName()}"
254 
255     companion object {
256         /** Looks up the retention policy for the given class */
257         fun findRetention(cls: ClassItem): AnnotationRetention {
258             val modifiers = cls.modifiers
259             val annotation = modifiers.findAnnotation(AnnotationItem::isRetention)
260             val value = annotation?.findAttribute(ANNOTATION_ATTR_VALUE)
261             val source = value?.legacyValue?.toSource()
262             return when {
263                 source == null -> AnnotationRetention.getDefault(cls)
264                 source.contains("CLASS") -> AnnotationRetention.CLASS
265                 source.contains("RUNTIME") -> AnnotationRetention.RUNTIME
266                 source.contains("SOURCE") -> AnnotationRetention.SOURCE
267                 source.contains("BINARY") -> AnnotationRetention.BINARY
268                 else -> AnnotationRetention.getDefault(cls)
269             }
270         }
271 
272         // Same as doclava1 (modulo the new handling when class names match)
273         val comparator: Comparator<in ClassItem> = Comparator { o1, o2 ->
274             val delta = o1.fullName().compareTo(o2.fullName())
275             if (delta == 0) {
276                 o1.qualifiedName().compareTo(o2.qualifiedName())
277             } else {
278                 delta
279             }
280         }
281 
282         /** A partial ordering over [ClassItem] comparing [ClassItem.fullName]. */
283         val fullNameComparator: Comparator<ClassItem> = Comparator.comparing { it.fullName() }
284 
285         /** A total ordering over [ClassItem] comparing [ClassItem.qualifiedName]. */
286         private val qualifiedComparator: Comparator<ClassItem> =
287             Comparator.comparing { it.qualifiedName() }
288 
289         /**
290          * A total ordering over [ClassItem] comparing [ClassItem.fullName] first and then
291          * [ClassItem.qualifiedName].
292          */
293         val fullNameThenQualifierComparator: Comparator<ClassItem> =
294             fullNameComparator.thenComparing(qualifiedComparator)
295 
296         fun classNameSorter(): Comparator<in ClassItem> = ClassItem.qualifiedComparator
297     }
298 
299     fun findMethod(
300         template: MethodItem,
301         includeSuperClasses: Boolean = false,
302         includeInterfaces: Boolean = false
303     ): MethodItem? {
304         methods()
305             .asSequence()
306             .filter { it.matches(template) }
307             .forEach {
308                 return it
309             }
310 
311         if (includeSuperClasses) {
312             superClass()?.findMethod(template, true, includeInterfaces)?.let {
313                 return it
314             }
315         }
316 
317         if (includeInterfaces) {
318             for (itf in interfaceTypes()) {
319                 val cls = itf.asClass() ?: continue
320                 cls.findMethod(template, includeSuperClasses, true)?.let {
321                     return it
322                 }
323             }
324         }
325         return null
326     }
327 
328     /**
329      * Finds a method matching the given method that satisfies the given predicate, considering all
330      * methods defined on this class and its super classes
331      */
332     fun findPredicateMethodWithSuper(template: MethodItem, filter: FilterPredicate?): MethodItem? {
333         val method = findMethod(template, true, true)
334         if (method == null) {
335             return null
336         }
337         if (filter == null || filter.test(method)) {
338             return method
339         }
340         return method.findPredicateSuperMethod(filter)
341     }
342 
343     fun findConstructor(template: ConstructorItem): ConstructorItem? {
344         constructors()
345             .asSequence()
346             .filter { it.matches(template) }
347             .forEach {
348                 return it
349             }
350         return null
351     }
352 
353     fun findField(
354         fieldName: String,
355         includeSuperClasses: Boolean = false,
356         includeInterfaces: Boolean = false
357     ): FieldItem? {
358         val field = fields().firstOrNull { it.name() == fieldName }
359         if (field != null) {
360             return field
361         }
362 
363         if (includeSuperClasses) {
364             superClass()?.findField(fieldName, true, includeInterfaces)?.let {
365                 return it
366             }
367         }
368 
369         if (includeInterfaces) {
370             for (itf in interfaceTypes()) {
371                 val cls = itf.asClass() ?: continue
372                 cls.findField(fieldName, includeSuperClasses, true)?.let {
373                     return it
374                 }
375             }
376         }
377         return null
378     }
379 
380     /**
381      * Find the [MethodItem] in this.
382      *
383      * It will look for [MethodItem]s whose [MethodItem.name] is equal to [methodName].
384      *
385      * Out of those matching items it will select the first [MethodItem] whose parameters match the
386      * supplied parameters string. Parameters are matched against a candidate [MethodItem] as
387      * follows:
388      * * The [parameters] string is split on `,` and trimmed and then each item in the list is
389      *   matched with the corresponding [ParameterItem] in `candidate.parameters()` as follows:
390      * * Everything after `<` is removed.
391      * * The result is compared to the result of calling [TypeItem.toErasedTypeString]`(candidate)`
392      *   on the [ParameterItem.type].
393      *
394      * If every parameter matches then the matched [MethodItem] is returned. If no `candidate`
395      * matches then it returns 'null`.
396      *
397      * @param methodName the name of the method or [simpleName] if looking for constructors.
398      * @param parameters the comma separated erased types of the parameters.
399      */
400     fun findMethod(methodName: String, parameters: String) =
401         methods().firstOrNull { it.name() == methodName && parametersMatch(it, parameters) }
402 
403     /**
404      * Find the [ConstructorItem] in this.
405      *
406      * Out of those matching items it will select the first [ConstructorItem] whose parameters match
407      * the supplied parameters string. Parameters are matched against a candidate [ConstructorItem]
408      * as follows:
409      * * The [parameters] string is split on `,` and trimmed and then each item in the list is
410      *   matched with the corresponding [ParameterItem] in `candidate.parameters()` as follows:
411      * * Everything after `<` is removed.
412      * * The result is compared to the result of calling [TypeItem.toErasedTypeString]`(candidate)`
413      *   on the [ParameterItem.type].
414      *
415      * If every parameter matches then the matched [ConstructorItem] is returned. If no `candidate`
416      * matches then it returns 'null`.
417      *
418      * @param parameters the comma separated erased types of the parameters.
419      */
420     fun findConstructor(parameters: String) =
421         constructors().firstOrNull { parametersMatch(it, parameters) }
422 
423     /**
424      * Find the [CallableItem] in this.
425      *
426      * If [name] is [simpleName] then call [findConstructor] else call [findMethod].
427      */
428     fun findCallable(name: String, parameters: String) =
429         if (name == simpleName()) findConstructor(parameters) else findMethod(name, parameters)
430 
431     private fun parametersMatch(callable: CallableItem, description: String): Boolean {
432         val parameterStrings =
433             description.splitToSequence(",").map(String::trim).filter(String::isNotEmpty).toList()
434         val parameters = callable.parameters()
435         if (parameters.size != parameterStrings.size) {
436             return false
437         }
438         for (i in parameters.indices) {
439             var parameterString = parameterStrings[i]
440             val index = parameterString.indexOf('<')
441             if (index != -1) {
442                 parameterString = parameterString.substring(0, index)
443             }
444             val parameter = parameters[i].type().toErasedTypeString()
445             if (parameter != parameterString) {
446                 return false
447             }
448         }
449 
450         return true
451     }
452 
453     /** Returns the corresponding source file, if any */
454     fun sourceFile(): SourceFile?
455 
456     /** If this class is an annotation type, returns the retention of this class */
457     fun getRetention(): AnnotationRetention
458 
459     /**
460      * Return superclass matching the given predicate. When a superclass doesn't match, we'll keep
461      * crawling up the tree until we find someone who matches.
462      */
463     fun filteredSuperclass(predicate: FilterPredicate): ClassItem? {
464         val superClass = superClass() ?: return null
465         return if (predicate.test(superClass)) {
466             superClass
467         } else {
468             superClass.filteredSuperclass(predicate)
469         }
470     }
471 
472     fun filteredSuperClassType(predicate: FilterPredicate): ClassTypeItem? {
473         var superClassType: ClassTypeItem? = superClassType() ?: return null
474         var prev: ClassItem? = null
475         while (superClassType != null) {
476             val superClass = superClassType.asClass() ?: return null
477             if (predicate.test(superClass)) {
478                 if (prev == null || superClass == superClass()) {
479                     // Direct reference; no need to map type variables
480                     return superClassType
481                 }
482                 if (!superClassType.hasTypeArguments()) {
483                     // No type variables - also no need for mapping
484                     return superClassType
485                 }
486 
487                 return superClassType.convertType(this, prev) as ClassTypeItem
488             }
489 
490             prev = superClass
491             superClassType = superClass.superClassType()
492         }
493 
494         return null
495     }
496 
497     /**
498      * Return methods matching the given predicate. Forcibly includes local methods that override a
499      * matching method in an ancestor class.
500      */
501     fun filteredMethods(
502         predicate: FilterPredicate,
503         includeSuperClassMethods: Boolean = false
504     ): Collection<MethodItem> {
505         val methods = LinkedHashSet<MethodItem>()
506         for (method in methods()) {
507             if (predicate.test(method) || method.findPredicateSuperMethod(predicate) != null) {
508                 // val duplicated = method.duplicate(this)
509                 // methods.add(duplicated)
510                 methods.remove(method)
511                 methods.add(method)
512             }
513         }
514         if (includeSuperClassMethods) {
515             superClass()?.filteredMethods(predicate, includeSuperClassMethods)?.let {
516                 methods += it
517             }
518         }
519         return methods
520     }
521 
522     /** Returns the constructors that match the given predicate */
523     fun filteredConstructors(predicate: FilterPredicate): Sequence<ConstructorItem> {
524         return constructors().asSequence().filter { predicate.test(it) }
525     }
526 
527     /**
528      * Return fields matching the given predicate. Also clones fields from ancestors that would
529      * match had they been defined in this class.
530      */
531     fun filteredFields(predicate: FilterPredicate, showUnannotated: Boolean): List<FieldItem> {
532         val fields = LinkedHashSet<FieldItem>()
533         if (showUnannotated) {
534             for (clazz in allInterfaces()) {
535                 // If this class is an interface then it will be included in allInterfaces(). If it
536                 // is a class then it will not be included. Either way, this class' fields will be
537                 // handled below so there is no point in processing the fields here.
538                 if (clazz == this) {
539                     continue
540                 }
541                 if (!clazz.isInterface()) {
542                     continue
543                 }
544                 for (field in clazz.fields()) {
545                     if (!predicate.test(field)) {
546                         val duplicated = field.duplicate(this)
547                         if (predicate.test(duplicated)) {
548                             fields.remove(duplicated)
549                             fields.add(duplicated)
550                         }
551                     }
552                 }
553             }
554 
555             val superClass = superClass()
556             if (superClass != null && !predicate.test(superClass) && predicate.test(this)) {
557                 // Include constants from hidden super classes.
558                 for (field in superClass.fields()) {
559                     val fieldModifiers = field.modifiers
560                     if (
561                         !fieldModifiers.isStatic() ||
562                             !fieldModifiers.isFinal() ||
563                             !fieldModifiers.isPublic()
564                     ) {
565                         continue
566                     }
567                     if (!field.originallyHidden) {
568                         val duplicated = field.duplicate(this)
569                         if (predicate.test(duplicated)) {
570                             fields.remove(duplicated)
571                             fields.add(duplicated)
572                         }
573                     }
574                 }
575             }
576         }
577         for (field in fields()) {
578             if (predicate.test(field)) {
579                 fields.remove(field)
580                 fields.add(field)
581             }
582         }
583         if (fields.isEmpty()) {
584             return emptyList()
585         }
586         val list = fields.toMutableList()
587         list.sortWith(FieldItem.comparator)
588         return list
589     }
590 
591     fun filteredInterfaceTypes(predicate: FilterPredicate): Collection<ClassTypeItem> {
592         val interfaceTypes =
593             filteredInterfaceTypes(
594                 predicate,
595                 LinkedHashSet(),
596                 includeSelf = false,
597                 includeParents = false,
598                 target = this
599             )
600 
601         return interfaceTypes
602     }
603 
604     fun allInterfaceTypes(predicate: FilterPredicate): Collection<TypeItem> {
605         val interfaceTypes =
606             filteredInterfaceTypes(
607                 predicate,
608                 LinkedHashSet(),
609                 includeSelf = false,
610                 includeParents = true,
611                 target = this
612             )
613         if (interfaceTypes.isEmpty()) {
614             return interfaceTypes
615         }
616 
617         return interfaceTypes
618     }
619 
620     private fun filteredInterfaceTypes(
621         predicate: FilterPredicate,
622         types: LinkedHashSet<ClassTypeItem>,
623         includeSelf: Boolean,
624         includeParents: Boolean,
625         target: ClassItem
626     ): LinkedHashSet<ClassTypeItem> {
627         val superClassType = superClassType()
628         if (superClassType != null) {
629             val superClass = superClassType.asClass()
630             if (superClass != null) {
631                 if (!predicate.test(superClass)) {
632                     superClass.filteredInterfaceTypes(
633                         predicate,
634                         types,
635                         true,
636                         includeParents,
637                         target
638                     )
639                 } else if (includeSelf && superClass.isInterface()) {
640                     types.add(superClassType)
641                     if (includeParents) {
642                         superClass.filteredInterfaceTypes(
643                             predicate,
644                             types,
645                             true,
646                             includeParents,
647                             target
648                         )
649                     }
650                 }
651             }
652         }
653         for (type in interfaceTypes()) {
654             val cls = type.asClass() ?: continue
655             if (predicate.test(cls)) {
656                 if (hasTypeVariables() && type.hasTypeArguments()) {
657                     val replacementMap = target.mapTypeVariables(this)
658                     if (replacementMap.isNotEmpty()) {
659                         val mapped = type.convertType(replacementMap)
660                         types.add(mapped)
661                         continue
662                     }
663                 }
664                 types.add(type)
665                 if (includeParents) {
666                     cls.filteredInterfaceTypes(predicate, types, true, includeParents, target)
667                 }
668             } else {
669                 cls.filteredInterfaceTypes(predicate, types, true, includeParents, target)
670             }
671         }
672         return types
673     }
674 
675     /**
676      * Creates a map of type parameters of the target class to the type variables substituted for
677      * those parameters by this class.
678      *
679      * If this class is declared as `class A<X,Y> extends B<X,Y>`, and target class `B` is declared
680      * as `class B<M,N>`, this method returns the map `{M->X, N->Y}`.
681      *
682      * There could be multiple intermediate classes between this class and the target class, and in
683      * some cases we could be substituting in a concrete class, e.g. if this class is declared as
684      * `class MyClass extends Parent<String,Number>` and target class `Parent` is declared as `class
685      * Parent<M,N>` would return the map `{M->java.lang.String, N>java.lang.Number}`.
686      *
687      * The target class can be an interface. If the interface can be found through multiple paths in
688      * the class hierarchy, this method returns the mapping from the first path found in terms of
689      * declaration order. For instance, given declarations `class C<X, Y> implements I1<X>, I2<Y>`,
690      * `interface I1<T1> implements Root<T1>`, `interface I2<T2> implements Root<T2>`, and
691      * `interface Root<T>`, this method will return `{T->X}` as the mapping from `C` to `Root`, not
692      * `{T->Y}`.
693      */
694     fun mapTypeVariables(target: ClassItem): TypeParameterBindings {
695         // Gather the supertypes to check for [target]. It is only possible for [target] to be found
696         // in the class hierarchy through this class's interfaces if [target] is an interface.
697         val candidates =
698             if (target.isInterface()) {
699                 interfaceTypes() + superClassType()
700             } else {
701                 listOf(superClassType())
702             }
703 
704         for (superClassType in candidates.filterNotNull()) {
705             superClassType as? ClassTypeItem ?: continue
706             // Get the class from the class type so that its type parameters can be accessed.
707             val declaringClass = superClassType.asClass() ?: continue
708 
709             if (declaringClass.qualifiedName() == target.qualifiedName()) {
710                 // The target has been found, return the map directly.
711                 return mapTypeVariables(declaringClass, superClassType)
712             } else {
713                 // This superClassType isn't target, but maybe it has target as a superclass.
714                 val nextLevelMap = declaringClass.mapTypeVariables(target)
715                 if (nextLevelMap.isNotEmpty()) {
716                     val thisLevelMap = mapTypeVariables(declaringClass, superClassType)
717                     // Link the two maps by removing intermediate type variables.
718                     return nextLevelMap.mapValues { (_, value) ->
719                         (value as? VariableTypeItem?)?.let { thisLevelMap[it.asTypeParameter] }
720                             ?: value
721                     }
722                 }
723             }
724         }
725         return emptyMap()
726     }
727 
728     /**
729      * Creates a map between the type parameters of [declaringClass] and the arguments of
730      * [classTypeItem].
731      */
732     private fun mapTypeVariables(
733         declaringClass: ClassItem,
734         classTypeItem: ClassTypeItem
735     ): TypeParameterBindings {
736         // Don't include arguments of class types, for consistency with the old psi implementation.
737         // i.e. if the mapping is from `T -> List<String>` then just use `T -> List`.
738         // TODO (b/319300404): remove this section
739         val classTypeArguments =
740             classTypeItem.arguments.map {
741                 if (it is ClassTypeItem && it.arguments.isNotEmpty()) {
742                     it.substitute(arguments = emptyList())
743                 } else {
744                     it
745                 }
746                 // Although a `ClassTypeItem`'s arguments can be `WildcardTypeItem`s as well as
747                 // `ReferenceTypeItem`s, a `ClassTypeItem` used in an extends or implements list
748                 // cannot have a `WildcardTypeItem` as an argument so this cast is safe. See
749                 // https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-Superclass
750                 as ReferenceTypeItem
751             }
752         return declaringClass.typeParameterList.zip(classTypeArguments).toMap()
753     }
754 
755     /**
756      * Creates a default constructor in this class.
757      *
758      * Default constructors that are added by Java have the same visibility as their class which is
759      * the default behavior of this method if no [visibility] is provided. However, this is also
760      * used to create default constructors in order for stub classes to compile and as they do not
761      * appear in the API they need to be marked as package private so this method allows the
762      * [visibility] to be explicitly specified by the caller.
763      *
764      * @param visibility the visibility of the constructor, defaults to the same as this class.
765      */
766     fun createDefaultConstructor(
767         visibility: VisibilityLevel = modifiers.getVisibilityLevel()
768     ): ConstructorItem
769 
770     fun addMethod(method: MethodItem)
771 
772     /**
773      * Return true if a [ClassItem] could be subclassed, i.e. is not final or sealed and has at
774      * least one accessible constructor.
775      */
776     fun isExtensible() =
777         !modifiers.isFinal() &&
778             !modifiers.isSealed() &&
779             constructors().any { it.isPublic || it.isProtected }
780 }
781