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