• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2020 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.stub
18 
19 import com.android.tools.metalava.model.AnnotationTarget
20 import com.android.tools.metalava.model.ClassItem
21 import com.android.tools.metalava.model.Item
22 import com.android.tools.metalava.model.Language
23 import com.android.tools.metalava.model.MemberItem
24 import com.android.tools.metalava.model.MethodItem
25 import com.android.tools.metalava.model.ModifierList
26 import com.android.tools.metalava.model.PackageItem
27 import com.android.tools.metalava.model.TypeItem
28 import com.android.tools.metalava.model.TypeParameterList
29 import com.android.tools.metalava.model.psi.PsiClassItem
30 import com.android.tools.metalava.model.visitors.ItemVisitor
31 import java.io.PrintWriter
32 import java.util.function.Predicate
33 
34 class KotlinStubWriter(
35     private val writer: PrintWriter,
36     private val filterEmit: Predicate<Item>,
37     private val filterReference: Predicate<Item>,
38     private val generateAnnotations: Boolean = false,
39     private val preFiltered: Boolean = true,
40     private val docStubs: Boolean
41 ) : ItemVisitor() {
42     private val annotationTarget = if (docStubs) AnnotationTarget.DOC_STUBS_FILE else AnnotationTarget.SDK_STUBS_FILE
43 
44     override fun visitClass(cls: ClassItem) {
45         if (cls.isTopLevelClass()) {
46             val qualifiedName = cls.containingPackage().qualifiedName()
47             if (qualifiedName.isNotBlank()) {
48                 writer.println("package $qualifiedName")
49                 writer.println()
50             }
51             cls.getSourceFile()?.getImportStatements(filterReference)?.let {
52                 for (item in it) {
53                     when (item) {
54                         is PackageItem ->
55                             writer.println("import ${item.qualifiedName()}.*")
56                         is ClassItem ->
57                             writer.println("import ${item.qualifiedName()}")
58                         is MemberItem ->
59                             writer.println("import static ${item.containingClass().qualifiedName()}.${item.name()}")
60                     }
61                 }
62                 writer.println()
63             }
64         }
65         appendDocumentation(cls, writer, docStubs)
66 
67         writer.println("@file:Suppress(\"ALL\")")
68 
69         // Need to filter out abstract from the modifiers list and turn it
70         // into a concrete method to make the stub compile
71         val removeAbstract = cls.modifiers.isAbstract() && (cls.isEnum() || cls.isAnnotationType())
72 
73         appendModifiers(cls, cls.modifiers, removeAbstract)
74 
75         when {
76             cls.isAnnotationType() -> writer.print("annotation class")
77             cls.isInterface() -> writer.print("interface")
78             cls.isEnum() -> writer.print("enum class")
79             else -> writer.print("class")
80         }
81 
82         writer.print(" ")
83         writer.print(cls.simpleName())
84 
85         generateTypeParameterList(typeList = cls.typeParameterList(), addSpace = false)
86         val printedSuperClass = generateSuperClassDeclaration(cls)
87         generateInterfaceList(cls, printedSuperClass)
88         writer.print(" {\n")
89     }
90 
91     private fun generateTypeParameterList(
92         typeList: TypeParameterList,
93         addSpace: Boolean
94     ) {
95         val typeListString = typeList.toString()
96         if (typeListString.isNotEmpty()) {
97             writer.print(typeListString)
98 
99             if (addSpace) {
100                 writer.print(' ')
101             }
102         }
103     }
104 
105     private fun appendModifiers(
106         item: Item,
107         modifiers: ModifierList,
108         removeAbstract: Boolean,
109         removeFinal: Boolean = false,
110         addPublic: Boolean = false
111     ) {
112         val separateLines = item is ClassItem || item is MethodItem
113 
114         ModifierList.write(
115             writer, modifiers, item,
116             target = annotationTarget,
117             skipNullnessAnnotations = true,
118             includeDeprecated = true,
119             runtimeAnnotationsOnly = !generateAnnotations,
120             removeAbstract = removeAbstract,
121             removeFinal = removeFinal,
122             addPublic = addPublic,
123             separateLines = separateLines,
124             language = Language.KOTLIN
125         )
126     }
127 
128     private fun generateSuperClassDeclaration(cls: ClassItem): Boolean {
129         if (cls.isEnum() || cls.isAnnotationType()) {
130             // No extends statement for enums and annotations; it's implied by the "enum" and "@interface" keywords
131             return false
132         }
133 
134         val superClass = if (preFiltered)
135             cls.superClassType()
136         else cls.filteredSuperClassType(filterReference)
137 
138         if (superClass != null && !superClass.isJavaLangObject()) {
139             val qualifiedName = superClass.toTypeString() // TODO start passing language = Language.KOTLIN
140             writer.print(" : ")
141 
142             if (qualifiedName.contains("<")) {
143                 // TODO: push this into the model at filter-time such that clients don't need
144                 // to remember to do this!!
145                 val s = superClass.asClass()
146                 if (s != null) {
147                     val map = cls.mapTypeVariables(s)
148                     val replaced = superClass.convertTypeString(map)
149                     writer.print(replaced)
150                     return true
151                 }
152             }
153             (cls as PsiClassItem).psiClass.superClassType
154             writer.print(qualifiedName)
155             // TODO: print out arguments to the parent constructor
156             writer.print("()")
157             return true
158         }
159         return false
160     }
161 
162     private fun generateInterfaceList(cls: ClassItem, printedSuperClass: Boolean) {
163         if (cls.isAnnotationType()) {
164             // No extends statement for annotations; it's implied by the "@interface" keyword
165             return
166         }
167 
168         val interfaces = if (preFiltered)
169             cls.interfaceTypes().asSequence()
170         else cls.filteredInterfaceTypes(filterReference).asSequence()
171 
172         if (interfaces.any()) {
173             if (printedSuperClass) {
174                 writer.print(",")
175             } else {
176                 writer.print(" :")
177             }
178             interfaces.forEachIndexed { index, type ->
179                 if (index > 0) {
180                     writer.print(",")
181                 }
182                 writer.print(" ")
183                 writer.print(type.toTypeString()) // TODO start passing language = Language.KOTLIN
184             }
185         }
186     }
187 
188     private fun writeType(
189         item: Item,
190         type: TypeItem?
191     ) {
192         type ?: return
193 
194         val typeString = type.toTypeString(
195             outerAnnotations = false,
196             innerAnnotations = generateAnnotations,
197             erased = false,
198             kotlinStyleNulls = true,
199             context = item,
200             filter = filterReference
201             // TODO pass in language = Language.KOTLIN
202         )
203 
204         writer.print(typeString)
205     }
206 
207     override fun visitMethod(method: MethodItem) {
208         if (method.isKotlinProperty()) return // will be handled by visitProperty
209         val containingClass = method.containingClass()
210         val modifiers = method.modifiers
211         val isEnum = containingClass.isEnum()
212         val isAnnotation = containingClass.isAnnotationType()
213 
214         writer.println()
215         appendDocumentation(method, writer, docStubs)
216 
217         // TODO: Should be an annotation
218         generateThrowsList(method)
219 
220         // Need to filter out abstract from the modifiers list and turn it
221         // into a concrete method to make the stub compile
222         val removeAbstract = modifiers.isAbstract() && (isEnum || isAnnotation)
223 
224         appendModifiers(method, modifiers, removeAbstract, false)
225         generateTypeParameterList(typeList = method.typeParameterList(), addSpace = true)
226 
227         writer.print("fun ")
228         writer.print(method.name())
229         generateParameterList(method)
230 
231         writer.print(": ")
232         val returnType = method.returnType()
233         writeType(method, returnType)
234 
235         if (isAnnotation) {
236             val default = method.defaultValue()
237             if (default.isNotEmpty()) {
238                 writer.print(" default ")
239                 writer.print(default)
240             }
241         }
242 
243         if (modifiers.isAbstract() && !isEnum || isAnnotation || modifiers.isNative()) {
244             // do nothing
245         } else {
246             writer.print(" = ")
247             writeThrowStub()
248         }
249         writer.println()
250     }
251 
252     override fun afterVisitClass(cls: ClassItem) {
253         writer.println("}\n\n")
254     }
255 
256     private fun writeThrowStub() {
257         writer.write("error(\"Stub!\")")
258     }
259 
260     private fun generateParameterList(method: MethodItem) {
261         writer.print("(")
262         method.parameters().asSequence().forEachIndexed { i, parameter ->
263             if (i > 0) {
264                 writer.print(", ")
265             }
266             appendModifiers(
267                 parameter,
268                 parameter.modifiers,
269                 removeAbstract = false,
270                 removeFinal = false,
271                 addPublic = false
272             )
273             val name = parameter.publicName() ?: parameter.name()
274             writer.print(name)
275             writer.print(": ")
276             writeType(method, parameter.type())
277         }
278         writer.print(")")
279     }
280 
281     private fun generateThrowsList(method: MethodItem) {
282         // Note that throws types are already sorted internally to help comparison matching
283         val throws = if (preFiltered) {
284             method.throwsTypes().asSequence()
285         } else {
286             method.filteredThrowsTypes(filterReference).asSequence()
287         }
288         if (throws.any()) {
289             writer.print("@Throws(")
290             throws.asSequence().sortedWith(ClassItem.fullNameComparator).forEachIndexed { i, type ->
291                 if (i > 0) {
292                     writer.print(",")
293                 }
294                 writer.print(type.qualifiedName())
295                 writer.print("::class")
296             }
297             writer.print(")")
298         }
299     }
300 }
301