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