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 import com.android.tools.metalava.model.TypeItem.Companion.equals
20 import java.util.Objects
21 import java.util.function.Predicate
22 
23 /**
24  * Whether metalava supports type use annotations. Note that you can't just turn this flag back on;
25  * you have to also add TYPE_USE back to the handful of nullness annotations in
26  * stub-annotations/src/main/java/.
27  */
28 const val SUPPORT_TYPE_USE_ANNOTATIONS = false
29 
30 /**
31  * Represents a {@link https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Type.html Type}
32  */
33 @MetalavaApi
34 interface TypeItem {
35     /** Modifiers for the type. Contains type-use annotation information. */
36     val modifiers: TypeModifiers
37 
38     fun accept(visitor: TypeVisitor)
39 
40     fun accept(visitor: MultipleTypeVisitor, other: List<TypeItem>)
41 
42     /**
43      * Whether this type is equal to [other], not considering modifiers.
44      *
45      * This is implemented on each sub-interface of [TypeItem] instead of [equals] because
46      * interfaces are not allowed to implement [equals]. An [equals] implementation is provided by
47      * [DefaultTypeItem].
48      */
49     fun equalToType(other: TypeItem?): Boolean
50 
51     /**
52      * Hashcode for the type.
53      *
54      * This is implemented on each sub-interface of [TypeItem] instead of [hashCode] because
55      * interfaces are not allowed to implement [hashCode]. A [hashCode] implementation is provided
56      * by [DefaultTypeItem].
57      */
58     fun hashCodeForType(): Int
59 
60     /**
61      * Provide a helpful description of the type, for use in error messages.
62      *
63      * This is not suitable for use in signature or stubs as while it defaults to [toTypeString] for
64      * most types it is overridden by others to provide additional information.
65      */
66     fun description(): String = toTypeString()
67 
68     /**
69      * Generates a string for this type.
70      *
71      * @param annotations For a type like this: @Nullable java.util.List<@NonNull java.lang.String>,
72      *   [annotations] controls whether the annotations like @Nullable and @NonNull are included.
73      * @param kotlinStyleNulls Controls whether it should return "@Nullable List<String>" as
74      *   "List<String!>?".
75      * @param filter Specifies a filter to apply to the type annotations, if any.
76      * @param spaceBetweenParameters Controls whether there should be a space between class type
77      *   parameters, e.g. "java.util.Map<java.lang.Integer, java.lang.Number>" or
78      *   "java.util.Map<java.lang.Integer,java.lang.Number>".
79      */
80     fun toTypeString(
81         annotations: Boolean = false,
82         kotlinStyleNulls: Boolean = false,
83         filter: Predicate<Item>? = null,
84         spaceBetweenParameters: Boolean = false
85     ): String
86 
87     /**
88      * Get a string representation of the erased type.
89      *
90      * Implements the behavior described
91      * [here](https://docs.oracle.com/javase/tutorial/java/generics/genTypes.html).
92      *
93      * One point to note is that vararg parameters are represented using standard array syntax, i.e.
94      * `[]`, not the special source `...` syntax. The reason for that is that the erased type is
95      * mainly used at runtime which treats a vararg parameter as a standard array type.
96      */
97     @MetalavaApi fun toErasedTypeString(): String
98 
99     /** Returns the internal name of the type, as seen in bytecode. */
100     fun internalName(): String
101 
102     fun asClass(): ClassItem?
103 
104     fun toSimpleType(): String {
105         return stripJavaLangPrefix(toTypeString())
106     }
107 
108     /**
109      * Helper methods to compare types, especially types from signature files with types from
110      * parsing, which may have slightly different formats, e.g. varargs ("...") versus arrays
111      * ("[]"), java.lang. prefixes removed in wildcard signatures, etc.
112      */
113     fun toCanonicalType(): String {
114         var s = toTypeString()
115         while (s.contains(JAVA_LANG_PREFIX)) {
116             s = s.replace(JAVA_LANG_PREFIX, "")
117         }
118         if (s.contains("...")) {
119             s = s.replace("...", "[]")
120         }
121 
122         return s
123     }
124 
125     /**
126      * Makes substitutions to the type based on the [typeParameterBindings]. For instance, if the
127      * [typeParameterBindings] contains `{T -> String}`, calling this method on `T` would return
128      * `String`, and calling it on `List<T>` would return `List<String>` (in both cases the
129      * modifiers on the `String` will be independently mutable from the `String` in the
130      * [typeParameterBindings]). Calling it on an unrelated type like `int` would return a duplicate
131      * of that type.
132      *
133      * This method is intended to be used in conjunction with [ClassItem.mapTypeVariables],
134      */
135     fun convertType(typeParameterBindings: TypeParameterBindings): TypeItem
136 
137     fun convertType(from: ClassItem, to: ClassItem): TypeItem {
138         val map = from.mapTypeVariables(to)
139         if (map.isNotEmpty()) {
140             return convertType(map)
141         }
142 
143         return this
144     }
145 
146     /** Returns `true` if `this` type can be assigned from `other` without unboxing the other. */
147     fun isAssignableFromWithoutUnboxing(other: TypeItem): Boolean {
148         // Limited text based check
149         if (this == other) return true
150         val bounds =
151             (other as? VariableTypeItem)?.asTypeParameter?.typeBounds()?.map { it.toTypeString() }
152                 ?: emptyList()
153         return bounds.contains(toTypeString())
154     }
155 
156     fun isJavaLangObject(): Boolean = false
157 
158     fun isString(): Boolean = false
159 
160     fun defaultValue(): Any? = null
161 
162     fun defaultValueString(): String = "null"
163 
164     /** Creates an identical type, with a copy of this type's modifiers, so they can be mutated. */
165     fun duplicate(): TypeItem
166 
167     /**
168      * Creates an identical type, with a copy of this type's modifiers with the specified
169      * [withNullability] that can be modified further if needed.
170      */
171     fun duplicate(withNullability: TypeNullability) =
172         duplicate().apply { modifiers.setNullability(withNullability) }
173 
174     companion object {
175         /** Shortens types, if configured */
176         fun shortenTypes(type: String): String {
177             var cleaned = type
178             if (cleaned.contains("@androidx.annotation.")) {
179                 cleaned = cleaned.replace("@androidx.annotation.", "@")
180             }
181             return stripJavaLangPrefix(cleaned)
182         }
183 
184         /**
185          * Removes java.lang. prefixes from types, unless it's in a subpackage such as
186          * java.lang.reflect. For simplicity we may also leave inner classes in the java.lang
187          * package untouched.
188          *
189          * NOTE: We only remove this from the front of the type; e.g. we'll replace
190          * java.lang.Class<java.lang.String> with Class<java.lang.String>. This is because the
191          * signature parsing of types is not 100% accurate and we don't want to run into trouble
192          * with more complicated generic type signatures where we end up not mapping the simplified
193          * types back to the real fully qualified type names.
194          */
195         fun stripJavaLangPrefix(type: String): String {
196             if (type.startsWith(JAVA_LANG_PREFIX)) {
197                 // Replacing java.lang is harder, since we don't want to operate in sub packages,
198                 // e.g. java.lang.String -> String, but java.lang.reflect.Method -> unchanged
199                 val start = JAVA_LANG_PREFIX.length
200                 val end = type.length
201                 for (index in start until end) {
202                     if (type[index] == '<') {
203                         return type.substring(start)
204                     } else if (type[index] == '.') {
205                         return type
206                     }
207                 }
208 
209                 return type.substring(start)
210             }
211 
212             return type
213         }
214 
215         /**
216          * Create a [Comparator] that when given two [TypeItem] will treat them as equal if either
217          * returns `null` from [TypeItem.asClass] and will otherwise compare the two [ClassItem]s
218          * using [comparator].
219          *
220          * This only defines a partial ordering over [TypeItem].
221          */
222         private fun typeItemAsClassComparator(
223             comparator: Comparator<ClassItem>
224         ): Comparator<TypeItem> {
225             return Comparator { type1, type2 ->
226                 val cls1 = type1.asClass()
227                 val cls2 = type2.asClass()
228                 if (cls1 != null && cls2 != null) {
229                     comparator.compare(cls1, cls2)
230                 } else {
231                     0
232                 }
233             }
234         }
235 
236         /** A total ordering over [TypeItem] comparing [TypeItem.toTypeString]. */
237         private val typeStringComparator =
238             Comparator.comparing<TypeItem, String> { it.toTypeString() }
239 
240         /**
241          * A total ordering over [TypeItem] comparing [TypeItem.asClass] using
242          * [ClassItem.fullNameThenQualifierComparator] and then comparing [TypeItem.toTypeString].
243          */
244         val totalComparator: Comparator<TypeItem> =
245             typeItemAsClassComparator(ClassItem.fullNameThenQualifierComparator)
246                 .thenComparing(typeStringComparator)
247 
248         @Deprecated(
249             "" +
250                 "this should not be used as it only defines a partial ordering which means that the " +
251                 "source order will affect the result"
252         )
253         val partialComparator: Comparator<TypeItem> = Comparator { type1, type2 ->
254             val cls1 = type1.asClass()
255             val cls2 = type2.asClass()
256             if (cls1 != null && cls2 != null) {
257                 ClassItem.fullNameComparator.compare(cls1, cls2)
258             } else {
259                 type1.toTypeString().compareTo(type2.toTypeString())
260             }
261         }
262 
263         /**
264          * Convert a type string containing to its lambda representation or return the original.
265          *
266          * E.g.: `"kotlin.jvm.functions.Function1<Integer, String>"` to `"(Integer) -> String"`.
267          */
268         fun toLambdaFormat(typeName: String): String {
269             // Bail if this isn't a Kotlin function type
270             if (!typeName.startsWith(KOTLIN_FUNCTION_PREFIX)) {
271                 return typeName
272             }
273 
274             // Find the first character after the first opening angle bracket. This will either be
275             // the first character of the paramTypes of the lambda if it has parameters.
276             val paramTypesStart =
277                 typeName.indexOf('<', startIndex = KOTLIN_FUNCTION_PREFIX.length) + 1
278 
279             // The last type param is always the return type. We find and set these boundaries with
280             // the push down loop below.
281             var paramTypesEnd = -1
282             var returnTypeStart = -1
283 
284             // Get the exclusive end of the return type parameter by finding the last closing
285             // angle bracket.
286             val returnTypeEnd = typeName.lastIndexOf('>')
287 
288             // Bail if an an unexpected format broke the indexOf's above.
289             if (paramTypesStart <= 0 || paramTypesStart >= returnTypeEnd) {
290                 return typeName
291             }
292 
293             // This loop looks for the last comma that is not inside the type parameters of a type
294             // parameter. It's a simple push down state machine that stores its depth as a counter
295             // instead of a stack. It runs backwards from the last character of the type parameters
296             // just before the last closing angle bracket to the beginning just before the first
297             // opening angle bracket.
298             var depth = 0
299             for (i in returnTypeEnd - 1 downTo paramTypesStart) {
300                 val c = typeName[i]
301 
302                 // Increase or decrease stack depth on angle brackets
303                 when (c) {
304                     '>' -> depth++
305                     '<' -> depth--
306                 }
307 
308                 when {
309                     depth == 0 ->
310                         when { // At the top level
311                             c == ',' -> {
312                                 // When top level comma is found, mark it as the exclusive end of
313                                 // the
314                                 // parameter types and end the loop
315                                 paramTypesEnd = i
316                                 break
317                             }
318                             !c.isWhitespace() -> {
319                                 // Keep moving the start of the return type back until whitespace
320                                 returnTypeStart = i
321                             }
322                         }
323                     depth < 0 -> return typeName // Bail, unbalanced nesting
324                 }
325             }
326 
327             // Bail if some sort of unbalanced nesting occurred or the indices around the comma
328             // appear grossly incorrect.
329             if (depth > 0 || returnTypeStart < 0 || returnTypeStart <= paramTypesEnd) {
330                 return typeName
331             }
332 
333             return buildString(typeName.length) {
334                 append("(")
335 
336                 // Slice param types, if any, and append them between the parenthesis
337                 if (paramTypesEnd > 0) {
338                     append(typeName, paramTypesStart, paramTypesEnd)
339                 }
340 
341                 append(") -> ")
342 
343                 // Slice out the return type param and append it after the arrow
344                 append(typeName, returnTypeStart, returnTypeEnd)
345             }
346         }
347 
348         /** Prefix of Kotlin JVM function types, used for lambdas. */
349         private const val KOTLIN_FUNCTION_PREFIX = "kotlin.jvm.functions.Function"
350     }
351 }
352 
353 /**
354  * A mapping from one class's type parameters to the types provided for those type parameters in a
355  * possibly indirect subclass.
356  *
357  * e.g. Given `Map<K, V>` and a subinterface `StringToIntMap extends Map<String, Integer>` then this
358  * would contain a mapping from `K -> String` and `V -> Integer`.
359  *
360  * Although a `ClassTypeItem`'s arguments can be `WildcardTypeItem`s as well as
361  * `ReferenceTypeItem`s, a `ClassTypeItem` used in an extends or implements list cannot have a
362  * `WildcardTypeItem` as an argument so this cast is safe. See
363  * https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-Superclass
364  */
365 typealias TypeParameterBindings = Map<TypeParameterItem, ReferenceTypeItem>
366 
367 abstract class DefaultTypeItem(
368     final override val modifiers: TypeModifiers,
369 ) : TypeItem {
370 
371     private lateinit var cachedDefaultType: String
372     private lateinit var cachedErasedType: String
373 
toStringnull374     override fun toString(): String = toTypeString()
375 
376     override fun toTypeString(
377         annotations: Boolean,
378         kotlinStyleNulls: Boolean,
379         filter: Predicate<Item>?,
380         spaceBetweenParameters: Boolean
381     ): String {
382         return toTypeString(
383             TypeStringConfiguration(annotations, kotlinStyleNulls, filter, spaceBetweenParameters)
384         )
385     }
386 
toTypeStringnull387     private fun toTypeString(configuration: TypeStringConfiguration): String {
388         // Cache the default type string. Other configurations are less likely to be reused.
389         return if (configuration.isDefault) {
390             if (!::cachedDefaultType.isInitialized) {
391                 cachedDefaultType = buildString {
392                     appendTypeString(this@DefaultTypeItem, configuration)
393                 }
394             }
395             cachedDefaultType
396         } else {
397             buildString { appendTypeString(this@DefaultTypeItem, configuration) }
398         }
399     }
400 
toErasedTypeStringnull401     override fun toErasedTypeString(): String {
402         if (!::cachedErasedType.isInitialized) {
403             cachedErasedType = buildString { appendErasedTypeString(this@DefaultTypeItem) }
404         }
405         return cachedErasedType
406     }
407 
internalNamenull408     override fun internalName(): String {
409         // Default implementation; PSI subclass is more accurate
410         return toSlashFormat(toErasedTypeString())
411     }
412 
equalsnull413     override fun equals(other: Any?): Boolean {
414         if (other !is TypeItem) return false
415         return equalToType(other)
416     }
417 
hashCodenull418     override fun hashCode(): Int = hashCodeForType()
419 
420     companion object {
421         /**
422          * Configuration options for how to represent a type as a string.
423          *
424          * @param annotations Whether to include annotations on the type.
425          * @param kotlinStyleNulls Whether to represent nullability with Kotlin-style suffixes: `?`
426          *   for nullable, no suffix for non-null, and `!` for platform nullability. For example,
427          *   the Java type `@Nullable List<String>` would be represented as `List<String!>?`.
428          * @param filter A filter to apply to the type annotations, if any.
429          * @param spaceBetweenParameters Whether to include a space between class type params.
430          */
431         private data class TypeStringConfiguration(
432             val annotations: Boolean = false,
433             val kotlinStyleNulls: Boolean = false,
434             val filter: Predicate<Item>? = null,
435             val spaceBetweenParameters: Boolean = false,
436         ) {
437             val isDefault =
438                 !annotations && !kotlinStyleNulls && filter == null && !spaceBetweenParameters
439         }
440 
441         private fun StringBuilder.appendTypeString(
442             type: TypeItem,
443             configuration: TypeStringConfiguration
444         ) {
445             when (type) {
446                 is PrimitiveTypeItem -> {
447                     if (configuration.annotations) {
448                         appendAnnotations(type.modifiers, configuration)
449                     }
450                     append(type.kind.primitiveName)
451                     // Primitives must be non-null.
452                 }
453                 is ArrayTypeItem -> {
454                     // The ordering of array annotations means this can't just use a recursive
455                     // approach for annotated multi-dimensional arrays, but it can if annotations
456                     // aren't included.
457                     if (configuration.annotations) {
458                         var deepComponentType = type.componentType
459                         val arrayModifiers = mutableListOf(type.modifiers)
460                         while (deepComponentType is ArrayTypeItem) {
461                             arrayModifiers.add(deepComponentType.modifiers)
462                             deepComponentType = deepComponentType.componentType
463                         }
464                         val suffixes = arrayModifiers.map { it.nullability().suffix }.reversed()
465 
466                         // Print the innermost component type.
467                         appendTypeString(deepComponentType, configuration)
468 
469                         // Print modifiers from the outermost array type in, and the array suffixes.
470                         arrayModifiers.zip(suffixes).forEachIndexed { index, (modifiers, suffix) ->
471                             appendAnnotations(modifiers, configuration, leadingSpace = true)
472                             // Only the outermost array can be varargs.
473                             if (index < arrayModifiers.size - 1 || !type.isVarargs) {
474                                 append("[]")
475                             } else {
476                                 append("...")
477                             }
478                             if (configuration.kotlinStyleNulls) {
479                                 append(suffix)
480                             }
481                         }
482                     } else {
483                         // Non-annotated case: just recur to the component
484                         appendTypeString(type.componentType, configuration)
485                         if (type.isVarargs) {
486                             append("...")
487                         } else {
488                             append("[]")
489                         }
490                         if (configuration.kotlinStyleNulls) {
491                             append(type.modifiers.nullability().suffix)
492                         }
493                     }
494                 }
495                 is ClassTypeItem -> {
496                     if (type.outerClassType != null) {
497                         appendTypeString(type.outerClassType!!, configuration)
498                         append('.')
499                         if (configuration.annotations) {
500                             appendAnnotations(type.modifiers, configuration)
501                         }
502                         append(type.className)
503                     } else {
504                         if (configuration.annotations) {
505                             append(type.qualifiedName.substringBeforeLast(type.className))
506                             appendAnnotations(type.modifiers, configuration)
507                             append(type.className)
508                         } else {
509                             append(type.qualifiedName)
510                         }
511                     }
512 
513                     if (type.arguments.isNotEmpty()) {
514                         append("<")
515                         type.arguments.forEachIndexed { index, parameter ->
516                             appendTypeString(parameter, configuration)
517                             if (index != type.arguments.size - 1) {
518                                 append(",")
519                                 if (configuration.spaceBetweenParameters) {
520                                     append(" ")
521                                 }
522                             }
523                         }
524                         append(">")
525                     }
526                     if (configuration.kotlinStyleNulls) {
527                         append(type.modifiers.nullability().suffix)
528                     }
529                 }
530                 is VariableTypeItem -> {
531                     if (configuration.annotations) {
532                         appendAnnotations(type.modifiers, configuration)
533                     }
534                     append(type.name)
535                     if (configuration.kotlinStyleNulls) {
536                         append(type.modifiers.nullability().suffix)
537                     }
538                 }
539                 is WildcardTypeItem -> {
540                     if (configuration.annotations) {
541                         appendAnnotations(type.modifiers, configuration)
542                     }
543                     append("?")
544 
545                     type.superBound?.let {
546                         append(" super ")
547                         appendTypeString(it, configuration)
548                         // If there's a super bound, don't also print an object extends bound.
549                         return
550                     }
551 
552                     type.extendsBound?.let {
553                         if (shouldIncludeExtendsBound(it, configuration)) {
554                             append(" extends ")
555                             appendTypeString(it, configuration)
556                         }
557                     }
558 
559                     // It doesn't make sense to have a nullness suffix on a wildcard, this should be
560                     // handled by the bound.
561                 }
562             }
563         }
564 
565         /**
566          * Returns whether the [extendsBound] should be included in the type string based on the
567          * [configuration].
568          */
569         private fun shouldIncludeExtendsBound(
570             extendsBound: ReferenceTypeItem,
571             configuration: TypeStringConfiguration
572         ): Boolean {
573             // Non-object bounds should always be included.
574             if (!extendsBound.isJavaLangObject()) return true
575 
576             // If the bound is Object, it should only be included when the nullability isn't implied
577             // by the configuration. If both kotlinStyleNulls and annotations are false, no
578             // nullability information is included anyway.
579             if (!configuration.kotlinStyleNulls && !configuration.annotations) return false
580 
581             // When nullability information is included, excluded bounds imply non-null when
582             // kotlinStyleNulls is true and platform when it is false.
583             val nullability = extendsBound.modifiers.nullability()
584             if (configuration.kotlinStyleNulls && nullability == TypeNullability.NONNULL)
585                 return false
586             if (!configuration.kotlinStyleNulls && nullability == TypeNullability.PLATFORM)
587                 return false
588             return true
589         }
590 
591         private fun StringBuilder.appendAnnotations(
592             modifiers: TypeModifiers,
593             configuration: TypeStringConfiguration,
594             leadingSpace: Boolean = false,
595             trailingSpace: Boolean = true
596         ) {
597             val annotations =
598                 modifiers.annotations().filter { annotation ->
599                     // If Kotlin-style nulls are printed, nullness annotations shouldn't be.
600                     if (configuration.kotlinStyleNulls && annotation.isNullnessAnnotation()) {
601                         return@filter false
602                     }
603 
604                     val filter = configuration.filter ?: return@filter true
605                     val qualifiedName = annotation.qualifiedName ?: return@filter true
606                     val annotationClass =
607                         annotation.codebase.findClass(qualifiedName) ?: return@filter true
608                     filter.test(annotationClass)
609                 }
610             if (annotations.isEmpty()) return
611 
612             if (leadingSpace) {
613                 append(' ')
614             }
615             annotations.forEachIndexed { index, annotation ->
616                 append(annotation.toSource())
617                 if (index != annotations.size - 1) {
618                     append(' ')
619                 }
620             }
621             if (trailingSpace) {
622                 append(' ')
623             }
624         }
625 
626         private fun StringBuilder.appendErasedTypeString(type: TypeItem) {
627             when (type) {
628                 is PrimitiveTypeItem -> append(type.kind.primitiveName)
629                 is ArrayTypeItem -> {
630                     appendErasedTypeString(type.componentType)
631                     append("[]")
632                 }
633                 is ClassTypeItem -> append(type.qualifiedName)
634                 is VariableTypeItem ->
635                     type.asTypeParameter.asErasedType()?.let { appendErasedTypeString(it) }
636                         ?: append(JAVA_LANG_OBJECT)
637                 else ->
638                     throw IllegalStateException(
639                         "should never visit $type of type ${type.javaClass} while generating erased type string"
640                     )
641             }
642         }
643 
644         // Copied from doclava1
645         private fun toSlashFormat(typeName: String): String {
646             var name = typeName
647             var dimension = ""
648             while (name.endsWith("[]")) {
649                 dimension += "["
650                 name = name.substring(0, name.length - 2)
651             }
652 
653             val base: String
654             base =
655                 when (name) {
656                     "void" -> "V"
657                     "byte" -> "B"
658                     "boolean" -> "Z"
659                     "char" -> "C"
660                     "short" -> "S"
661                     "int" -> "I"
662                     "long" -> "J"
663                     "float" -> "F"
664                     "double" -> "D"
665                     else -> "L" + getInternalName(name) + ";"
666                 }
667 
668             return dimension + base
669         }
670 
671         /**
672          * Computes the internal class name of the given fully qualified class name. For example, it
673          * converts foo.bar.Foo.Bar into foo/bar/Foo$Bar
674          *
675          * @param qualifiedName the fully qualified class name
676          * @return the internal class name
677          */
678         private fun getInternalName(qualifiedName: String): String {
679             if (qualifiedName.indexOf('.') == -1) {
680                 return qualifiedName
681             }
682 
683             // If class name contains $, it's not an ambiguous inner class name.
684             if (qualifiedName.indexOf('$') != -1) {
685                 return qualifiedName.replace('.', '/')
686             }
687             // Let's assume that components that start with Caps are class names.
688             return buildString {
689                 var prev: String? = null
690                 for (part in qualifiedName.split(".")) {
691                     if (!prev.isNullOrEmpty()) {
692                         if (Character.isUpperCase(prev[0])) {
693                             append('$')
694                         } else {
695                             append('/')
696                         }
697                     }
698                     append(part)
699                     prev = part
700                 }
701             }
702         }
703     }
704 }
705 
706 /**
707  * The type for [ClassTypeItem.arguments].
708  *
709  * See https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-TypeArgument.
710  */
711 interface TypeArgumentTypeItem : TypeItem {
712     /** Override to specialize the return type. */
convertTypenull713     override fun convertType(typeParameterBindings: TypeParameterBindings): TypeArgumentTypeItem
714 
715     /** Override to specialize the return type. */
716     override fun duplicate(): TypeArgumentTypeItem
717 }
718 
719 /**
720  * The type for a reference.
721  *
722  * See https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-ReferenceType.
723  */
724 interface ReferenceTypeItem : TypeItem, TypeArgumentTypeItem {
725     /** Override to specialize the return type. */
726     override fun convertType(typeParameterBindings: TypeParameterBindings): ReferenceTypeItem
727 
728     /** Override to specialize the return type. */
729     override fun duplicate(): ReferenceTypeItem
730 }
731 
732 /**
733  * The type of [TypeParameterItem]'s type bounds.
734  *
735  * See https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-TypeBound
736  */
737 interface BoundsTypeItem : TypeItem, ReferenceTypeItem
738 
739 /**
740  * The type of [MethodItem.throwsTypes]'s.
741  *
742  * See https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-ExceptionType.
743  */
744 sealed interface ExceptionTypeItem : TypeItem, ReferenceTypeItem {
745     /**
746      * Get the erased [ClassItem], if any.
747      *
748      * The erased [ClassItem] is the one which would be used by Java at runtime after the generic
749      * types have been erased. This will cause an error if it is called on a [VariableTypeItem]
750      * whose [TypeParameterItem]'s upper bound is not a [ExceptionTypeItem]. However, that should
751      * never happen as it would be a compile time error.
752      */
753     val erasedClass: ClassItem?
754 
755     /**
756      * The best guess of the full name, i.e. the qualified class name without the package but
757      * including the outer class names.
758      *
759      * This is not something that can be accurately determined solely by examining the reference or
760      * even the import as there is no distinction made between a package name and a class name. Java
761      * naming conventions do say that package names should start with a lower case letter and class
762      * names should start with an upper case letter, but they are not enforced so cannot be fully
763      * relied upon.
764      *
765      * It is possible that in some contexts a model could provide a better full name than guessing
766      * from the fully qualified name, e.g. a reference within the same package, however that is not
767      * something that will be supported by all models and so attempting to use that could lead to
768      * subtle model differences that could break users of the models.
769      *
770      * The only way to fully determine the full name is to resolve the class and extract it from
771      * there but this avoids resolving a class as it can be expensive. Instead, this just makes the
772      * best guess assuming normal Java conventions.
773      */
774     @Deprecated(
775         "Do not use as full name is only ever a best guess based on naming conventions; use the full type string instead",
776         ReplaceWith("toTypeString()")
777     )
fullNamenull778     fun fullName(): String = bestGuessAtFullName(toTypeString())
779 
780     companion object {
781         /** A partial ordering over [ExceptionTypeItem] comparing [ExceptionTypeItem] full names. */
782         val fullNameComparator: Comparator<ExceptionTypeItem> =
783             Comparator.comparing { @Suppress("DEPRECATION") it.fullName() }
784     }
785 }
786 
787 /** Represents a primitive type, like int or boolean. */
788 interface PrimitiveTypeItem : TypeItem {
789     /** The kind of [Primitive] this type is. */
790     val kind: Primitive
791 
792     /** The possible kinds of primitives. */
793     enum class Primitive(
794         val primitiveName: String,
795         val defaultValue: Any?,
796         val defaultValueString: String
797     ) {
798         BOOLEAN("boolean", false, "false"),
799         BYTE("byte", 0.toByte(), "0"),
800         CHAR("char", 0.toChar(), "0"),
801         DOUBLE("double", 0.0, "0"),
802         FLOAT("float", 0F, "0"),
803         INT("int", 0, "0"),
804         LONG("long", 0L, "0"),
805         SHORT("short", 0.toShort(), "0"),
806         VOID("void", null, "null")
807     }
808 
defaultValuenull809     override fun defaultValue(): Any? = kind.defaultValue
810 
811     override fun defaultValueString(): String = kind.defaultValueString
812 
813     override fun accept(visitor: TypeVisitor) {
814         visitor.visit(this)
815     }
816 
acceptnull817     override fun accept(visitor: MultipleTypeVisitor, other: List<TypeItem>) {
818         visitor.visit(this, other)
819     }
820 
duplicatenull821     override fun duplicate(): PrimitiveTypeItem
822 
823     override fun convertType(typeParameterBindings: TypeParameterBindings): PrimitiveTypeItem {
824         return duplicate()
825     }
826 
equalToTypenull827     override fun equalToType(other: TypeItem?): Boolean {
828         return (other as? PrimitiveTypeItem)?.kind == kind
829     }
830 
hashCodeForTypenull831     override fun hashCodeForType(): Int = kind.hashCode()
832 
833     override fun asClass(): ClassItem? = null
834 }
835 
836 /** Represents an array type, including vararg types. */
837 interface ArrayTypeItem : TypeItem, ReferenceTypeItem {
838     /** The array's inner type (which for multidimensional arrays is another array type). */
839     val componentType: TypeItem
840 
841     /** Whether this array type represents a varargs parameter. */
842     val isVarargs: Boolean
843 
844     override fun accept(visitor: TypeVisitor) {
845         visitor.visit(this)
846     }
847 
848     override fun accept(visitor: MultipleTypeVisitor, other: List<TypeItem>) {
849         visitor.visit(this, other)
850     }
851 
852     override fun duplicate(): ArrayTypeItem = duplicate(componentType.duplicate())
853 
854     /**
855      * Duplicates this type (including duplicating the modifiers so they can be independently
856      * mutated), but substituting in the provided [componentType] in place of this type's component.
857      */
858     fun duplicate(componentType: TypeItem): ArrayTypeItem
859 
860     override fun convertType(typeParameterBindings: TypeParameterBindings): ArrayTypeItem {
861         return duplicate(componentType.convertType(typeParameterBindings))
862     }
863 
864     override fun equalToType(other: TypeItem?): Boolean {
865         if (other !is ArrayTypeItem) return false
866         return isVarargs == other.isVarargs && componentType.equalToType(other.componentType)
867     }
868 
869     override fun hashCodeForType(): Int = Objects.hash(isVarargs, componentType)
870 
871     override fun asClass(): ClassItem? = componentType.asClass()
872 }
873 
874 /** Represents a class type. */
875 interface ClassTypeItem : TypeItem, BoundsTypeItem, ReferenceTypeItem, ExceptionTypeItem {
876     /** The qualified name of this class, e.g. "java.lang.String". */
877     val qualifiedName: String
878 
879     /**
880      * The class type's arguments, empty if it has none.
881      *
882      * i.e. The specific types that this class type assigns to each of the referenced [ClassItem]'s
883      * type parameters.
884      */
885     val arguments: List<TypeArgumentTypeItem>
886 
887     /** The outer class type of this class, if it is an inner type. */
888     val outerClassType: ClassTypeItem?
889 
890     /**
891      * The name of the class, e.g. "String" for "java.lang.String" and "Inner" for
892      * "test.pkg.Outer.Inner".
893      */
894     val className: String
895 
896     override val erasedClass: ClassItem?
897         get() = asClass()
898 
acceptnull899     override fun accept(visitor: TypeVisitor) {
900         visitor.visit(this)
901     }
902 
acceptnull903     override fun accept(visitor: MultipleTypeVisitor, other: List<TypeItem>) {
904         visitor.visit(this, other)
905     }
906 
907     /**
908      * Check to see whether this type has any type arguments.
909      *
910      * It will return `true` for say `List<T>`, but `false` for `String`.
911      */
hasTypeArgumentsnull912     fun hasTypeArguments() = arguments.isNotEmpty()
913 
914     override fun isString(): Boolean = qualifiedName == JAVA_LANG_STRING
915 
916     override fun isJavaLangObject(): Boolean = qualifiedName == JAVA_LANG_OBJECT
917 
918     override fun duplicate(): ClassTypeItem =
919         duplicate(outerClassType?.duplicate(), arguments.map { it.duplicate() })
920 
921     /**
922      * Duplicates this type (including duplicating the modifiers, so they can be independently
923      * mutated), but substituting in the provided [outerClass] and [arguments] in place of this
924      * instance's [outerClass] and [arguments].
925      */
duplicatenull926     fun duplicate(outerClass: ClassTypeItem?, arguments: List<TypeArgumentTypeItem>): ClassTypeItem
927 
928     override fun convertType(typeParameterBindings: TypeParameterBindings): ClassTypeItem {
929         return duplicate(
930             outerClassType?.convertType(typeParameterBindings),
931             arguments.map { it.convertType(typeParameterBindings) }
932         )
933     }
934 
equalToTypenull935     override fun equalToType(other: TypeItem?): Boolean {
936         if (other !is ClassTypeItem) return false
937         return qualifiedName == other.qualifiedName &&
938             arguments.size == other.arguments.size &&
939             arguments.zip(other.arguments).all { (p1, p2) -> p1.equalToType(p2) } &&
940             ((outerClassType == null && other.outerClassType == null) ||
941                 outerClassType?.equalToType(other.outerClassType) == true)
942     }
943 
hashCodeForTypenull944     override fun hashCodeForType(): Int = Objects.hash(qualifiedName, outerClassType, arguments)
945 
946     companion object {
947         /** Computes the simple name of a class from a qualified class name. */
948         fun computeClassName(qualifiedName: String): String {
949             val lastDotIndex = qualifiedName.lastIndexOf('.')
950             return if (lastDotIndex == -1) {
951                 qualifiedName
952             } else {
953                 qualifiedName.substring(lastDotIndex + 1)
954             }
955         }
956     }
957 }
958 
959 /**
960  * Represents a kotlin lambda type.
961  *
962  * This extends [ClassTypeItem] out of necessity because that is how lambdas have been represented
963  * in Metalava up until this was created and so until such time as all the code that consumes this
964  * has been updated to handle lambdas specifically it will need to remain a [ClassTypeItem].
965  */
966 interface LambdaTypeItem : ClassTypeItem {
967     /** True if the lambda is a suspend function, false otherwise. */
968     val isSuspend: Boolean
969 
970     /** The type of the optional receiver. */
971     val receiverType: TypeItem?
972 
973     /** The parameter types. */
974     val parameterTypes: List<TypeItem>
975 
976     /** The return type. */
977     val returnType: TypeItem
978 
duplicatenull979     override fun duplicate(): LambdaTypeItem =
980         duplicate(outerClassType?.duplicate(), arguments.map { it.duplicate() })
981 
duplicatenull982     override fun duplicate(
983         outerClass: ClassTypeItem?,
984         arguments: List<TypeArgumentTypeItem>
985     ): LambdaTypeItem
986 }
987 
988 /** Represents a type variable type. */
989 interface VariableTypeItem : TypeItem, BoundsTypeItem, ReferenceTypeItem, ExceptionTypeItem {
990     /** The name of the type variable */
991     val name: String
992 
993     /** The corresponding type parameter for this type variable. */
994     val asTypeParameter: TypeParameterItem
995 
996     override val erasedClass: ClassItem?
997         get() = (asTypeParameter.asErasedType() as ClassTypeItem).erasedClass
998 
999     override fun description() =
1000         "$name (extends ${this.asTypeParameter.asErasedType()?.description() ?: "unknown type"})}"
1001 
1002     override fun accept(visitor: TypeVisitor) {
1003         visitor.visit(this)
1004     }
1005 
1006     override fun accept(visitor: MultipleTypeVisitor, other: List<TypeItem>) {
1007         visitor.visit(this, other)
1008     }
1009 
1010     override fun convertType(typeParameterBindings: TypeParameterBindings): ReferenceTypeItem {
1011         val nullability = modifiers.nullability()
1012         return (typeParameterBindings[asTypeParameter] ?: this).duplicate().apply {
1013             // If this use of the type parameter is marked as nullable, then it overrides the
1014             // nullability of the substituted type.
1015             if (nullability == TypeNullability.NULLABLE) {
1016                 modifiers.setNullability(nullability)
1017             } else {
1018                 // If the type that is replacing the type parameter has platform nullability, i.e.
1019                 // carries no information one way or another about whether it is nullable, then
1020                 // use the nullability of the use of the type parameter as while at worst it may
1021                 // also have no nullability information, it could have some, e.g. from a declaration
1022                 // nullability annotation.
1023                 val typeParameterNullability = modifiers.nullability()
1024                 if (typeParameterNullability == TypeNullability.PLATFORM) {
1025                     modifiers.setNullability(nullability)
1026                 }
1027             }
1028         }
1029     }
1030 
1031     override fun asClass() = asTypeParameter.asErasedType()?.asClass()
1032 
1033     override fun duplicate(): VariableTypeItem
1034 
1035     override fun equalToType(other: TypeItem?): Boolean {
1036         return (other as? VariableTypeItem)?.name == name
1037     }
1038 
1039     override fun hashCodeForType(): Int = name.hashCode()
1040 }
1041 
1042 /**
1043  * Represents a wildcard type, like `?`, `? extends String`, and `? super String` in Java, or `*`,
1044  * `out String`, and `in String` in Kotlin.
1045  */
1046 interface WildcardTypeItem : TypeItem, TypeArgumentTypeItem {
1047     /** The type this wildcard must extend. If null, the extends bound is implicitly `Object`. */
1048     val extendsBound: ReferenceTypeItem?
1049 
1050     /** The type this wildcard must be a super class of. */
1051     val superBound: ReferenceTypeItem?
1052 
acceptnull1053     override fun accept(visitor: TypeVisitor) {
1054         visitor.visit(this)
1055     }
1056 
acceptnull1057     override fun accept(visitor: MultipleTypeVisitor, other: List<TypeItem>) {
1058         visitor.visit(this, other)
1059     }
1060 
duplicatenull1061     override fun duplicate(): WildcardTypeItem =
1062         duplicate(extendsBound?.duplicate(), superBound?.duplicate())
1063 
1064     /**
1065      * Duplicates this type (including duplicating the modifiers so they can be independently
1066      * mutated), but substituting in the provided [extendsBound] and [superBound] in place of this
1067      * type's bounds.
1068      */
1069     fun duplicate(
1070         extendsBound: ReferenceTypeItem?,
1071         superBound: ReferenceTypeItem?,
1072     ): WildcardTypeItem
1073 
1074     override fun convertType(typeParameterBindings: TypeParameterBindings): WildcardTypeItem {
1075         return duplicate(
1076             extendsBound?.convertType(typeParameterBindings),
1077             superBound?.convertType(typeParameterBindings)
1078         )
1079     }
1080 
equalToTypenull1081     override fun equalToType(other: TypeItem?): Boolean {
1082         if (other !is WildcardTypeItem) return false
1083         return extendsBound?.equalToType(other.extendsBound) != false &&
1084             superBound?.equalToType(other.superBound) != false
1085     }
1086 
hashCodeForTypenull1087     override fun hashCodeForType(): Int = Objects.hash(extendsBound, superBound)
1088 
1089     override fun asClass(): ClassItem? = null
1090 }
1091 
1092 /**
1093  * Attempt to get the full name from the qualified name.
1094  *
1095  * The full name is the qualified name without the package including any outer class names.
1096  *
1097  * It relies on the convention that packages start with a lower case letter and classes start with
1098  * an upper case letter.
1099  */
1100 fun bestGuessAtFullName(qualifiedName: String): String {
1101     val length = qualifiedName.length
1102     var prev: Char? = null
1103     var lastDotIndex = -1
1104     for (i in 0..length - 1) {
1105         val c = qualifiedName[i]
1106         if (prev == null || prev == '.') {
1107             if (c.isUpperCase()) {
1108                 return qualifiedName.substring(i)
1109             }
1110         }
1111         if (c == '.') {
1112             lastDotIndex = i
1113         }
1114         prev = c
1115     }
1116 
1117     return if (lastDotIndex == -1) {
1118         qualifiedName
1119     } else {
1120         qualifiedName.substring(lastDotIndex + 1)
1121     }
1122 }
1123