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