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.model 18 19 import com.android.tools.metalava.DocLevel 20 import com.android.tools.metalava.DocLevel.HIDDEN 21 import com.android.tools.metalava.DocLevel.PACKAGE 22 import com.android.tools.metalava.DocLevel.PRIVATE 23 import com.android.tools.metalava.DocLevel.PROTECTED 24 import com.android.tools.metalava.DocLevel.PUBLIC 25 import com.android.tools.metalava.Options 26 import com.android.tools.metalava.compatibility 27 import com.android.tools.metalava.options 28 import java.io.Writer 29 30 interface ModifierList { 31 val codebase: Codebase 32 fun annotations(): List<AnnotationItem> 33 34 fun owner(): Item 35 fun getVisibilityLevel(): VisibilityLevel 36 fun isPublic(): Boolean 37 fun isProtected(): Boolean 38 fun isPrivate(): Boolean 39 fun isStatic(): Boolean 40 fun isAbstract(): Boolean 41 fun isFinal(): Boolean 42 fun isNative(): Boolean 43 fun isSynchronized(): Boolean 44 fun isStrictFp(): Boolean 45 fun isTransient(): Boolean 46 fun isVolatile(): Boolean 47 fun isDefault(): Boolean 48 49 // Modifier in Kotlin, separate syntax (...) in Java but modeled as modifier here 50 fun isVarArg(): Boolean = false 51 52 // Kotlin 53 fun isSealed(): Boolean = false 54 fun isCompanion(): Boolean = false 55 fun isInfix(): Boolean = false 56 fun isConst(): Boolean = false 57 fun isSuspend(): Boolean = false 58 fun isOperator(): Boolean = false 59 fun isInline(): Boolean = false 60 fun isEmpty(): Boolean 61 62 fun isPackagePrivate() = !(isPublic() || isProtected() || isPrivate()) 63 fun isPublicOrProtected() = isPublic() || isProtected() 64 65 // Rename? It's not a full equality, it's whether an override's modifier set is significant 66 fun equivalentTo(other: ModifierList): Boolean { 67 if (isPublic() != other.isPublic()) return false 68 if (isProtected() != other.isProtected()) return false 69 if (isPrivate() != other.isPrivate()) return false 70 71 if (isStatic() != other.isStatic()) return false 72 if (isAbstract() != other.isAbstract()) return false 73 if (isFinal() != other.isFinal()) { return false } 74 if (compatibility.includeSynchronized && isSynchronized() != other.isSynchronized()) return false 75 if (isTransient() != other.isTransient()) return false 76 if (isVolatile() != other.isVolatile()) return false 77 78 // Default does not require an override to "remove" it 79 // if (isDefault() != other.isDefault()) return false 80 81 return true 82 } 83 84 /** Returns true if this modifier list contains any nullness information */ 85 fun hasNullnessInfo(): Boolean { 86 return annotations().any { it.isNonNull() || it.isNullable() } 87 } 88 89 /** Returns true if this modifier list contains any a Nullable annotation */ 90 fun isNullable(): Boolean { 91 return annotations().any { it.isNullable() } 92 } 93 94 /** 95 * Returns true if this modifier list contains any annotations explicitly passed in 96 * via [Options.showAnnotations] 97 */ 98 fun hasShowAnnotation(): Boolean { 99 if (options.showAnnotations.isEmpty()) { 100 return false 101 } 102 return annotations().any { 103 options.showAnnotations.matches(it) 104 } 105 } 106 107 /** 108 * Returns true if this modifier list contains any annotations explicitly passed in 109 * via [Options.showSingleAnnotations] 110 */ 111 fun hasShowSingleAnnotation(): Boolean { 112 113 if (options.showSingleAnnotations.isEmpty()) { 114 return false 115 } 116 return annotations().any { 117 options.showSingleAnnotations.matches(it) 118 } 119 } 120 121 /** 122 * Returns true if this modifier list contains any annotations explicitly passed in 123 * via [Options.hideAnnotations] or any annotations which are themselves annotated 124 * with meta-annotations explicitly passed in via [Options.hideMetaAnnotations] 125 * 126 * @see hasHideMetaAnnotations 127 */ 128 fun hasHideAnnotations(): Boolean { 129 if (options.hideAnnotations.isEmpty() && options.hideMetaAnnotations.isEmpty()) { 130 return false 131 } 132 return annotations().any { annotation -> 133 options.hideAnnotations.matches(annotation) || 134 annotation.resolve()?.hasHideMetaAnnotation() ?: false 135 } 136 } 137 138 /** 139 * Returns true if this modifier list contains any meta-annotations explicitly passed in 140 * via [Options.hideMetaAnnotations]. 141 * 142 * Hidden meta-annotations allow Metalava to handle concepts like Kotlin's [Experimental], 143 * which allows developers to create annotations that describe experimental features -- sets 144 * of distinct and potentially overlapping unstable API surfaces. Libraries may wish to exclude 145 * such sets of APIs from tracking and stub JAR generation by passing [Experimental] as a 146 * hidden meta-annotation. 147 */ 148 fun hasHideMetaAnnotations(): Boolean { 149 if (options.hideMetaAnnotations.isEmpty()) { 150 return false 151 } 152 return annotations().any { annotation -> 153 options.hideMetaAnnotations.contains(annotation.qualifiedName()) 154 } 155 } 156 157 /** Returns true if this modifier list contains the given annotation */ 158 fun isAnnotatedWith(qualifiedName: String): Boolean { 159 return findAnnotation(qualifiedName) != null 160 } 161 162 /** Returns the annotation of the given qualified name if found in this modifier list */ 163 fun findAnnotation(qualifiedName: String): AnnotationItem? { 164 val mappedName = AnnotationItem.mapName(codebase, qualifiedName) 165 return annotations().firstOrNull { 166 mappedName == it.qualifiedName() 167 } 168 } 169 170 /** Returns true if this modifier list has adequate access */ 171 fun checkLevel() = checkLevel(options.docLevel) 172 173 /** 174 * Returns true if this modifier list has access modifiers that 175 * are adequate for the given documentation level 176 */ 177 fun checkLevel(level: DocLevel): Boolean { 178 if (level == HIDDEN) { 179 return true 180 } else if (owner().isHiddenOrRemoved()) { 181 return false 182 } 183 return when (level) { 184 PUBLIC -> isPublic() 185 PROTECTED -> isPublic() || isProtected() 186 PACKAGE -> !isPrivate() 187 PRIVATE, HIDDEN -> true 188 } 189 } 190 191 /** 192 * Returns true if the visibility modifiers in this modifier list is as least as visible 193 * as the ones in the given [other] modifier list 194 */ 195 fun asAccessibleAs(other: ModifierList): Boolean { 196 val otherLevel = other.getVisibilityLevel() 197 val thisLevel = getVisibilityLevel() 198 // Generally the access level enum order determines relative visibility. However, there is an exception because 199 // package private and internal are not directly comparable. 200 val result = thisLevel >= otherLevel 201 return when (otherLevel) { 202 VisibilityLevel.PACKAGE_PRIVATE -> result && thisLevel != VisibilityLevel.INTERNAL 203 VisibilityLevel.INTERNAL -> result && thisLevel != VisibilityLevel.PACKAGE_PRIVATE 204 else -> result 205 } 206 } 207 208 /** User visible description of the visibility in this modifier list */ 209 fun getVisibilityString(): String { 210 return getVisibilityLevel().userVisibleDescription 211 } 212 213 /** 214 * Like [getVisibilityString], but package private has no modifiers; this typically corresponds to 215 * the source code for the visibility modifiers in the modifier list 216 */ 217 fun getVisibilityModifiers(): String { 218 return getVisibilityLevel().sourceCodeModifier 219 } 220 221 companion object { 222 fun write( 223 writer: Writer, 224 modifiers: ModifierList, 225 item: Item, 226 target: AnnotationTarget, 227 // TODO: "deprecated" isn't a modifier; clarify method name 228 includeDeprecated: Boolean = false, 229 includeAnnotations: Boolean = true, 230 runtimeAnnotationsOnly: Boolean = false, 231 skipNullnessAnnotations: Boolean = false, 232 omitCommonPackages: Boolean = false, 233 removeAbstract: Boolean = false, 234 removeFinal: Boolean = false, 235 addPublic: Boolean = false, 236 separateLines: Boolean = false 237 ) { 238 239 val list = if (removeAbstract || removeFinal || addPublic) { 240 class AbstractFiltering : ModifierList by modifiers { 241 override fun isAbstract(): Boolean { 242 return if (removeAbstract) false else modifiers.isAbstract() 243 } 244 245 override fun isFinal(): Boolean { 246 return if (removeFinal) false else modifiers.isFinal() 247 } 248 249 override fun getVisibilityLevel(): VisibilityLevel { 250 return if (addPublic) VisibilityLevel.PUBLIC else modifiers.getVisibilityLevel() 251 } 252 } 253 AbstractFiltering() 254 } else { 255 modifiers 256 } 257 258 if (includeAnnotations) { 259 writeAnnotations( 260 item, 261 target, 262 runtimeAnnotationsOnly, 263 includeDeprecated, 264 writer, 265 separateLines, 266 list, 267 skipNullnessAnnotations, 268 omitCommonPackages 269 ) 270 } else { 271 // We always include @Deprecated annotation in stub files 272 if (item.deprecated && target.isStubsFile()) { 273 writer.write("@Deprecated") 274 writer.write(if (separateLines) "\n" else " ") 275 } 276 } 277 278 if (item is PackageItem) { 279 // Packages use a modifier list, but only annotations apply 280 return 281 } 282 283 // Kotlin order: 284 // https://kotlinlang.org/docs/reference/coding-conventions.html#modifiers 285 286 // Abstract: should appear in interfaces if in compat mode 287 val classItem = item as? ClassItem 288 val methodItem = item as? MethodItem 289 290 // Order based on the old stubs code: TODO, use Java standard order instead? 291 292 if (compatibility.nonstandardModifierOrder) { 293 val visibilityLevel = list.getVisibilityLevel() 294 if (visibilityLevel != VisibilityLevel.PACKAGE_PRIVATE) { 295 writer.write(visibilityLevel.sourceCodeModifier + " ") 296 } 297 298 if (list.isDefault()) { 299 writer.write("default ") 300 } 301 302 if (list.isStatic() && (compatibility.staticEnums || classItem == null || !classItem.isEnum())) { 303 writer.write("static ") 304 } 305 306 if (list.isFinal() && 307 // Don't show final on parameters: that's an implementation side detail 308 item !is ParameterItem && 309 (classItem?.isEnum() != true || compatibility.finalInInterfaces) || 310 compatibility.forceFinalInEnumValueMethods && 311 methodItem?.name() == "values" && methodItem.containingClass().isEnum() 312 ) { 313 writer.write("final ") 314 } 315 316 if (list.isSealed()) { 317 writer.write("sealed ") 318 } 319 320 if (list.isSuspend()) { 321 writer.write("suspend ") 322 } 323 324 if (list.isInline()) { 325 writer.write("inline ") 326 } 327 328 if (list.isInfix()) { 329 writer.write("infix ") 330 } 331 332 if (list.isOperator()) { 333 writer.write("operator ") 334 } 335 336 val isInterface = classItem?.isInterface() == true || 337 (methodItem?.containingClass()?.isInterface() == true && 338 !list.isDefault() && !list.isStatic()) 339 340 if ((compatibility.abstractInInterfaces && isInterface || 341 list.isAbstract() && 342 (classItem?.isEnum() != true && 343 (compatibility.abstractInAnnotations || classItem?.isAnnotationType() != true))) && 344 (!isInterface || compatibility.abstractInInterfaces) 345 ) { 346 writer.write("abstract ") 347 } 348 349 if (list.isNative() && target.isStubsFile()) { 350 writer.write("native ") 351 } 352 353 if (item.deprecated && includeDeprecated && !target.isStubsFile() && !compatibility.deprecatedAsAnnotation) { 354 writer.write("deprecated ") 355 } 356 357 if (list.isSynchronized() && (compatibility.includeSynchronized || target.isStubsFile())) { 358 writer.write("synchronized ") 359 } 360 361 if (list.isTransient()) { 362 writer.write("transient ") 363 } 364 365 if (list.isVolatile()) { 366 writer.write("volatile ") 367 } 368 } else { 369 if (item.deprecated && includeDeprecated && !target.isStubsFile() && !compatibility.deprecatedAsAnnotation) { 370 writer.write("deprecated ") 371 } 372 373 val visibilityLevel = list.getVisibilityLevel() 374 if (visibilityLevel != VisibilityLevel.PACKAGE_PRIVATE) { 375 writer.write(visibilityLevel.sourceCodeModifier + " ") 376 } 377 378 val isInterface = classItem?.isInterface() == true || 379 (methodItem?.containingClass()?.isInterface() == true && 380 !list.isDefault() && !list.isStatic()) 381 382 if ((compatibility.abstractInInterfaces && isInterface || 383 list.isAbstract() && 384 (classItem?.isEnum() != true && 385 (compatibility.abstractInAnnotations || classItem?.isAnnotationType() != true))) && 386 (!isInterface || compatibility.abstractInInterfaces) 387 ) { 388 writer.write("abstract ") 389 } 390 391 if (list.isDefault() && item !is ParameterItem) { 392 writer.write("default ") 393 } 394 395 if (list.isStatic() && (compatibility.staticEnums || classItem == null || !classItem.isEnum())) { 396 writer.write("static ") 397 } 398 399 if (list.isFinal() && 400 // Don't show final on parameters: that's an implementation side detail 401 item !is ParameterItem && 402 (classItem?.isEnum() != true || compatibility.finalInInterfaces) 403 ) { 404 writer.write("final ") 405 } 406 407 if (list.isSealed()) { 408 writer.write("sealed ") 409 } 410 411 if (list.isSuspend()) { 412 writer.write("suspend ") 413 } 414 415 if (list.isInline()) { 416 writer.write("inline ") 417 } 418 419 if (list.isInfix()) { 420 writer.write("infix ") 421 } 422 423 if (list.isOperator()) { 424 writer.write("operator ") 425 } 426 427 if (list.isTransient()) { 428 writer.write("transient ") 429 } 430 431 if (list.isVolatile()) { 432 writer.write("volatile ") 433 } 434 435 if (list.isSynchronized() && (compatibility.includeSynchronized || target.isStubsFile())) { 436 writer.write("synchronized ") 437 } 438 439 if (list.isNative() && target.isStubsFile()) { 440 writer.write("native ") 441 } 442 } 443 } 444 445 fun writeAnnotations( 446 item: Item, 447 target: AnnotationTarget, 448 runtimeAnnotationsOnly: Boolean, 449 includeDeprecated: Boolean, 450 writer: Writer, 451 separateLines: Boolean, 452 list: ModifierList, 453 skipNullnessAnnotations: Boolean, 454 omitCommonPackages: Boolean 455 ) { 456 // if includeDeprecated we want to do it 457 // unless runtimeOnly is false, in which case we'd include it too 458 // e.g. emit @Deprecated if includeDeprecated && !runtimeOnly 459 if (item.deprecated && 460 (compatibility.deprecatedAsAnnotation || target.isStubsFile()) && 461 (runtimeAnnotationsOnly || includeDeprecated) 462 ) { 463 writer.write("@Deprecated") 464 writer.write(if (separateLines) "\n" else " ") 465 } 466 467 writeAnnotations( 468 list = list, 469 runtimeAnnotationsOnly = runtimeAnnotationsOnly, 470 skipNullnessAnnotations = skipNullnessAnnotations, 471 omitCommonPackages = omitCommonPackages, 472 separateLines = separateLines, 473 writer = writer, 474 target = target 475 ) 476 } 477 478 fun writeAnnotations( 479 list: ModifierList, 480 skipNullnessAnnotations: Boolean = false, 481 runtimeAnnotationsOnly: Boolean = false, 482 omitCommonPackages: Boolean = false, 483 separateLines: Boolean = false, 484 filterDuplicates: Boolean = false, 485 writer: Writer, 486 target: AnnotationTarget 487 ) { 488 var annotations = list.annotations() 489 490 // Ensure stable signature file order 491 if (annotations.size > 1) { 492 annotations = annotations.sortedBy { it.qualifiedName() } 493 } 494 495 if (annotations.isNotEmpty()) { 496 var index = -1 497 for (annotation in annotations) { 498 index++ 499 500 if (runtimeAnnotationsOnly && annotation.retention != AnnotationRetention.RUNTIME) { 501 continue 502 } 503 504 var printAnnotation = annotation 505 if (!annotation.targets().contains(target)) { 506 continue 507 } else if ((annotation.isNullnessAnnotation())) { 508 if (skipNullnessAnnotations) { 509 continue 510 } 511 } else if (annotation.qualifiedName() == "java.lang.Deprecated") { 512 // Special cased in stubs and signature files: emitted first 513 continue 514 } else if (options.typedefMode == Options.TypedefMode.INLINE) { 515 val typedef = annotation.findTypedefAnnotation() 516 if (typedef != null) { 517 printAnnotation = typedef 518 } 519 } else if (options.typedefMode == Options.TypedefMode.REFERENCE && 520 annotation.targets() === ANNOTATION_SIGNATURE_ONLY && 521 annotation.findTypedefAnnotation() != null) { 522 // For annotation references, only include the simple name 523 writer.write("@") 524 writer.write(annotation.resolve()?.simpleName() ?: annotation.qualifiedName()!!) 525 if (separateLines) { 526 writer.write("\n") 527 } else { 528 writer.write(" ") 529 } 530 continue 531 } 532 533 // Optionally filter out duplicates 534 if (index > 0 && filterDuplicates) { 535 val qualifiedName = annotation.qualifiedName() 536 var found = false 537 for (i in 0 until index) { 538 val prev = annotations[i] 539 if (prev.qualifiedName() == qualifiedName) { 540 found = true 541 break 542 } 543 } 544 if (found) { 545 continue 546 } 547 } 548 549 val source = printAnnotation.toSource(target, showDefaultAttrs = false) 550 551 if (omitCommonPackages) { 552 writer.write(AnnotationItem.shortenAnnotation(source)) 553 } else { 554 writer.write(source) 555 } 556 if (separateLines) { 557 writer.write("\n") 558 } else { 559 writer.write(" ") 560 } 561 } 562 } 563 } 564 } 565 } 566