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.doclava1.SourcePositionInfo
20 import com.android.tools.metalava.doclava1.TextCodebase
21 import com.android.tools.metalava.model.AnnotationRetention
22 import com.android.tools.metalava.model.ClassItem
23 import com.android.tools.metalava.model.ConstructorItem
24 import com.android.tools.metalava.model.DefaultModifierList
25 import com.android.tools.metalava.model.FieldItem
26 import com.android.tools.metalava.model.Item
27 import com.android.tools.metalava.model.MethodItem
28 import com.android.tools.metalava.model.PackageItem
29 import com.android.tools.metalava.model.PropertyItem
30 import com.android.tools.metalava.model.TypeItem
31 import com.android.tools.metalava.model.TypeParameterItem
32 import com.android.tools.metalava.model.TypeParameterList
33 import com.android.tools.metalava.model.TypeParameterListOwner
34 import java.util.function.Predicate
35 
36 open class TextClassItem(
37     override val codebase: TextCodebase,
38     position: SourcePositionInfo = SourcePositionInfo.UNKNOWN,
39     modifiers: TextModifiers,
40     private var isInterface: Boolean = false,
41     private var isEnum: Boolean = false,
42     private var isAnnotation: Boolean = false,
43     val qualifiedName: String = "",
44     private val qualifiedTypeName: String = qualifiedName,
45     var name: String = qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1),
46     val annotations: List<String>? = null
47 ) : TextItem(
48     codebase = codebase,
49     position = position,
50     modifiers = modifiers
51 ), ClassItem, TypeParameterListOwner {
52 
53     init {
54         @Suppress("LeakingThis")
55         modifiers.setOwner(this)
56     }
57 
58     override val isTypeParameter: Boolean = false
59 
60     override var notStrippable = false
61 
62     override var artifact: String? = null
63 
equalsnull64     override fun equals(other: Any?): Boolean {
65         if (this === other) return true
66         if (other !is ClassItem) return false
67 
68         return qualifiedName == other.qualifiedName()
69     }
70 
hashCodenull71     override fun hashCode(): Int {
72         return qualifiedName.hashCode()
73     }
74 
interfaceTypesnull75     override fun interfaceTypes(): List<TypeItem> = interfaceTypes
76     override fun allInterfaces(): Sequence<ClassItem> {
77         return interfaceTypes.asSequence().map { it.asClass() }.filterNotNull()
78     }
79 
80     private var innerClasses: List<ClassItem> = mutableListOf()
81 
82     override var stubConstructor: ConstructorItem? = null
83 
84     override var hasPrivateConstructor: Boolean = false
85 
innerClassesnull86     override fun innerClasses(): List<ClassItem> = innerClasses
87 
88     override fun hasImplicitDefaultConstructor(): Boolean {
89         return false
90     }
91 
isInterfacenull92     override fun isInterface(): Boolean = isInterface
93     override fun isAnnotationType(): Boolean = isAnnotation
94     override fun isEnum(): Boolean = isEnum
95 
96     var containingClass: TextClassItem? = null
97     override fun containingClass(): ClassItem? = containingClass
98 
99     private var containingPackage: PackageItem? = null
100 
101     fun setContainingPackage(containingPackage: TextPackageItem) {
102         this.containingPackage = containingPackage
103     }
104 
setIsAnnotationTypenull105     fun setIsAnnotationType(isAnnotation: Boolean) {
106         this.isAnnotation = isAnnotation
107     }
108 
setIsEnumnull109     fun setIsEnum(isEnum: Boolean) {
110         this.isEnum = isEnum
111     }
112 
containingPackagenull113     override fun containingPackage(): PackageItem =
114         containingClass?.containingPackage() ?: containingPackage ?: error(this)
115 
116     override fun toType(): TypeItem {
117         val typeParameterListString = typeParameterList().toString()
118         return codebase.obtainTypeFromString(
119             if (typeParameterListString.isNotEmpty()) {
120                 // TODO: No, handle List<String>[], though this is highly unlikely in a class
121                 qualifiedName() + typeParameterListString
122             } else qualifiedName()
123         )
124     }
125 
hasTypeVariablesnull126     override fun hasTypeVariables(): Boolean {
127         return typeInfo?.hasTypeArguments() ?: false
128     }
129 
130     private var typeParameterList: TypeParameterList? = null
131 
typeParameterListnull132     override fun typeParameterList(): TypeParameterList {
133         if (typeParameterList == null) {
134             val s = typeInfo.toString()
135             // TODO: No, handle List<String>[]  (though it's not likely for type parameters)
136             val index = s.indexOf('<')
137             typeParameterList = if (index != -1) {
138                 TextTypeParameterList.create(codebase, this, s.substring(index))
139             } else {
140                 TypeParameterList.NONE
141             }
142         }
143 
144         return typeParameterList!!
145     }
146 
typeParameterListOwnerParentnull147     override fun typeParameterListOwnerParent(): TypeParameterListOwner? {
148         return containingClass
149     }
150 
resolveParameternull151     override fun resolveParameter(variable: String): TypeParameterItem? {
152         if (hasTypeVariables()) {
153             for (t in typeParameterList().typeParameters()) {
154                 if (t.simpleName() == variable) {
155                     return t
156                 }
157             }
158         }
159 
160         return null
161     }
162 
163     private var superClass: ClassItem? = null
164     private var superClassType: TypeItem? = null
165 
superClassnull166     override fun superClass(): ClassItem? = superClass
167     override fun superClassType(): TypeItem? = superClassType
168 
169     override fun setSuperClass(superClass: ClassItem?, superClassType: TypeItem?) {
170         this.superClass = superClass
171         this.superClassType = superClassType
172     }
173 
setInterfaceTypesnull174     override fun setInterfaceTypes(interfaceTypes: List<TypeItem>) {
175         this.interfaceTypes = interfaceTypes.toMutableList()
176     }
177 
178     private var typeInfo: TextTypeItem? = null
setTypeInfonull179     fun setTypeInfo(typeInfo: TextTypeItem) {
180         this.typeInfo = typeInfo
181     }
182 
asTypeInfonull183     fun asTypeInfo(): TextTypeItem {
184         if (typeInfo == null) {
185             typeInfo = codebase.obtainTypeFromString(qualifiedTypeName)
186         }
187         return typeInfo!!
188     }
189 
190     private var interfaceTypes = mutableListOf<TypeItem>()
191     private val constructors = mutableListOf<ConstructorItem>()
192     private val methods = mutableListOf<MethodItem>()
193     private val fields = mutableListOf<FieldItem>()
194     private val properties = mutableListOf<PropertyItem>()
195 
constructorsnull196     override fun constructors(): List<ConstructorItem> = constructors
197     override fun methods(): List<MethodItem> = methods
198     override fun fields(): List<FieldItem> = fields
199     override fun properties(): List<PropertyItem> = properties
200 
201     fun addInterface(itf: TypeItem) {
202         interfaceTypes.add(itf)
203     }
204 
addConstructornull205     fun addConstructor(constructor: TextConstructorItem) {
206         constructors += constructor
207     }
208 
addMethodnull209     fun addMethod(method: TextMethodItem) {
210         methods += method
211     }
212 
addFieldnull213     fun addField(field: TextFieldItem) {
214         fields += field
215     }
216 
addPropertynull217     fun addProperty(property: TextPropertyItem) {
218         properties += property
219     }
220 
addEnumConstantnull221     fun addEnumConstant(field: TextFieldItem) {
222         field.setEnumConstant(true)
223         fields += field
224     }
225 
addInnerClassnull226     fun addInnerClass(cls: TextClassItem) {
227         innerClasses += cls
228     }
229 
filteredSuperClassTypenull230     override fun filteredSuperClassType(predicate: Predicate<Item>): TypeItem? {
231         // No filtering in signature files: we assume signature APIs
232         // have already been filtered and all items should match.
233         // This lets us load signature files and rewrite them using updated
234         // output formats etc.
235         return superClassType
236     }
237 
238     private var retention: AnnotationRetention? = null
239 
getRetentionnull240     override fun getRetention(): AnnotationRetention {
241         retention?.let { return it }
242 
243         if (!isAnnotationType()) {
244             error("getRetention() should only be called on annotation classes")
245         }
246 
247         retention = ClassItem.findRetention(this)
248         return retention!!
249     }
250 
251     private var fullName: String = name
simpleNamenull252     override fun simpleName(): String = name.substring(name.lastIndexOf('.') + 1)
253     override fun fullName(): String = fullName
254     override fun qualifiedName(): String = qualifiedName
255     override fun isDefined(): Boolean {
256         assert(emit == (position != SourcePositionInfo.UNKNOWN))
257         return emit
258     }
toStringnull259     override fun toString(): String = qualifiedName()
260 
261     override fun mapTypeVariables(target: ClassItem): Map<String, String> {
262         return emptyMap()
263     }
264 
265     companion object {
createClassStubnull266         fun createClassStub(codebase: TextCodebase, name: String): TextClassItem =
267             createStub(codebase, name, isInterface = false)
268 
269         fun createInterfaceStub(codebase: TextCodebase, name: String): TextClassItem =
270             createStub(codebase, name, isInterface = true)
271 
272         private fun createStub(codebase: TextCodebase, name: String, isInterface: Boolean): TextClassItem {
273             val index = if (name.endsWith(">")) name.indexOf('<') else -1
274             val qualifiedName = if (index == -1) name else name.substring(0, index)
275             val fullName = getFullName(qualifiedName)
276             val cls = TextClassItem(
277                 codebase = codebase,
278                 name = fullName,
279                 qualifiedName = qualifiedName,
280                 isInterface = isInterface,
281                 modifiers = TextModifiers(codebase, DefaultModifierList.PUBLIC)
282             )
283             cls.emit = false // it's a stub
284 
285             if (index != -1) {
286                 cls.typeParameterList = TextTypeParameterList.create(codebase, cls, name.substring(index))
287             }
288 
289             return cls
290         }
291 
getFullNamenull292         private fun getFullName(qualifiedName: String): String {
293             var end = -1
294             val length = qualifiedName.length
295             var prev = qualifiedName[length - 1]
296             for (i in length - 2 downTo 0) {
297                 val c = qualifiedName[i]
298                 if (c == '.' && prev.isUpperCase()) {
299                     end = i + 1
300                 }
301                 prev = c
302             }
303             if (end != -1) {
304                 return qualifiedName.substring(end)
305             }
306 
307             return qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1)
308         }
309     }
310 }
311