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.CallableItem 20 import com.android.tools.metalava.model.ClassItem 21 import com.android.tools.metalava.model.ClassTypeItem 22 import com.android.tools.metalava.model.ConstructorItem 23 import com.android.tools.metalava.model.DelegatedVisitor 24 import com.android.tools.metalava.model.ExceptionTypeItem 25 import com.android.tools.metalava.model.FieldItem 26 import com.android.tools.metalava.model.Item 27 import com.android.tools.metalava.model.JAVA_LANG_STRING 28 import com.android.tools.metalava.model.MethodItem 29 import com.android.tools.metalava.model.ModifierListWriter 30 import com.android.tools.metalava.model.PrimitiveTypeItem 31 import com.android.tools.metalava.model.TypeItem 32 import com.android.tools.metalava.model.TypeParameterBindings 33 import com.android.tools.metalava.model.TypeParameterList 34 import com.android.tools.metalava.model.VariableTypeItem 35 import java.io.PrintWriter 36 37 internal class JavaStubWriter( 38 private val writer: PrintWriter, 39 private val modifierListWriter: ModifierListWriter, 40 private val config: StubWriterConfig, 41 private val stubConstructorManager: StubConstructorManager, 42 ) : DelegatedVisitor { 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 (config.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.sourceFile()?.getImports()?.let { 56 for (item in it) { 57 if (item.isMember) { 58 writer.println("import static ${item.pattern};") 59 } else { 60 writer.println("import ${item.pattern};") 61 } 62 } 63 writer.println() 64 } 65 } 66 } 67 68 appendDocumentation(cls, writer, config) 69 70 // "ALL" doesn't do it; compiler still warns unless you actually explicitly list "unchecked" 71 writer.println("@SuppressWarnings({\"unchecked\", \"deprecation\", \"all\"})") 72 73 appendModifiers(cls) 74 75 when { 76 cls.isAnnotationType() -> writer.print("@interface") 77 cls.isInterface() -> writer.print("interface") 78 cls.isEnum() -> writer.print("enum") 79 else -> writer.print("class") 80 } 81 82 writer.print(" ") 83 writer.print(cls.simpleName()) 84 85 generateTypeParameterList(typeList = cls.typeParameterList, addSpace = false) 86 generateSuperClassDeclaration(cls) 87 generateInterfaceList(cls) 88 writer.print(" {\n") 89 90 // Enum constants must be written out first. 91 if (cls.isEnum()) { 92 var first = true 93 // While enum order is significant at runtime as it affects `Enum.ordinal` and its 94 // comparable order it is not significant in the stubs so sort alphabetically. That 95 // matches the order in the documentation and the signature files. It is theoretically 96 // possible for an annotation processor to care about the order but any that did would 97 // be poorly written and would break on stubs created from signature files. 98 val enumConstants = 99 cls.fields().filter { it.isEnumConstant() }.sortedWith(FieldItem.comparator) 100 for (enumConstant in enumConstants) { 101 if (first) { 102 first = false 103 } else { 104 writer.write(",\n") 105 } 106 appendDocumentation(enumConstant, writer, config) 107 108 // Append the modifier list even though the enum constant does not actually have 109 // modifiers as that will write the annotations which it does have and ignore 110 // the modifiers. 111 appendModifiers(enumConstant) 112 113 writer.write(enumConstant.name()) 114 } 115 writer.println(";") 116 } 117 } 118 119 override fun afterVisitClass(cls: ClassItem) { 120 writer.print("}\n\n") 121 } 122 123 private fun appendModifiers(item: Item) { 124 modifierListWriter.write(item) 125 } 126 127 private fun generateSuperClassDeclaration(cls: ClassItem) { 128 if (cls.isEnum() || cls.isAnnotationType() || cls.isInterface()) { 129 // No extends statement for enums and annotations; it's implied by the "enum" and 130 // "@interface" keywords. Normal interfaces do support an extends statement but it is 131 // generated in [generateInterfaceList]. 132 return 133 } 134 135 val superClass = cls.superClassType() 136 if (superClass != null && !superClass.isJavaLangObject()) { 137 writer.print(" extends ") 138 writer.print(superClass.toTypeString()) 139 } 140 } 141 142 private fun generateInterfaceList(cls: ClassItem) { 143 if (cls.isAnnotationType()) { 144 // No extends statement for annotations; it's implied by the "@interface" keyword 145 return 146 } 147 148 val interfaces = cls.interfaceTypes() 149 if (interfaces.isNotEmpty()) { 150 val label = if (cls.isInterface()) " extends" else " implements" 151 writer.print(label) 152 interfaces.sortedWith(TypeItem.totalComparator).forEachIndexed { index, type -> 153 if (index > 0) { 154 writer.print(",") 155 } 156 writer.print(" ") 157 writer.print(type.toTypeString()) 158 } 159 } 160 } 161 162 private fun generateTypeParameterList(typeList: TypeParameterList, addSpace: Boolean) { 163 val typeListString = typeList.toString() 164 if (typeListString.isNotEmpty()) { 165 writer.print(typeListString) 166 167 if (addSpace) { 168 writer.print(' ') 169 } 170 } 171 } 172 173 override fun visitConstructor(constructor: ConstructorItem) { 174 writer.println() 175 appendDocumentation(constructor, writer, config) 176 appendModifiers(constructor) 177 generateTypeParameterList(typeList = constructor.typeParameterList, addSpace = true) 178 writer.print(constructor.containingClass().simpleName()) 179 180 generateParameterList(constructor) 181 generateThrowsList(constructor) 182 183 writer.print(" { ") 184 185 writeConstructorBody(constructor) 186 writer.println(" }") 187 } 188 189 private fun writeConstructorBody(constructor: ConstructorItem) { 190 val optionalSuperConstructor = 191 stubConstructorManager.optionalSuperConstructor(constructor.containingClass()) 192 optionalSuperConstructor?.let { superConstructor -> 193 val parameters = superConstructor.parameters() 194 if (parameters.isNotEmpty()) { 195 writer.print("super(") 196 197 // Get the types to which this class binds the super class's type parameters, if 198 // any. 199 val typeParameterBindings = 200 constructor 201 .containingClass() 202 .mapTypeVariables(superConstructor.containingClass()) 203 204 for ((index, parameter) in parameters.withIndex()) { 205 if (index > 0) { 206 writer.write(", ") 207 } 208 // Always make sure to add appropriate casts to the parameters in the super call 209 // as without the casts the compiler will fail if there is more than one 210 // constructor that could match. 211 val defaultValueWithCast = 212 defaultValueWithCastForType(parameter.type(), typeParameterBindings) 213 writer.write(defaultValueWithCast) 214 } 215 writer.print("); ") 216 } 217 } 218 219 writeThrowStub() 220 } 221 222 /** 223 * Get the string representation of the default value for [type], it will include a cast if 224 * necessary. 225 * 226 * If [type] is a [VariableTypeItem] then it will map it to the appropriate type given the 227 * [typeParameterBindings]. See the comment in the body for more details. 228 */ 229 private fun defaultValueWithCastForType( 230 type: TypeItem, 231 typeParameterBindings: TypeParameterBindings, 232 ): String { 233 // Handle special cases and non-reference types, drop through to handle the default 234 // reference type. 235 when (type) { 236 is PrimitiveTypeItem -> { 237 val kind = type.kind 238 return when (kind) { 239 PrimitiveTypeItem.Primitive.BOOLEAN, 240 PrimitiveTypeItem.Primitive.INT, 241 PrimitiveTypeItem.Primitive.LONG -> kind.defaultValueString 242 else -> "(${kind.primitiveName})${kind.defaultValueString}" 243 } 244 } 245 is ClassTypeItem -> { 246 val qualifiedName = type.qualifiedName 247 when (qualifiedName) { 248 JAVA_LANG_STRING -> return "\"\"" 249 } 250 } 251 } 252 253 // Get the actual type that the super constructor expects, taking into account any type 254 // parameter mappings. 255 val mappedType = 256 if (type is VariableTypeItem) { 257 // The super constructor's parameter is a type variable: so see if it should be 258 // mapped back to a type specified by this class. e.g. 259 // 260 // Given: 261 // class Bar<T extends Number> { 262 // public Bar(int i) {} 263 // public Bar(T t) {} 264 // } 265 // class Foo extends Bar<Integer> { 266 // public Foo(Integer i) { super(i); } 267 // } 268 // 269 // The stub for Foo should use: 270 // super((Integer) i); 271 // Not: 272 // super((Number) i); 273 // 274 // However, if the super class is referenced as a raw type then there will be no 275 // mapping in which case fall back to the erased type which will use the type 276 // variable's lower bound. e.g. 277 // 278 // Given: 279 // class Foo extends Bar { 280 // public Foo(Integer i) { super(i); } 281 // } 282 // 283 // The stub for Foo should use: 284 // super((Number) i); 285 type.convertType(typeParameterBindings) 286 } else { 287 type 288 } 289 290 // Casting to the erased type could lead to unchecked warnings (which are suppressed) but 291 // avoids having to deal with parameterized types and ensures that casting to a vararg 292 // parameter uses an array type. 293 val erasedTypeString = mappedType.toErasedTypeString() 294 return "($erasedTypeString)null" 295 } 296 297 override fun visitMethod(method: MethodItem) { 298 writeMethod(method.containingClass(), method) 299 } 300 301 private fun writeMethod(containingClass: ClassItem, method: MethodItem) { 302 writer.println() 303 appendDocumentation(method, writer, config) 304 305 appendModifiers(method) 306 generateTypeParameterList(typeList = method.typeParameterList, addSpace = true) 307 308 val returnType = method.returnType() 309 writer.print(returnType.toTypeString()) 310 311 writer.print(' ') 312 writer.print(method.name()) 313 generateParameterList(method) 314 generateThrowsList(method) 315 316 if (containingClass.isAnnotationType()) { 317 val default = method.legacyDefaultValue() 318 if (default.isNotEmpty()) { 319 writer.print(" default ") 320 writer.print(default) 321 } 322 } 323 324 if (ModifierListWriter.requiresMethodBodyInStubs(method)) { 325 writer.print(" { ") 326 writeThrowStub() 327 writer.println(" }") 328 } else { 329 writer.println(";") 330 } 331 } 332 333 override fun visitField(field: FieldItem) { 334 // Handled earlier in visitClass 335 if (field.isEnumConstant()) { 336 return 337 } 338 339 writer.println() 340 341 appendDocumentation(field, writer, config) 342 appendModifiers(field) 343 writer.print(field.type().toTypeString()) 344 writer.print(' ') 345 writer.print(field.name()) 346 val needsInitialization = 347 field.modifiers.isFinal() && 348 field.legacyInitialValue(true) == null && 349 field.containingClass().isClass() 350 field.writeValueWithSemicolon( 351 writer, 352 allowDefaultValue = !needsInitialization, 353 requireInitialValue = !needsInitialization 354 ) 355 writer.print("\n") 356 357 if (needsInitialization) { 358 if (field.modifiers.isStatic()) { 359 writer.print("static ") 360 } 361 writer.print("{ ${field.name()} = ${field.type().defaultValueString()}; }\n") 362 } 363 } 364 365 private fun writeThrowStub() { 366 writer.write("throw new RuntimeException(\"Stub!\");") 367 } 368 369 private fun generateParameterList(callable: CallableItem) { 370 writer.print("(") 371 callable.parameters().asSequence().forEachIndexed { i, parameter -> 372 if (i > 0) { 373 writer.print(", ") 374 } 375 appendModifiers(parameter) 376 writer.print(parameter.type().toTypeString()) 377 writer.print(' ') 378 val name = parameter.publicName() ?: parameter.name() 379 writer.print(name) 380 } 381 writer.print(")") 382 } 383 384 private fun generateThrowsList(callable: CallableItem) { 385 val throws = callable.throwsTypes() 386 if (throws.isNotEmpty()) { 387 writer.print(" throws ") 388 throws.sortedWith(ExceptionTypeItem.fullNameComparator).forEachIndexed { i, type -> 389 if (i > 0) { 390 writer.print(", ") 391 } 392 writer.print(type.toTypeString()) 393 } 394 } 395 } 396 } 397