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 18 19 import com.android.tools.metalava.model.ClassItem 20 import com.android.tools.metalava.model.ConstructorItem 21 import com.android.tools.metalava.model.ExceptionTypeItem 22 import com.android.tools.metalava.model.FieldItem 23 import com.android.tools.metalava.model.Item 24 import com.android.tools.metalava.model.MethodItem 25 import com.android.tools.metalava.model.ModifierListWriter 26 import com.android.tools.metalava.model.PackageItem 27 import com.android.tools.metalava.model.PropertyItem 28 import com.android.tools.metalava.model.TypeItem 29 import com.android.tools.metalava.model.TypeParameterList 30 import com.android.tools.metalava.model.text.FileFormat 31 import com.android.tools.metalava.model.visitors.ApiVisitor 32 import java.io.PrintWriter 33 import java.util.BitSet 34 import java.util.function.Predicate 35 36 class SignatureWriter( 37 private val writer: PrintWriter, 38 filterEmit: Predicate<Item>, 39 filterReference: Predicate<Item>, 40 private val preFiltered: Boolean, 41 private var emitHeader: EmitFileHeader = EmitFileHeader.ALWAYS, 42 private val fileFormat: FileFormat, 43 showUnannotated: Boolean, 44 apiVisitorConfig: Config, 45 ) : 46 ApiVisitor( 47 visitConstructorsAsMethods = false, 48 nestInnerClasses = false, 49 inlineInheritedFields = true, 50 methodComparator = fileFormat.overloadedMethodOrder.comparator, 51 filterEmit = filterEmit, 52 filterReference = filterReference, 53 showUnannotated = showUnannotated, 54 config = apiVisitorConfig, 55 ) { 56 init { 57 // If a header must always be written out (even if the file is empty) then write it here. 58 if (emitHeader == EmitFileHeader.ALWAYS) { 59 writer.print(fileFormat.header()) 60 } 61 } 62 63 private val modifierListWriter = 64 ModifierListWriter.forSignature( 65 writer = writer, 66 skipNullnessAnnotations = fileFormat.kotlinStyleNulls, 67 ) 68 69 internal fun write(text: String) { 70 // If a header must only be written out when the file is not empty then write it here as 71 // this is not called 72 if (emitHeader == EmitFileHeader.IF_NONEMPTY_FILE) { 73 writer.print(fileFormat.header()) 74 // Remember that the header was written out, so it will not be written again. 75 emitHeader = EmitFileHeader.NEVER 76 } 77 writer.print(text) 78 } 79 80 override fun visitPackage(pkg: PackageItem) { 81 write("package ") 82 writeModifiers(pkg) 83 write("${pkg.qualifiedName()} {\n\n") 84 } 85 86 override fun afterVisitPackage(pkg: PackageItem) { 87 write("}\n\n") 88 } 89 90 override fun visitConstructor(constructor: ConstructorItem) { 91 fun writeConstructor(skipMask: BitSet? = null) { 92 write(" ctor ") 93 writeModifiers(constructor) 94 writeTypeParameterList(constructor.typeParameterList, addSpace = true) 95 write(constructor.containingClass().fullName()) 96 writeParameterList(constructor, skipMask) 97 writeThrowsList(constructor) 98 write(";\n") 99 } 100 101 // Workaround for https://youtrack.jetbrains.com/issue/KT-57537 102 if (constructor.shouldExpandOverloads()) { 103 val parameters = constructor.parameters() 104 val defaultMask = BitSet(parameters.size) 105 106 // fill the bitmask for all parameters 107 parameters.forEachIndexed { i, item -> defaultMask.set(i, item.hasDefaultValue()) } 108 109 // expand overloads ordered by number of parameters, skipping last parameters first 110 for (i in parameters.indices) { 111 if (!defaultMask.get(i)) continue 112 writeConstructor(defaultMask) 113 defaultMask.clear(i) 114 } 115 } 116 117 writeConstructor() 118 } 119 120 override fun visitField(field: FieldItem) { 121 val name = if (field.isEnumConstant()) "enum_constant" else "field" 122 write(" ") 123 write(name) 124 write(" ") 125 writeModifiers(field) 126 127 if (fileFormat.kotlinNameTypeOrder) { 128 // Kotlin style: write the name of the field, then the type. 129 write(field.name()) 130 write(": ") 131 writeType(field.type()) 132 } else { 133 // Java style: write the type, then the name of the field. 134 writeType(field.type()) 135 write(" ") 136 write(field.name()) 137 } 138 139 field.writeValueWithSemicolon( 140 writer, 141 allowDefaultValue = false, 142 requireInitialValue = false 143 ) 144 write("\n") 145 } 146 147 override fun visitProperty(property: PropertyItem) { 148 write(" property ") 149 writeModifiers(property) 150 if (fileFormat.kotlinNameTypeOrder) { 151 // Kotlin style: write the name of the property, then the type. 152 write(property.name()) 153 write(": ") 154 writeType(property.type()) 155 } else { 156 // Java style: write the type, then the name of the property. 157 writeType(property.type()) 158 write(" ") 159 write(property.name()) 160 } 161 write(";\n") 162 } 163 164 override fun visitMethod(method: MethodItem) { 165 write(" method ") 166 writeModifiers(method) 167 writeTypeParameterList(method.typeParameterList, addSpace = true) 168 169 if (fileFormat.kotlinNameTypeOrder) { 170 // Kotlin style: write the name of the method and the parameters, then the type. 171 write(method.name()) 172 writeParameterList(method) 173 write(": ") 174 writeType(method.returnType()) 175 } else { 176 // Java style: write the type, then the name of the method and the parameters. 177 writeType(method.returnType()) 178 write(" ") 179 write(method.name()) 180 writeParameterList(method) 181 } 182 183 writeThrowsList(method) 184 185 if (method.containingClass().isAnnotationType()) { 186 val default = method.defaultValue() 187 if (default.isNotEmpty()) { 188 write(" default ") 189 write(default) 190 } 191 } 192 193 write(";\n") 194 } 195 196 override fun visitClass(cls: ClassItem) { 197 write(" ") 198 199 writeModifiers(cls) 200 201 if (cls.isAnnotationType()) { 202 write("@interface") 203 } else if (cls.isInterface()) { 204 write("interface") 205 } else if (cls.isEnum()) { 206 write("enum") 207 } else { 208 write("class") 209 } 210 write(" ") 211 write(cls.fullName()) 212 writeTypeParameterList(cls.typeParameterList, addSpace = false) 213 writeSuperClassStatement(cls) 214 writeInterfaceList(cls) 215 216 write(" {\n") 217 } 218 219 override fun afterVisitClass(cls: ClassItem) { 220 write(" }\n\n") 221 } 222 223 private fun writeModifiers(item: Item) { 224 modifierListWriter.write(item.actualItem) 225 } 226 227 /** Get the filtered super class type, ignoring java.lang.Object. */ 228 private fun getFilteredSuperClassTypeFor(cls: ClassItem): TypeItem? { 229 val superClassItem = 230 if (preFiltered) cls.superClassType() else cls.filteredSuperClassType(filterReference) 231 return if (superClassItem == null || superClassItem.isJavaLangObject()) null 232 else superClassItem 233 } 234 235 private fun writeSuperClassStatement(cls: ClassItem) { 236 if (cls.isEnum() || cls.isAnnotationType() || cls.isInterface()) { 237 return 238 } 239 240 getFilteredSuperClassTypeFor(cls)?.let { superClassType -> 241 write(" extends") 242 writeExtendsOrImplementsType(superClassType) 243 } 244 } 245 246 private fun writeExtendsOrImplementsType(typeItem: TypeItem) { 247 val superClassString = 248 typeItem.toTypeString( 249 annotations = fileFormat.includeTypeUseAnnotations, 250 kotlinStyleNulls = fileFormat.kotlinStyleNulls, 251 filter = filterReference 252 ) 253 write(" ") 254 write(superClassString) 255 } 256 257 private fun writeInterfaceList(cls: ClassItem) { 258 if (cls.isAnnotationType()) { 259 return 260 } 261 val isInterface = cls.isInterface() 262 263 val unfilteredInterfaceTypes = cls.interfaceTypes() 264 val interfaces = 265 if (preFiltered) unfilteredInterfaceTypes 266 else cls.filteredInterfaceTypes(filterReference) 267 if (interfaces.isEmpty()) { 268 return 269 } 270 271 // Sort before prepending the super class (if this is an interface) as the super class 272 // always comes first because it was previously written out by writeSuperClassStatement. 273 @Suppress("DEPRECATION") 274 val comparator = 275 if (fileFormat.sortWholeExtendsList) TypeItem.totalComparator 276 else TypeItem.partialComparator 277 val sortedInterfaces = interfaces.sortedWith(comparator) 278 279 // Combine the super class and interfaces into a full list of them. 280 val fullInterfaces = 281 if (isInterface && !fileFormat.sortWholeExtendsList) { 282 // Previously, when the first interface in the extends list was stored in 283 // superClass, if that interface was visible in the signature then it would always 284 // be first even though the other interfaces are sorted in alphabetical order. This 285 // implements similar logic. 286 val firstUnfilteredInterfaceType = unfilteredInterfaceTypes.first() 287 val firstFilteredInterfaceType = interfaces.first() 288 if (firstFilteredInterfaceType == firstUnfilteredInterfaceType) { 289 buildList { 290 // The first interface in the interfaces list is also the first interface in 291 // the filtered interfaces list so add it first. 292 add(firstFilteredInterfaceType) 293 294 // Add the remaining interfaces in sorted order. 295 if (sortedInterfaces.size > 1) { 296 for (interfaceType in sortedInterfaces) { 297 if (interfaceType != firstFilteredInterfaceType) { 298 add(interfaceType) 299 } 300 } 301 } 302 } 303 } else { 304 sortedInterfaces 305 } 306 } else sortedInterfaces 307 308 val label = if (isInterface) " extends" else " implements" 309 write(label) 310 311 fullInterfaces.forEach { typeItem -> writeExtendsOrImplementsType(typeItem) } 312 } 313 314 private fun writeTypeParameterList(typeList: TypeParameterList, addSpace: Boolean) { 315 val typeListString = typeList.toString() 316 if (typeListString.isNotEmpty()) { 317 write(typeListString) 318 if (addSpace) { 319 write(" ") 320 } 321 } 322 } 323 324 private fun writeParameterList(method: MethodItem, skipMask: BitSet? = null) { 325 write("(") 326 var writtenParams = 0 327 method.parameters().asSequence().forEachIndexed { i, parameter -> 328 // skip over defaults when generating @JvmOverloads permutations 329 if (skipMask != null && skipMask.get(i)) return@forEachIndexed 330 331 if (writtenParams > 0) { 332 write(", ") 333 } 334 if (parameter.hasDefaultValue() && fileFormat.conciseDefaultValues) { 335 // Concise representation of a parameter with a default 336 write("optional ") 337 } 338 writeModifiers(parameter) 339 340 if (fileFormat.kotlinNameTypeOrder) { 341 // Kotlin style: the parameter must have a name (use `_` if it doesn't have a public 342 // name). Write the name and then the type. 343 val name = parameter.publicName() ?: "_" 344 write(name) 345 write(": ") 346 writeType(parameter.type()) 347 } else { 348 // Java style: write the type, then the name if it has a public name. 349 writeType(parameter.type()) 350 val name = parameter.publicName() 351 if (name != null) { 352 write(" ") 353 write(name) 354 } 355 } 356 357 if (parameter.isDefaultValueKnown() && !fileFormat.conciseDefaultValues) { 358 write(" = ") 359 val defaultValue = parameter.defaultValue() 360 if (defaultValue != null) { 361 write(defaultValue) 362 } else { 363 // null is a valid default value! 364 write("null") 365 } 366 } 367 writtenParams++ 368 } 369 write(")") 370 } 371 372 private fun writeType(type: TypeItem?) { 373 type ?: return 374 375 var typeString = 376 type.toTypeString( 377 annotations = fileFormat.includeTypeUseAnnotations, 378 kotlinStyleNulls = fileFormat.kotlinStyleNulls, 379 filter = filterReference 380 ) 381 382 // Strip java.lang. prefix 383 typeString = TypeItem.shortenTypes(typeString) 384 385 write(typeString) 386 } 387 388 private fun writeThrowsList(method: MethodItem) { 389 val throws = 390 when { 391 preFiltered -> method.throwsTypes().asSequence() 392 else -> method.filteredThrowsTypes(filterReference).asSequence() 393 } 394 if (throws.any()) { 395 write(" throws ") 396 throws.asSequence().sortedWith(ExceptionTypeItem.fullNameComparator).forEachIndexed { 397 i, 398 type -> 399 if (i > 0) { 400 write(", ") 401 } 402 write(type.toTypeString()) 403 } 404 } 405 } 406 } 407 408 enum class EmitFileHeader { 409 ALWAYS, 410 NEVER, 411 IF_NONEMPTY_FILE 412 } 413