1 /* <lambda>null2 * 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.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.ConstructorItem 22 import com.android.tools.metalava.model.FieldItem 23 import com.android.tools.metalava.model.Item 24 import com.android.tools.metalava.model.MemberItem 25 import com.android.tools.metalava.model.MethodItem 26 import com.android.tools.metalava.model.ModifierList 27 import com.android.tools.metalava.model.PackageItem 28 import com.android.tools.metalava.model.TypeParameterList 29 import com.android.tools.metalava.model.visitors.ItemVisitor 30 import com.android.tools.metalava.options 31 import java.io.PrintWriter 32 import java.util.function.Predicate 33 34 class JavaStubWriter( 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 if (options.includeDocumentationInStubs) { 52 // All the classes referenced in the stubs are fully qualified, so no imports are 53 // needed. However, in some cases for javadoc, replacement with fully qualified name 54 // fails and thus we need to include imports for the stubs to compile. 55 cls.getSourceFile()?.getImportStatements(filterReference)?.let { 56 for (item in it) { 57 when (item) { 58 is PackageItem -> 59 writer.println("import ${item.qualifiedName()}.*;") 60 is ClassItem -> 61 writer.println("import ${item.qualifiedName()};") 62 is MemberItem -> 63 writer.println( 64 "import static ${item.containingClass() 65 .qualifiedName()}.${item.name()};" 66 ) 67 } 68 } 69 writer.println() 70 } 71 } 72 } 73 74 appendDocumentation(cls, writer, docStubs) 75 76 // "ALL" doesn't do it; compiler still warns unless you actually explicitly list "unchecked" 77 writer.println("@SuppressWarnings({\"unchecked\", \"deprecation\", \"all\"})") 78 79 // Need to filter out abstract from the modifiers list and turn it 80 // into a concrete method to make the stub compile 81 val removeAbstract = cls.modifiers.isAbstract() && (cls.isEnum() || cls.isAnnotationType()) 82 83 appendModifiers(cls, removeAbstract) 84 85 when { 86 cls.isAnnotationType() -> writer.print("@interface") 87 cls.isInterface() -> writer.print("interface") 88 cls.isEnum() -> writer.print("enum") 89 else -> writer.print("class") 90 } 91 92 writer.print(" ") 93 writer.print(cls.simpleName()) 94 95 generateTypeParameterList(typeList = cls.typeParameterList(), addSpace = false) 96 generateSuperClassDeclaration(cls) 97 generateInterfaceList(cls) 98 writer.print(" {\n") 99 100 if (cls.isEnum()) { 101 var first = true 102 // Enums should preserve the original source order, not alphabetical etc sort 103 for (field in cls.filteredFields(filterReference, true).sortedBy { it.sortingRank }) { 104 if (field.isEnumConstant()) { 105 if (first) { 106 first = false 107 } else { 108 writer.write(",\n") 109 } 110 appendDocumentation(field, writer, docStubs) 111 112 // Can't just appendModifiers(field, true, true): enum constants 113 // don't take modifier lists, only annotations 114 ModifierList.writeAnnotations( 115 item = field, 116 target = annotationTarget, 117 runtimeAnnotationsOnly = !generateAnnotations, 118 includeDeprecated = true, 119 writer = writer, 120 separateLines = true, 121 list = field.modifiers, 122 skipNullnessAnnotations = false, 123 omitCommonPackages = false 124 ) 125 126 writer.write(field.name()) 127 } 128 } 129 writer.println(";") 130 } 131 132 generateMissingConstructors(cls) 133 } 134 135 override fun afterVisitClass(cls: ClassItem) { 136 writer.print("}\n\n") 137 } 138 139 private fun appendModifiers( 140 item: Item, 141 removeAbstract: Boolean = false, 142 removeFinal: Boolean = false, 143 addPublic: Boolean = false 144 ) { 145 appendModifiers(item, item.modifiers, removeAbstract, removeFinal, addPublic) 146 } 147 148 private fun appendModifiers( 149 item: Item, 150 modifiers: ModifierList, 151 removeAbstract: Boolean, 152 removeFinal: Boolean = false, 153 addPublic: Boolean = false 154 ) { 155 val separateLines = item is ClassItem || item is MethodItem 156 157 ModifierList.write( 158 writer, modifiers, item, 159 target = annotationTarget, 160 includeDeprecated = true, 161 runtimeAnnotationsOnly = !generateAnnotations, 162 removeAbstract = removeAbstract, 163 removeFinal = removeFinal, 164 addPublic = addPublic, 165 separateLines = separateLines 166 ) 167 } 168 169 private fun generateSuperClassDeclaration(cls: ClassItem) { 170 if (cls.isEnum() || cls.isAnnotationType()) { 171 // No extends statement for enums and annotations; it's implied by the "enum" and "@interface" keywords 172 return 173 } 174 175 val superClass = if (preFiltered) 176 cls.superClassType() 177 else cls.filteredSuperClassType(filterReference) 178 179 if (superClass != null && !superClass.isJavaLangObject()) { 180 val qualifiedName = superClass.toTypeString() 181 writer.print(" extends ") 182 183 if (qualifiedName.contains("<")) { 184 // TODO: I need to push this into the model at filter-time such that clients don't need 185 // to remember to do this!! 186 val s = superClass.asClass() 187 if (s != null) { 188 val map = cls.mapTypeVariables(s) 189 val replaced = superClass.convertTypeString(map) 190 writer.print(replaced) 191 return 192 } 193 } 194 writer.print(qualifiedName) 195 } 196 } 197 198 private fun generateInterfaceList(cls: ClassItem) { 199 if (cls.isAnnotationType()) { 200 // No extends statement for annotations; it's implied by the "@interface" keyword 201 return 202 } 203 204 val interfaces = if (preFiltered) 205 cls.interfaceTypes().asSequence() 206 else cls.filteredInterfaceTypes(filterReference).asSequence() 207 208 if (interfaces.any()) { 209 if (cls.isInterface() && cls.superClassType() != null) 210 writer.print(", ") 211 else writer.print(" implements") 212 interfaces.forEachIndexed { index, type -> 213 if (index > 0) { 214 writer.print(",") 215 } 216 writer.print(" ") 217 writer.print(type.toTypeString()) 218 } 219 } 220 } 221 222 private fun generateTypeParameterList( 223 typeList: TypeParameterList, 224 addSpace: Boolean 225 ) { 226 // TODO: Do I need to map type variables? 227 228 val typeListString = typeList.toString() 229 if (typeListString.isNotEmpty()) { 230 writer.print(typeListString) 231 232 if (addSpace) { 233 writer.print(' ') 234 } 235 } 236 } 237 238 override fun visitConstructor(constructor: ConstructorItem) { 239 writeConstructor(constructor, constructor.superConstructor) 240 } 241 242 private fun writeConstructor( 243 constructor: MethodItem, 244 superConstructor: MethodItem? 245 ) { 246 writer.println() 247 appendDocumentation(constructor, writer, docStubs) 248 appendModifiers(constructor, false) 249 generateTypeParameterList( 250 typeList = constructor.typeParameterList(), 251 addSpace = true 252 ) 253 writer.print(constructor.containingClass().simpleName()) 254 255 generateParameterList(constructor) 256 generateThrowsList(constructor) 257 258 writer.print(" { ") 259 260 writeConstructorBody(constructor, superConstructor) 261 writer.println(" }") 262 } 263 264 private fun writeConstructorBody(constructor: MethodItem?, superConstructor: MethodItem?) { 265 // Find any constructor in parent that we can compile against 266 superConstructor?.let { it -> 267 val parameters = it.parameters() 268 val invokeOnThis = constructor != null && constructor.containingClass() == it.containingClass() 269 if (invokeOnThis || parameters.isNotEmpty()) { 270 val includeCasts = parameters.isNotEmpty() && 271 it.containingClass().constructors().filter { filterReference.test(it) }.size > 1 272 if (invokeOnThis) { 273 writer.print("this(") 274 } else { 275 writer.print("super(") 276 } 277 parameters.forEachIndexed { index, parameter -> 278 if (index > 0) { 279 writer.write(", ") 280 } 281 val type = parameter.type() 282 if (!type.primitive) { 283 if (includeCasts) { 284 // Types with varargs can't appear as varargs when used as an argument 285 val typeString = type.toErasedTypeString(it).replace("...", "[]") 286 writer.write("(") 287 if (type.asTypeParameter(superConstructor) != null) { 288 // It's a type parameter: see if we should map the type back to the concrete 289 // type in this class 290 val map = constructor?.containingClass()?.mapTypeVariables(it.containingClass()) 291 val cast = map?.get(type.toTypeString(context = it)) ?: typeString 292 writer.write(cast) 293 } else { 294 writer.write(typeString) 295 } 296 writer.write(")") 297 } 298 writer.write("null") 299 } else { 300 // Add cast for things like shorts and bytes 301 val typeString = type.toTypeString(context = it) 302 if (typeString != "boolean" && typeString != "int" && typeString != "long") { 303 writer.write("(") 304 writer.write(typeString) 305 writer.write(")") 306 } 307 writer.write(type.defaultValueString()) 308 } 309 } 310 writer.print("); ") 311 } 312 } 313 314 writeThrowStub() 315 } 316 317 private fun generateMissingConstructors(cls: ClassItem) { 318 val clsStubConstructor = cls.stubConstructor 319 val constructors = cls.filteredConstructors(filterEmit) 320 // If the default stub constructor is not publicly visible then it won't be output during the normal visiting 321 // so visit it specially to ensure that it is output. 322 if (clsStubConstructor != null && !constructors.contains(clsStubConstructor)) { 323 visitConstructor(clsStubConstructor) 324 return 325 } 326 } 327 328 override fun visitMethod(method: MethodItem) { 329 writeMethod(method.containingClass(), method, false) 330 } 331 332 private fun writeMethod(containingClass: ClassItem, method: MethodItem, movedFromInterface: Boolean) { 333 val modifiers = method.modifiers 334 val isEnum = containingClass.isEnum() 335 val isAnnotation = containingClass.isAnnotationType() 336 337 if (method.isEnumSyntheticMethod()) { 338 // Skip the values() and valueOf(String) methods in enums: these are added by 339 // the compiler for enums anyway, but was part of the doclava1 signature files 340 // so inserted in compat mode. 341 return 342 } 343 344 writer.println() 345 appendDocumentation(method, writer, docStubs) 346 347 // Need to filter out abstract from the modifiers list and turn it 348 // into a concrete method to make the stub compile 349 val removeAbstract = modifiers.isAbstract() && (isEnum || isAnnotation) || movedFromInterface 350 351 appendModifiers(method, modifiers, removeAbstract, movedFromInterface) 352 generateTypeParameterList(typeList = method.typeParameterList(), addSpace = true) 353 354 val returnType = method.returnType() 355 writer.print( 356 returnType.toTypeString( 357 outerAnnotations = false, 358 innerAnnotations = generateAnnotations, 359 filter = filterReference 360 ) 361 ) 362 363 writer.print(' ') 364 writer.print(method.name()) 365 generateParameterList(method) 366 generateThrowsList(method) 367 368 if (isAnnotation) { 369 val default = method.defaultValue() 370 if (default.isNotEmpty()) { 371 writer.print(" default ") 372 writer.print(default) 373 } 374 } 375 376 if (modifiers.isAbstract() && !removeAbstract && !isEnum || isAnnotation || modifiers.isNative()) { 377 writer.println(";") 378 } else { 379 writer.print(" { ") 380 writeThrowStub() 381 writer.println(" }") 382 } 383 } 384 385 override fun visitField(field: FieldItem) { 386 // Handled earlier in visitClass 387 if (field.isEnumConstant()) { 388 return 389 } 390 391 writer.println() 392 393 appendDocumentation(field, writer, docStubs) 394 appendModifiers(field, removeAbstract = false, removeFinal = false) 395 writer.print( 396 field.type().toTypeString( 397 outerAnnotations = false, 398 innerAnnotations = generateAnnotations, 399 filter = filterReference 400 ) 401 ) 402 writer.print(' ') 403 writer.print(field.name()) 404 val needsInitialization = 405 field.modifiers.isFinal() && field.initialValue(true) == null && field.containingClass().isClass() 406 field.writeValueWithSemicolon( 407 writer, 408 allowDefaultValue = !needsInitialization, 409 requireInitialValue = !needsInitialization 410 ) 411 writer.print("\n") 412 413 if (needsInitialization) { 414 if (field.modifiers.isStatic()) { 415 writer.print("static ") 416 } 417 writer.print("{ ${field.name()} = ${field.type().defaultValueString()}; }\n") 418 } 419 } 420 421 private fun writeThrowStub() { 422 writer.write("throw new RuntimeException(\"Stub!\");") 423 } 424 425 private fun generateParameterList(method: MethodItem) { 426 writer.print("(") 427 method.parameters().asSequence().forEachIndexed { i, parameter -> 428 if (i > 0) { 429 writer.print(", ") 430 } 431 appendModifiers(parameter, false) 432 writer.print( 433 parameter.type().toTypeString( 434 outerAnnotations = false, 435 innerAnnotations = generateAnnotations, 436 filter = filterReference 437 ) 438 ) 439 writer.print(' ') 440 val name = parameter.publicName() ?: parameter.name() 441 writer.print(name) 442 } 443 writer.print(")") 444 } 445 446 private fun generateThrowsList(method: MethodItem) { 447 // Note that throws types are already sorted internally to help comparison matching 448 val throws = if (preFiltered) { 449 method.throwsTypes().asSequence() 450 } else { 451 method.filteredThrowsTypes(filterReference).asSequence() 452 } 453 if (throws.any()) { 454 writer.print(" throws ") 455 throws.asSequence().sortedWith(ClassItem.fullNameComparator).forEachIndexed { i, type -> 456 if (i > 0) { 457 writer.print(", ") 458 } 459 // TODO: Shouldn't declare raw types here! 460 writer.print(type.qualifiedName()) 461 } 462 } 463 } 464 } 465