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