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