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