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