xref: /aosp_15_r20/tools/metalava/metalava-model/src/main/java/com/android/tools/metalava/model/TypeParameterList.kt (revision 115816f9299ab6ddd6b9673b81f34e707f6bacab)
1 /*
<lambda>null2  * Copyright (C) 2018 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.item.DefaultTypeParameterItem
20 import com.android.tools.metalava.model.type.TypeItemFactory
21 
22 /**
23  * Represents a type parameter list. For example, in class<S, T extends List<String>> the type
24  * parameter list is <S, T extends List<String>> and has type parameters named S and T, and type
25  * parameter T has bounds List<String>.
26  */
27 interface TypeParameterList : List<TypeParameterItem> {
28     /**
29      * Returns source representation of this type parameter, using fully qualified names (possibly
30      * with java.lang. removed if requested via options)
31      */
32     override fun toString(): String
33 
34     /** Implemented according to the general [java.util.List.equals] contract. */
35     override fun equals(other: Any?): Boolean
36 
37     /** Implemented according to the general [java.util.List.hashCode] contract. */
38     override fun hashCode(): Int
39 
40     companion object {
41         private val emptyListDelegate = emptyList<TypeParameterItem>()
42 
43         /** Type parameter list when there are no type parameters */
44         val NONE: TypeParameterList =
45             object : TypeParameterList, List<TypeParameterItem> by emptyListDelegate {
46                 override fun toString(): String = ""
47 
48                 override fun equals(other: Any?) = emptyListDelegate == other
49 
50                 override fun hashCode() = emptyListDelegate.hashCode()
51             }
52     }
53 }
54 
55 class DefaultTypeParameterList
56 private constructor(private val typeParameters: List<TypeParameterItem>) :
57     TypeParameterList, List<TypeParameterItem> by typeParameters {
58 
<lambda>null59     private val toString by lazy {
60         buildString {
61             if (this@DefaultTypeParameterList.isNotEmpty()) {
62                 append("<")
63                 var first = true
64                 for (param in this@DefaultTypeParameterList) {
65                     if (!first) {
66                         append(", ")
67                     }
68                     first = false
69                     append(param.toSource())
70                 }
71                 append(">")
72             }
73         }
74     }
75 
toStringnull76     override fun toString(): String {
77         return toString
78     }
79 
equalsnull80     override fun equals(other: Any?) = typeParameters == other
81 
82     override fun hashCode() = typeParameters.hashCode()
83 
84     companion object {
85 
86         /**
87          * Create a list of [TypeParameterItem] and a corresponding [TypeItemFactory] from model
88          * specific parameter and bounds information.
89          *
90          * A type parameter list can contain cycles between its type parameters, e.g.
91          *
92          *     class Node<L extends Node<L, R>, R extends Node<L, R>>
93          *
94          * Parsing that requires a multi-stage approach.
95          * 1. Separate the list into a mapping from `TypeParameterItem` that have not yet had their
96          *    `bounds` property initialized to the model specific parameter.
97          * 2. Create a nested factory of the enclosing factory which includes the type parameters.
98          *    That will allow references between them to be resolved.
99          * 3. Complete the initialization by converting each bounds string into a TypeItem.
100          *
101          * @param containingTypeItemFactory the containing factory.
102          * @param scopeDescription the description of the scope that will be created by the factory.
103          * @param inputParams a list of the model specific type parameters.
104          * @param paramFactory a function that will create a [TypeParameterItem] from the model
105          *   specified parameter [P].
106          * @param boundsGetter a function that will create a list of [BoundsTypeItem] from the model
107          *   specific bounds which will be stored in [DefaultTypeParameterItem.bounds].
108          * @param P the type of the underlying model specific type parameter objects.
109          * @param F the type of the model specific [TypeItemFactory].
110          */
111         fun <P, F : TypeItemFactory<*, F>> createTypeParameterItemsAndFactory(
112             containingTypeItemFactory: F,
113             scopeDescription: String,
114             inputParams: List<P>,
115             paramFactory: (P) -> DefaultTypeParameterItem,
116             boundsGetter: (F, P) -> List<BoundsTypeItem>,
117         ): TypeParameterListAndFactory<F> {
118             // First, create a Map from [TypeParameterItem] to the model specific parameter. Using
119             // the [paramFactory] to convert the model specific parameter to a [TypeParameterItem].
120             val typeParameterItemToBounds = inputParams.associateBy { param -> paramFactory(param) }
121 
122             // Then, create a [TypeItemFactory] for this list of type parameters.
123             val typeParameters = typeParameterItemToBounds.keys.toList()
124             val typeItemFactory =
125                 containingTypeItemFactory.nestedFactory(scopeDescription, typeParameters)
126 
127             // Then, create and set the bounds in the [TypeParameterItem] passing in the
128             // [TypeItemFactory] to allow cross-references to type parameters to be resolved.
129             for ((typeParameter, param) in typeParameterItemToBounds) {
130                 val boundsTypeItems = boundsGetter(typeItemFactory, param)
131                 typeParameter.bounds = boundsTypeItems
132             }
133 
134             // Pair the list up with the [TypeItemFactory] so that the latter can be reused.
135             val typeParameterList = DefaultTypeParameterList(typeParameters)
136             return TypeParameterListAndFactory(typeParameterList, typeItemFactory)
137         }
138     }
139 }
140 
141 /**
142  * Group up [typeParameterList] and the [factory] that was used to resolve references when creating
143  * their [BoundsTypeItem]s.
144  */
145 data class TypeParameterListAndFactory<F : TypeItemFactory<*, F>>(
146     val typeParameterList: TypeParameterList,
147     val factory: F,
148 )
149