1 /*
2  * 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 ), ClassItem, TypeParameterListOwner {
50 
51     init {
52         @Suppress("LeakingThis")
53         modifiers.setOwner(this)
54     }
55 
56     override val isTypeParameter: Boolean = false
57 
58     override var artifact: String? = null
59 
equalsnull60     override fun equals(other: Any?): Boolean {
61         if (this === other) return true
62         if (other !is ClassItem) return false
63 
64         return qualifiedName == other.qualifiedName()
65     }
66 
hashCodenull67     override fun hashCode(): Int {
68         return qualifiedName.hashCode()
69     }
70 
interfaceTypesnull71     override fun interfaceTypes(): List<TypeItem> = interfaceTypes
72     override fun allInterfaces(): Sequence<ClassItem> {
73         return interfaceTypes.asSequence().map { it.asClass() }.filterNotNull()
74     }
75 
76     private var innerClasses: MutableList<ClassItem> = mutableListOf()
77 
78     override var stubConstructor: ConstructorItem? = null
79 
80     override var hasPrivateConstructor: Boolean = false
81 
innerClassesnull82     override fun innerClasses(): List<ClassItem> = innerClasses
83 
84     override fun hasImplicitDefaultConstructor(): Boolean {
85         return false
86     }
87 
isInterfacenull88     override fun isInterface(): Boolean = isInterface
89     override fun isAnnotationType(): Boolean = isAnnotation
90     override fun isEnum(): Boolean = isEnum
91 
92     var containingClass: TextClassItem? = null
93     override fun containingClass(): ClassItem? = containingClass
94 
95     private var containingPackage: PackageItem? = null
96 
97     fun setContainingPackage(containingPackage: TextPackageItem) {
98         this.containingPackage = containingPackage
99     }
100 
setIsAnnotationTypenull101     fun setIsAnnotationType(isAnnotation: Boolean) {
102         this.isAnnotation = isAnnotation
103     }
104 
setIsEnumnull105     fun setIsEnum(isEnum: Boolean) {
106         this.isEnum = isEnum
107     }
108 
containingPackagenull109     override fun containingPackage(): PackageItem =
110         containingClass?.containingPackage() ?: containingPackage ?: error(this)
111 
112     override fun toType(): TypeItem {
113         val typeParameterListString = typeParameterList().toString()
114         return codebase.obtainTypeFromString(
115             if (typeParameterListString.isNotEmpty()) {
116                 // TODO: No, handle List<String>[], though this is highly unlikely in a class
117                 qualifiedName() + typeParameterListString
118             } else qualifiedName()
119         )
120     }
121 
hasTypeVariablesnull122     override fun hasTypeVariables(): Boolean {
123         return typeInfo?.hasTypeArguments() ?: false
124     }
125 
126     private var typeParameterList: TypeParameterList? = null
127 
typeParameterListnull128     override fun typeParameterList(): TypeParameterList {
129         if (typeParameterList == null) {
130             val s = typeInfo.toString()
131             // TODO: No, handle List<String>[]  (though it's not likely for type parameters)
132             val index = s.indexOf('<')
133             typeParameterList = if (index != -1) {
134                 TextTypeParameterList.create(codebase, this, s.substring(index))
135             } else {
136                 TypeParameterList.NONE
137             }
138         }
139 
140         return typeParameterList!!
141     }
142 
typeParameterListOwnerParentnull143     override fun typeParameterListOwnerParent(): TypeParameterListOwner? {
144         return containingClass
145     }
146 
resolveParameternull147     override fun resolveParameter(variable: String): TypeParameterItem? {
148         if (hasTypeVariables()) {
149             for (t in typeParameterList().typeParameters()) {
150                 if (t.simpleName() == variable) {
151                     return t
152                 }
153             }
154         }
155 
156         return null
157     }
158 
159     private var superClass: ClassItem? = null
160     private var superClassType: TypeItem? = null
161 
superClassnull162     override fun superClass(): ClassItem? = superClass
163     override fun superClassType(): TypeItem? = superClassType
164 
165     override fun setSuperClass(superClass: ClassItem?, superClassType: TypeItem?) {
166         this.superClass = superClass
167         this.superClassType = superClassType
168     }
169 
setInterfaceTypesnull170     override fun setInterfaceTypes(interfaceTypes: List<TypeItem>) {
171         this.interfaceTypes = interfaceTypes.toMutableList()
172     }
173 
174     private var typeInfo: TextTypeItem? = null
setTypeInfonull175     fun setTypeInfo(typeInfo: TextTypeItem) {
176         this.typeInfo = typeInfo
177     }
178 
asTypeInfonull179     fun asTypeInfo(): TextTypeItem {
180         if (typeInfo == null) {
181             typeInfo = codebase.obtainTypeFromString(qualifiedTypeName)
182         }
183         return typeInfo!!
184     }
185 
186     private var interfaceTypes = mutableListOf<TypeItem>()
187     private val constructors = mutableListOf<ConstructorItem>()
188     private val methods = mutableListOf<MethodItem>()
189     private val fields = mutableListOf<FieldItem>()
190     private val properties = mutableListOf<PropertyItem>()
191 
constructorsnull192     override fun constructors(): List<ConstructorItem> = constructors
193     override fun methods(): List<MethodItem> = methods
194     override fun fields(): List<FieldItem> = fields
195     override fun properties(): List<PropertyItem> = properties
196 
197     fun addInterface(itf: TypeItem) {
198         interfaceTypes.add(itf)
199     }
200 
addConstructornull201     fun addConstructor(constructor: TextConstructorItem) {
202         constructors += constructor
203     }
204 
addMethodnull205     fun addMethod(method: TextMethodItem) {
206         methods += method
207     }
208 
addFieldnull209     fun addField(field: TextFieldItem) {
210         fields += field
211     }
212 
addPropertynull213     fun addProperty(property: TextPropertyItem) {
214         properties += property
215     }
216 
addEnumConstantnull217     fun addEnumConstant(field: TextFieldItem) {
218         field.setEnumConstant(true)
219         fields += field
220     }
221 
addInnerClassnull222     fun addInnerClass(cls: TextClassItem) {
223         innerClasses.add(cls)
224     }
225 
filteredSuperClassTypenull226     override fun filteredSuperClassType(predicate: Predicate<Item>): TypeItem? {
227         // No filtering in signature files: we assume signature APIs
228         // have already been filtered and all items should match.
229         // This lets us load signature files and rewrite them using updated
230         // output formats etc.
231         return superClassType
232     }
233 
234     private var retention: AnnotationRetention? = null
235 
getRetentionnull236     override fun getRetention(): AnnotationRetention {
237         retention?.let { return it }
238 
239         if (!isAnnotationType()) {
240             error("getRetention() should only be called on annotation classes")
241         }
242 
243         retention = ClassItem.findRetention(this)
244         return retention!!
245     }
246 
247     private var fullName: String = name
simpleNamenull248     override fun simpleName(): String = name.substring(name.lastIndexOf('.') + 1)
249     override fun fullName(): String = fullName
250     override fun qualifiedName(): String = qualifiedName
251     override fun isDefined(): Boolean {
252         assert(emit == (position != SourcePositionInfo.UNKNOWN))
253         return emit
254     }
toStringnull255     override fun toString(): String = qualifiedName()
256 
257     override fun mapTypeVariables(target: ClassItem): Map<String, String> {
258         return emptyMap()
259     }
260 
261     companion object {
createClassStubnull262         fun createClassStub(codebase: TextCodebase, name: String): TextClassItem =
263             createStub(codebase, name, isInterface = false)
264 
265         fun createInterfaceStub(codebase: TextCodebase, name: String): TextClassItem =
266             createStub(codebase, name, isInterface = true)
267 
268         private fun createStub(codebase: TextCodebase, name: String, isInterface: Boolean): TextClassItem {
269             val index = if (name.endsWith(">")) name.indexOf('<') else -1
270             val qualifiedName = if (index == -1) name else name.substring(0, index)
271             val fullName = getFullName(qualifiedName)
272             val cls = TextClassItem(
273                 codebase = codebase,
274                 name = fullName,
275                 qualifiedName = qualifiedName,
276                 isInterface = isInterface,
277                 modifiers = TextModifiers(codebase, DefaultModifierList.PUBLIC)
278             )
279             cls.emit = false // it's a stub
280 
281             if (index != -1) {
282                 cls.typeParameterList = TextTypeParameterList.create(codebase, cls, name.substring(index))
283             }
284 
285             return cls
286         }
287 
getFullNamenull288         private fun getFullName(qualifiedName: String): String {
289             var end = -1
290             val length = qualifiedName.length
291             var prev = qualifiedName[length - 1]
292             for (i in length - 2 downTo 0) {
293                 val c = qualifiedName[i]
294                 if (c == '.' && prev.isUpperCase()) {
295                     end = i + 1
296                 }
297                 prev = c
298             }
299             if (end != -1) {
300                 return qualifiedName.substring(end)
301             }
302 
303             return qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1)
304         }
305     }
306 }
307