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