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.BaseItemVisitor 20 import com.android.tools.metalava.model.ClassItem 21 import com.android.tools.metalava.model.ExceptionTypeItem 22 import com.android.tools.metalava.model.Item 23 import com.android.tools.metalava.model.MethodItem 24 import com.android.tools.metalava.model.ModifierListWriter 25 import com.android.tools.metalava.model.TypeItem 26 import com.android.tools.metalava.model.TypeParameterList 27 import com.android.tools.metalava.model.psi.PsiClassItem 28 import java.io.PrintWriter 29 import java.util.function.Predicate 30 31 internal class KotlinStubWriter( 32 private val writer: PrintWriter, 33 private val modifierListWriter: ModifierListWriter, 34 private val filterReference: Predicate<Item>, 35 private val preFiltered: Boolean = true, 36 private val config: StubWriterConfig, 37 ) : BaseItemVisitor() { 38 39 override fun visitClass(cls: ClassItem) { 40 if (cls.isTopLevelClass()) { 41 val qualifiedName = cls.containingPackage().qualifiedName() 42 if (qualifiedName.isNotBlank()) { 43 writer.println("package $qualifiedName") 44 writer.println() 45 } 46 cls.getSourceFile()?.getImports(filterReference)?.let { 47 for (item in it) { 48 writer.println("import ${item.pattern}") 49 } 50 writer.println() 51 } 52 } 53 appendDocumentation(cls, writer, config) 54 55 writer.println("@file:Suppress(\"ALL\")") 56 57 appendModifiers(cls) 58 59 when { 60 cls.isAnnotationType() -> writer.print("annotation class") 61 cls.isInterface() -> writer.print("interface") 62 cls.isEnum() -> writer.print("enum class") 63 else -> writer.print("class") 64 } 65 66 writer.print(" ") 67 writer.print(cls.simpleName()) 68 69 generateTypeParameterList(typeList = cls.typeParameterList, addSpace = false) 70 val printedSuperClass = generateSuperClassDeclaration(cls) 71 generateInterfaceList(cls, printedSuperClass) 72 writer.print(" {\n") 73 } 74 75 private fun generateTypeParameterList(typeList: TypeParameterList, addSpace: Boolean) { 76 val typeListString = typeList.toString() 77 if (typeListString.isNotEmpty()) { 78 writer.print(typeListString) 79 80 if (addSpace) { 81 writer.print(' ') 82 } 83 } 84 } 85 86 private fun appendModifiers(item: Item) = modifierListWriter.write(item) 87 88 private fun generateSuperClassDeclaration(cls: ClassItem): Boolean { 89 if (cls.isEnum() || cls.isAnnotationType()) { 90 // No extends statement for enums and annotations; it's implied by the "enum" and 91 // "@interface" keywords 92 return false 93 } 94 95 val superClass = 96 if (preFiltered) cls.superClassType() else cls.filteredSuperClassType(filterReference) 97 98 if (superClass != null && !superClass.isJavaLangObject()) { 99 val qualifiedName = 100 superClass.toTypeString() // TODO start passing language = Language.KOTLIN 101 writer.print(" : ") 102 103 if (qualifiedName.contains("<")) { 104 // TODO: push this into the model at filter-time such that clients don't need 105 // to remember to do this!! 106 val s = superClass.asClass() 107 if (s != null) { 108 val replaced = superClass.convertType(cls, s) 109 writer.print(replaced.toTypeString()) 110 return true 111 } 112 } 113 (cls as PsiClassItem).psiClass.superClassType 114 writer.print(qualifiedName) 115 // TODO: print out arguments to the parent constructor 116 writer.print("()") 117 return true 118 } 119 return false 120 } 121 122 private fun generateInterfaceList(cls: ClassItem, printedSuperClass: Boolean) { 123 if (cls.isAnnotationType()) { 124 // No extends statement for annotations; it's implied by the "@interface" keyword 125 return 126 } 127 128 val interfaces = 129 if (preFiltered) cls.interfaceTypes().asSequence() 130 else cls.filteredInterfaceTypes(filterReference).asSequence() 131 132 if (interfaces.any()) { 133 if (printedSuperClass) { 134 writer.print(",") 135 } else { 136 writer.print(" :") 137 } 138 interfaces.forEachIndexed { index, type -> 139 if (index > 0) { 140 writer.print(",") 141 } 142 writer.print(" ") 143 writer.print(type.toTypeString()) // TODO start passing language = Language.KOTLIN 144 } 145 } 146 } 147 148 private fun writeType(type: TypeItem?) { 149 type ?: return 150 151 val typeString = 152 type.toTypeString( 153 annotations = false, 154 kotlinStyleNulls = true, 155 filter = filterReference 156 // TODO pass in language = Language.KOTLIN 157 ) 158 159 writer.print(typeString) 160 } 161 162 override fun visitMethod(method: MethodItem) { 163 if (method.isKotlinProperty()) return // will be handled by visitProperty 164 165 writer.println() 166 appendDocumentation(method, writer, config) 167 168 // TODO: Should be an annotation 169 generateThrowsList(method) 170 171 appendModifiers(method) 172 generateTypeParameterList(typeList = method.typeParameterList, addSpace = true) 173 174 writer.print("fun ") 175 writer.print(method.name()) 176 generateParameterList(method) 177 178 writer.print(": ") 179 val returnType = method.returnType() 180 writeType(returnType) 181 182 if (method.containingClass().isAnnotationType()) { 183 val default = method.defaultValue() 184 if (default.isNotEmpty()) { 185 writer.print(" default ") 186 writer.print(default) 187 } 188 } 189 190 if (ModifierListWriter.requiresMethodBodyInStubs(method)) { 191 writer.print(" = ") 192 writeThrowStub() 193 } 194 writer.println() 195 } 196 197 override fun afterVisitClass(cls: ClassItem) { 198 writer.println("}\n\n") 199 } 200 201 private fun writeThrowStub() { 202 writer.write("error(\"Stub!\")") 203 } 204 205 private fun generateParameterList(method: MethodItem) { 206 writer.print("(") 207 method.parameters().asSequence().forEachIndexed { i, parameter -> 208 if (i > 0) { 209 writer.print(", ") 210 } 211 appendModifiers(parameter) 212 val name = parameter.publicName() ?: parameter.name() 213 writer.print(name) 214 writer.print(": ") 215 writeType(parameter.type()) 216 } 217 writer.print(")") 218 } 219 220 private fun generateThrowsList(method: MethodItem) { 221 // Note that throws types are already sorted internally to help comparison matching 222 val throws = 223 if (preFiltered) { 224 method.throwsTypes().asSequence() 225 } else { 226 method.filteredThrowsTypes(filterReference).asSequence() 227 } 228 if (throws.any()) { 229 writer.print("@Throws(") 230 throws.asSequence().sortedWith(ExceptionTypeItem.fullNameComparator).forEachIndexed { 231 i, 232 type -> 233 if (i > 0) { 234 writer.print(",") 235 } 236 writer.print(type.toTypeString()) 237 writer.print("::class") 238 } 239 writer.print(")") 240 } 241 } 242 } 243