• 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.text
18 
19 import com.android.tools.metalava.model.AnnotationRetention
20 import com.android.tools.metalava.model.ClassItem
21 import com.android.tools.metalava.model.ConstructorItem
22 import com.android.tools.metalava.model.DefaultModifierList
23 import com.android.tools.metalava.model.FieldItem
24 import com.android.tools.metalava.model.Item
25 import com.android.tools.metalava.model.MethodItem
26 import com.android.tools.metalava.model.PackageItem
27 import com.android.tools.metalava.model.PropertyItem
28 import com.android.tools.metalava.model.TypeItem
29 import com.android.tools.metalava.model.TypeParameterItem
30 import com.android.tools.metalava.model.TypeParameterList
31 import com.android.tools.metalava.model.TypeParameterListOwner
32 import java.util.function.Predicate
33 
34 open class TextClassItem(
35     override val codebase: TextCodebase,
36     position: SourcePositionInfo = SourcePositionInfo.UNKNOWN,
37     modifiers: TextModifiers,
38     private var isInterface: Boolean = false,
39     private var isEnum: Boolean = false,
40     private var isAnnotation: Boolean = false,
41     val qualifiedName: String = "",
42     private val qualifiedTypeName: String = qualifiedName,
43     var name: String = qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1),
44     val annotations: List<String>? = null
45 ) : TextItem(
46     codebase = codebase,
47     position = position,
48     modifiers = modifiers
49 ),
50     ClassItem,
51     TypeParameterListOwner {
52 
53     init {
54         @Suppress("LeakingThis")
55         modifiers.setOwner(this)
56     }
57 
58     override val isTypeParameter: Boolean = false
59 
60     override var artifact: String? = null
61 
62     override fun equals(other: Any?): Boolean {
63         if (this === other) return true
64         if (other !is ClassItem) return false
65 
66         return qualifiedName == other.qualifiedName()
67     }
68 
69     override fun hashCode(): Int {
70         return qualifiedName.hashCode()
71     }
72 
73     override fun interfaceTypes(): List<TypeItem> = interfaceTypes
74     override fun allInterfaces(): Sequence<ClassItem> {
75         return interfaceTypes.asSequence().map { it.asClass() }.filterNotNull()
76     }
77 
78     private var innerClasses: MutableList<ClassItem> = mutableListOf()
79 
80     override var stubConstructor: ConstructorItem? = null
81 
82     override var hasPrivateConstructor: Boolean = false
83 
84     override fun innerClasses(): List<ClassItem> = innerClasses
85 
86     override fun hasImplicitDefaultConstructor(): Boolean {
87         return false
88     }
89 
90     override fun isInterface(): Boolean = isInterface
91     override fun isAnnotationType(): Boolean = isAnnotation
92     override fun isEnum(): Boolean = isEnum
93 
94     var containingClass: TextClassItem? = null
95     override fun containingClass(): ClassItem? = containingClass
96 
97     private var containingPackage: PackageItem? = null
98 
99     fun setContainingPackage(containingPackage: TextPackageItem) {
100         this.containingPackage = containingPackage
101     }
102 
103     fun setIsAnnotationType(isAnnotation: Boolean) {
104         this.isAnnotation = isAnnotation
105     }
106 
107     fun setIsEnum(isEnum: Boolean) {
108         this.isEnum = isEnum
109     }
110 
111     override fun containingPackage(): PackageItem =
112         containingClass?.containingPackage() ?: containingPackage ?: error(this)
113 
114     override fun toType(): TypeItem {
115         val typeParameterListString = typeParameterList().toString()
116         return codebase.obtainTypeFromString(
117             if (typeParameterListString.isNotEmpty()) {
118                 // TODO: No, handle List<String>[], though this is highly unlikely in a class
119                 qualifiedName() + typeParameterListString
120             } else qualifiedName()
121         )
122     }
123 
124     override fun hasTypeVariables(): Boolean {
125         return typeInfo?.hasTypeArguments() ?: false
126     }
127 
128     private var typeParameterList: TypeParameterList? = null
129 
130     override fun typeParameterList(): TypeParameterList {
131         if (typeParameterList == null) {
132             val s = typeInfo.toString()
133             // TODO: No, handle List<String>[]  (though it's not likely for type parameters)
134             val index = s.indexOf('<')
135             typeParameterList = if (index != -1) {
136                 TextTypeParameterList.create(codebase, this, s.substring(index))
137             } else {
138                 TypeParameterList.NONE
139             }
140         }
141 
142         return typeParameterList!!
143     }
144 
145     override fun typeParameterListOwnerParent(): TypeParameterListOwner? {
146         return containingClass
147     }
148 
149     override fun resolveParameter(variable: String): TypeParameterItem? {
150         if (hasTypeVariables()) {
151             for (t in typeParameterList().typeParameters()) {
152                 if (t.simpleName() == variable) {
153                     return t
154                 }
155             }
156         }
157 
158         return null
159     }
160 
161     private var superClass: ClassItem? = null
162     private var superClassType: TypeItem? = null
163 
164     override fun superClass(): ClassItem? = superClass
165     override fun superClassType(): TypeItem? = superClassType
166 
167     override fun setSuperClass(superClass: ClassItem?, superClassType: TypeItem?) {
168         this.superClass = superClass
169         this.superClassType = superClassType
170     }
171 
172     override fun setInterfaceTypes(interfaceTypes: List<TypeItem>) {
173         this.interfaceTypes = interfaceTypes.toMutableList()
174     }
175 
176     private var typeInfo: TextTypeItem? = null
177     fun setTypeInfo(typeInfo: TextTypeItem) {
178         this.typeInfo = typeInfo
179     }
180 
181     fun asTypeInfo(): TextTypeItem {
182         if (typeInfo == null) {
183             typeInfo = codebase.obtainTypeFromString(qualifiedTypeName)
184         }
185         return typeInfo!!
186     }
187 
188     private var interfaceTypes = mutableListOf<TypeItem>()
189     private val constructors = mutableListOf<ConstructorItem>()
190     private val methods = mutableListOf<MethodItem>()
191     private val fields = mutableListOf<FieldItem>()
192     private val properties = mutableListOf<PropertyItem>()
193 
194     override fun constructors(): List<ConstructorItem> = constructors
195     override fun methods(): List<MethodItem> = methods
196     override fun fields(): List<FieldItem> = fields
197     override fun properties(): List<PropertyItem> = properties
198 
199     fun addInterface(itf: TypeItem) {
200         interfaceTypes.add(itf)
201     }
202 
203     fun addConstructor(constructor: TextConstructorItem) {
204         constructors += constructor
205     }
206 
207     fun addMethod(method: TextMethodItem) {
208         methods += method
209     }
210 
211     fun addField(field: TextFieldItem) {
212         fields += field
213     }
214 
215     fun addProperty(property: TextPropertyItem) {
216         properties += property
217     }
218 
219     fun addEnumConstant(field: TextFieldItem) {
220         field.setEnumConstant(true)
221         fields += field
222     }
223 
224     fun addInnerClass(cls: TextClassItem) {
225         innerClasses.add(cls)
226     }
227 
228     fun isCompatible(cls: TextClassItem): Boolean {
229         if (this === cls) {
230             return true
231         }
232         if (fullName != cls.fullName) {
233             return false
234         }
235 
236         return modifiers.toString() == cls.modifiers.toString() &&
237             isInterface == cls.isInterface &&
238             isEnum == cls.isEnum &&
239             isAnnotation == cls.isAnnotation &&
240             superClass == cls.superClass &&
241             allInterfaces().toSet() == cls.allInterfaces().toSet()
242     }
243 
244     override fun filteredSuperClassType(predicate: Predicate<Item>): TypeItem? {
245         // No filtering in signature files: we assume signature APIs
246         // have already been filtered and all items should match.
247         // This lets us load signature files and rewrite them using updated
248         // output formats etc.
249         return superClassType
250     }
251 
252     private var retention: AnnotationRetention? = null
253 
254     override fun getRetention(): AnnotationRetention {
255         retention?.let { return it }
256 
257         if (!isAnnotationType()) {
258             error("getRetention() should only be called on annotation classes")
259         }
260 
261         retention = ClassItem.findRetention(this)
262         return retention!!
263     }
264 
265     private var fullName: String = name
266     override fun simpleName(): String = name.substring(name.lastIndexOf('.') + 1)
267     override fun fullName(): String = fullName
268     override fun qualifiedName(): String = qualifiedName
269     override fun isDefined(): Boolean {
270         assert(emit == (position != SourcePositionInfo.UNKNOWN))
271         return emit
272     }
273     override fun toString(): String = "class ${qualifiedName()}"
274 
275     override fun mapTypeVariables(target: ClassItem): Map<String, String> {
276         return emptyMap()
277     }
278 
279     override fun createDefaultConstructor(): ConstructorItem {
280         return TextConstructorItem.createDefaultConstructor(codebase, this, position)
281     }
282 
283     companion object {
284         fun createClassStub(codebase: TextCodebase, name: String): TextClassItem =
285             createStub(codebase, name, isInterface = false)
286 
287         fun createInterfaceStub(codebase: TextCodebase, name: String): TextClassItem =
288             createStub(codebase, name, isInterface = true)
289 
290         private fun createStub(codebase: TextCodebase, name: String, isInterface: Boolean): TextClassItem {
291             val index = if (name.endsWith(">")) name.indexOf('<') else -1
292             val qualifiedName = if (index == -1) name else name.substring(0, index)
293             val fullName = getFullName(qualifiedName)
294             val cls = TextClassItem(
295                 codebase = codebase,
296                 name = fullName,
297                 qualifiedName = qualifiedName,
298                 isInterface = isInterface,
299                 modifiers = TextModifiers(codebase, DefaultModifierList.PUBLIC)
300             )
301             cls.emit = false // it's a stub
302 
303             if (index != -1) {
304                 cls.typeParameterList = TextTypeParameterList.create(codebase, cls, name.substring(index))
305             }
306 
307             return cls
308         }
309 
310         private fun getFullName(qualifiedName: String): String {
311             var end = -1
312             val length = qualifiedName.length
313             var prev = qualifiedName[length - 1]
314             for (i in length - 2 downTo 0) {
315                 val c = qualifiedName[i]
316                 if (c == '.' && prev.isUpperCase()) {
317                     end = i + 1
318                 }
319                 prev = c
320             }
321             if (end != -1) {
322                 return qualifiedName.substring(end)
323             }
324 
325             return qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1)
326         }
327 
328         private fun hasEqualTypeVar(
329             type1: TypeItem,
330             class1: ClassItem,
331             type2: TypeItem,
332             class2: ClassItem
333         ): Boolean {
334 
335             // Given a type and its containing class,
336             // find the interface types that contains the type.
337             // For instance, for a method that looks like:
338             // class SomeClass implements InterfaceA<some.return.Type>, InterfaceB<some.return.Type>
339             //     Type foo()
340             // this function will return [InterfaceA, InterfaceB] when Type and SomeClass
341             // are passed as inputs.
342             val typeContainingInterfaces = {
343                 t: TypeItem, cl: ClassItem ->
344                 val interfaceTypes = cl.interfaceTypes()
345                     .plus(cl.toType())
346                     .plus(cl.superClassType())
347                     .filterNotNull()
348                 interfaceTypes.filter {
349                     val typeArgs = it.typeArguments(simplified = true)
350                     t.toString() in typeArgs ||
351                         t.toElementType() in typeArgs ||
352                         t.asClass()?.superClass()?.qualifiedName() in typeArgs
353                 }
354             }
355 
356             val typeContainingInterfaces1 = typeContainingInterfaces(type1, class1)
357             val typeContainingInterfaces2 = typeContainingInterfaces(type2, class2)
358 
359             if (typeContainingInterfaces1.isEmpty() || typeContainingInterfaces2.isEmpty()) {
360                 return false
361             }
362 
363             val interfaceTypesAreCovariant = {
364                 t1: TypeItem, t2: TypeItem ->
365                 t1.toErasedTypeString() == t2.toErasedTypeString() ||
366                     t1.asClass()?.superClass()?.qualifiedName() == t2.asClass()?.qualifiedName() ||
367                     t2.asClass()?.superClass()?.qualifiedName() == t1.asClass()?.qualifiedName()
368             }
369 
370             // Check if the return type containing interfaces of the two methods have an intersection.
371             return typeContainingInterfaces1.any {
372                 typeInterface1 ->
373                 typeContainingInterfaces2.any {
374                     typeInterface2 ->
375                     interfaceTypesAreCovariant(typeInterface1, typeInterface2)
376                 }
377             }
378         }
379 
380         private fun hasEqualTypeBounds(method1: MethodItem, method2: MethodItem): Boolean {
381             val typeInTypeParams = {
382                 t: TypeItem, m: MethodItem ->
383                 t in m.typeParameterList().typeParameters().map { it.toType() }
384             }
385 
386             val getTypeBounds = {
387                 t: TypeItem, m: MethodItem ->
388                 m.typeParameterList().typeParameters().single { it.toType() == t }.typeBounds().toSet()
389             }
390 
391             val returnType1 = method1.returnType()
392             val returnType2 = method2.returnType()
393 
394             // The following two methods are considered equal:
395             // method public <A extends some.package.SomeClass> A foo (Class<A>);
396             // method public <T extends some.package.SomeClass> T foo (Class<T>);
397             // This can be verified by converting return types to bounds ([some.package.SomeClass])
398             // and compare equivalence.
399             return typeInTypeParams(returnType1, method1) && typeInTypeParams(returnType2, method2) &&
400                 getTypeBounds(returnType1, method1) == getTypeBounds(returnType2, method2)
401         }
402 
403         /**
404          * Compares two [MethodItem]s and determines if the two methods have equal return types.
405          * The two methods' return types are considered equal even if the two are not identical,
406          * but are compatible in compiler level. For instance, return types in a same hierarchy tree
407          * are considered equal.
408          *
409          * @param method1 first [MethodItem] to compare the return type
410          * @param method2 second [MethodItem] to compare the return type
411          * @return a [Boolean] value representing if the two methods' return types are equal
412          */
413         fun hasEqualReturnType(method1: MethodItem, method2: MethodItem): Boolean {
414             val returnType1 = method1.returnType()
415             val returnType2 = method2.returnType()
416             val class1 = method1.containingClass()
417             val class2 = method2.containingClass()
418 
419             if (returnType1 == returnType2) return true
420 
421             if (hasEqualTypeVar(returnType1, class1, returnType2, class2)) return true
422 
423             if (hasEqualTypeBounds(method1, method2)) return true
424 
425             return false
426         }
427     }
428 }
429