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.SdkConstants 20 import com.android.SdkConstants.ATTR_VALUE 21 import com.android.SdkConstants.INT_DEF_ANNOTATION 22 import com.android.SdkConstants.LONG_DEF_ANNOTATION 23 import com.android.SdkConstants.STRING_DEF_ANNOTATION 24 import com.android.tools.lint.annotations.Extractor.ANDROID_INT_DEF 25 import com.android.tools.lint.annotations.Extractor.ANDROID_LONG_DEF 26 import com.android.tools.lint.annotations.Extractor.ANDROID_STRING_DEF 27 import com.android.tools.metalava.ANDROIDX_ANNOTATION_PREFIX 28 import com.android.tools.metalava.ANDROID_SUPPORT_ANNOTATION_PREFIX 29 import com.android.tools.metalava.JAVA_LANG_PREFIX 30 import com.android.tools.metalava.Options 31 import com.android.tools.metalava.RECENTLY_NONNULL 32 import com.android.tools.metalava.RECENTLY_NULLABLE 33 import com.android.tools.metalava.options 34 import java.util.function.Predicate 35 36 fun isNullableAnnotation(qualifiedName: String): Boolean { 37 return qualifiedName.endsWith("Nullable") 38 } 39 40 fun isNonNullAnnotation(qualifiedName: String): Boolean { 41 return qualifiedName.endsWith("NonNull") || 42 qualifiedName.endsWith("NotNull") || 43 qualifiedName.endsWith("Nonnull") 44 } 45 46 interface AnnotationItem { 47 val codebase: Codebase 48 49 /** Fully qualified name of the annotation */ 50 fun qualifiedName(): String? 51 52 /** Generates source code for this annotation (using fully qualified names) */ 53 fun toSource(): String 54 55 /** Whether this annotation is significant and should be included in signature files, stubs, etc */ 56 fun isSignificant(): Boolean { 57 return includeInSignatures(qualifiedName() ?: return false) 58 } 59 60 /** Attributes of the annotation (may be empty) */ 61 fun attributes(): List<AnnotationAttribute> 62 63 /** True if this annotation represents @Nullable or @NonNull (or some synonymous annotation) */ 64 fun isNullnessAnnotation(): Boolean { 65 return isNullable() || isNonNull() 66 } 67 68 /** True if this annotation represents @Nullable (or some synonymous annotation) */ 69 fun isNullable(): Boolean { 70 return isNullableAnnotation(qualifiedName() ?: return false) 71 } 72 73 /** True if this annotation represents @NonNull (or some synonymous annotation) */ 74 fun isNonNull(): Boolean { 75 return isNonNullAnnotation(qualifiedName() ?: return false) 76 } 77 78 /** True if this annotation represents @IntDef, @LongDef or @StringDef */ 79 fun isTypeDefAnnotation(): Boolean { 80 val name = qualifiedName() ?: return false 81 return (INT_DEF_ANNOTATION.isEquals(name) || 82 STRING_DEF_ANNOTATION.isEquals(name) || 83 LONG_DEF_ANNOTATION.isEquals(name) || 84 ANDROID_INT_DEF == name || 85 ANDROID_STRING_DEF == name || 86 ANDROID_LONG_DEF == name) 87 } 88 89 /** 90 * True if this annotation represents a @ParameterName annotation (or some synonymous annotation). 91 * The parameter name should be the default attribute or "value". 92 */ 93 fun isParameterName(): Boolean { 94 return qualifiedName()?.endsWith(".ParameterName") ?: return false 95 } 96 97 /** 98 * True if this annotation represents a @DefaultValue annotation (or some synonymous annotation). 99 * The default value should be the default attribute or "value". 100 */ 101 fun isDefaultValue(): Boolean { 102 return qualifiedName()?.endsWith(".DefaultValue") ?: return false 103 } 104 105 /** Returns the given named attribute if specified */ 106 fun findAttribute(name: String?): AnnotationAttribute? { 107 val actualName = name ?: ATTR_VALUE 108 return attributes().firstOrNull { it.name == actualName } 109 } 110 111 /** Find the class declaration for the given annotation */ 112 fun resolve(): ClassItem? { 113 return codebase.findClass(qualifiedName() ?: return null) 114 } 115 116 companion object { 117 /** Whether the given annotation name is "significant", e.g. should be included in signature files */ 118 fun includeInSignatures(qualifiedName: String?): Boolean { 119 qualifiedName ?: return false 120 if (qualifiedName.startsWith(ANDROID_SUPPORT_ANNOTATION_PREFIX) || 121 qualifiedName.startsWith(ANDROIDX_ANNOTATION_PREFIX) 122 ) { 123 124 // Don't include typedefs in the stub files. 125 if (qualifiedName.endsWith("IntDef") || qualifiedName.endsWith("StringDef")) { 126 return false 127 } 128 129 return true 130 } 131 return false 132 } 133 134 /** The simple name of an annotation, which is the annotation name (not qualified name) prefixed by @ */ 135 fun simpleName(item: AnnotationItem): String { 136 val qualifiedName = item.qualifiedName() ?: return "" 137 return "@${qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1)}" 138 } 139 140 /** 141 * Maps an annotation name to the name to be used in signatures/stubs/external annotation files. 142 * Annotations that should not be exported are mapped to null. 143 */ 144 fun mapName(codebase: Codebase, qualifiedName: String?, filter: Predicate<Item>? = null): String? { 145 qualifiedName ?: return null 146 147 when (qualifiedName) { 148 // Resource annotations 149 "android.support.annotation.AnimRes", 150 "android.annotation.AnimRes" -> return "androidx.annotation.AnimRes" 151 "android.support.annotation.AnimatorRes", 152 "android.annotation.AnimatorRes" -> return "androidx.annotation.AnimatorRes" 153 "android.support.annotation.AnyRes", 154 "android.annotation.AnyRes" -> return "androidx.annotation.AnyRes" 155 "android.support.annotation.ArrayRes", 156 "android.annotation.ArrayRes" -> return "androidx.annotation.ArrayRes" 157 "android.support.annotation.AttrRes", 158 "android.annotation.AttrRes" -> return "androidx.annotation.AttrRes" 159 "android.support.annotation.BoolRes", 160 "android.annotation.BoolRes" -> return "androidx.annotation.BoolRes" 161 "android.support.annotation.ColorRes", 162 "android.annotation.ColorRes" -> return "androidx.annotation.ColorRes" 163 "android.support.annotation.DimenRes", 164 "android.annotation.DimenRes" -> return "androidx.annotation.DimenRes" 165 "android.support.annotation.DrawableRes", 166 "android.annotation.DrawableRes" -> return "androidx.annotation.DrawableRes" 167 "android.support.annotation.FontRes", 168 "android.annotation.FontRes" -> return "androidx.annotation.FontRes" 169 "android.support.annotation.FractionRes", 170 "android.annotation.FractionRes" -> return "androidx.annotation.FractionRes" 171 "android.support.annotation.IdRes", 172 "android.annotation.IdRes" -> return "androidx.annotation.IdRes" 173 "android.support.annotation.IntegerRes", 174 "android.annotation.IntegerRes" -> return "androidx.annotation.IntegerRes" 175 "android.support.annotation.InterpolatorRes", 176 "android.annotation.InterpolatorRes" -> return "androidx.annotation.InterpolatorRes" 177 "android.support.annotation.LayoutRes", 178 "android.annotation.LayoutRes" -> return "androidx.annotation.LayoutRes" 179 "android.support.annotation.MenuRes", 180 "android.annotation.MenuRes" -> return "androidx.annotation.MenuRes" 181 "android.support.annotation.PluralsRes", 182 "android.annotation.PluralsRes" -> return "androidx.annotation.PluralsRes" 183 "android.support.annotation.RawRes", 184 "android.annotation.RawRes" -> return "androidx.annotation.RawRes" 185 "android.support.annotation.StringRes", 186 "android.annotation.StringRes" -> return "androidx.annotation.StringRes" 187 "android.support.annotation.StyleRes", 188 "android.annotation.StyleRes" -> return "androidx.annotation.StyleRes" 189 "android.support.annotation.StyleableRes", 190 "android.annotation.StyleableRes" -> return "androidx.annotation.StyleableRes" 191 "android.support.annotation.TransitionRes", 192 "android.annotation.TransitionRes" -> return "androidx.annotation.TransitionRes" 193 "android.support.annotation.XmlRes", 194 "android.annotation.XmlRes" -> return "androidx.annotation.XmlRes" 195 196 // Threading 197 "android.support.annotation.AnyThread", 198 "android.annotation.AnyThread" -> return "androidx.annotation.AnyThread" 199 "android.support.annotation.BinderThread", 200 "android.annotation.BinderThread" -> return "androidx.annotation.BinderThread" 201 "android.support.annotation.MainThread", 202 "android.annotation.MainThread" -> return "androidx.annotation.MainThread" 203 "android.support.annotation.UiThread", 204 "android.annotation.UiThread" -> return "androidx.annotation.UiThread" 205 "android.support.annotation.WorkerThread", 206 "android.annotation.WorkerThread" -> return "androidx.annotation.WorkerThread" 207 208 // Colors 209 "android.support.annotation.ColorInt", 210 "android.annotation.ColorInt" -> return "androidx.annotation.ColorInt" 211 "android.support.annotation.ColorLong", 212 "android.annotation.ColorLong" -> return "androidx.annotation.ColorLong" 213 "android.support.annotation.HalfFloat", 214 "android.annotation.HalfFloat" -> return "androidx.annotation.HalfFloat" 215 216 // Ranges and sizes 217 "android.support.annotation.FloatRange", 218 "android.annotation.FloatRange" -> return "androidx.annotation.FloatRange" 219 "android.support.annotation.IntRange", 220 "android.annotation.IntRange" -> return "androidx.annotation.IntRange" 221 "android.support.annotation.Size", 222 "android.annotation.Size" -> return "androidx.annotation.Size" 223 "android.support.annotation.Px", 224 "android.annotation.Px" -> return "androidx.annotation.Px" 225 "android.support.annotation.Dimension", 226 "android.annotation.Dimension" -> return "androidx.annotation.Dimension" 227 228 // Null 229 "android.support.annotation.NonNull", 230 "android.annotation.NonNull" -> return "androidx.annotation.NonNull" 231 "android.support.annotation.Nullable", 232 "android.annotation.Nullable" -> return "androidx.annotation.Nullable" 233 "libcore.util.NonNull" -> return "androidx.annotation.NonNull" 234 "libcore.util.Nullable" -> return "androidx.annotation.Nullable" 235 "org.jetbrains.annotations.NotNull" -> return "androidx.annotation.NonNull" 236 "org.jetbrains.annotations.Nullable" -> return "androidx.annotation.Nullable" 237 238 // Typedefs 239 "android.support.annotation.IntDef", 240 "android.annotation.IntDef" -> return "androidx.annotation.IntDef" 241 "android.support.annotation.StringDef", 242 "android.annotation.StringDef" -> return "androidx.annotation.StringDef" 243 "android.support.annotation.LongDef", 244 "android.annotation.LongDef" -> return "androidx.annotation.LongDef" 245 246 // Misc 247 "android.support.annotation.CallSuper", 248 "android.annotation.CallSuper" -> return "androidx.annotation.CallSuper" 249 "android.support.annotation.CheckResult", 250 "android.annotation.CheckResult" -> return "androidx.annotation.CheckResult" 251 "android.support.annotation.RequiresPermission", 252 "android.annotation.RequiresPermission" -> return "androidx.annotation.RequiresPermission" 253 254 // These aren't support annotations, but could/should be: 255 "android.annotation.CurrentTimeMillisLong", 256 "android.annotation.DurationMillisLong", 257 "android.annotation.ElapsedRealtimeLong", 258 "android.annotation.UserIdInt", 259 "android.annotation.BytesLong", 260 261 // These aren't support annotations 262 "android.annotation.AppIdInt", 263 "android.annotation.SuppressAutoDoc", 264 "android.annotation.SystemApi", 265 "android.annotation.TestApi", 266 "android.annotation.CallbackExecutor", 267 "android.annotation.Condemned", 268 269 "android.annotation.Widget" -> { 270 // Remove, unless (a) public or (b) specifically included in --showAnnotations 271 return if (options.showAnnotations.contains(qualifiedName)) { 272 qualifiedName 273 } else if (filter != null) { 274 val cls = codebase.findClass(qualifiedName) 275 if (cls != null && filter.test(cls)) { 276 qualifiedName 277 } else { 278 null 279 } 280 } else { 281 qualifiedName 282 } 283 } 284 285 // Included for analysis, but should not be exported: 286 "android.annotation.BroadcastBehavior", 287 "android.annotation.SdkConstant", 288 "android.annotation.RequiresFeature", 289 "android.annotation.SystemService" -> return qualifiedName 290 291 // Should not be mapped to a different package name: 292 "android.annotation.TargetApi", 293 "android.annotation.SuppressLint" -> return qualifiedName 294 295 // We only change recently/newly nullable annotation if the codebase supports it 296 RECENTLY_NULLABLE -> return if (codebase.supportsStagedNullability) qualifiedName else "androidx.annotation.Nullable" 297 RECENTLY_NONNULL -> return if (codebase.supportsStagedNullability) qualifiedName else "androidx.annotation.NonNull" 298 299 else -> { 300 // Some new annotations added to the platform: assume they are support annotations? 301 return when { 302 // Special Kotlin annotations recognized by the compiler: map to supported package name 303 qualifiedName.endsWith(".ParameterName") || qualifiedName.endsWith(".DefaultValue") -> 304 "kotlin.annotations.jvm.internal${qualifiedName.substring(qualifiedName.lastIndexOf('.'))}" 305 306 // Other third party nullness annotations? 307 isNullableAnnotation(qualifiedName) -> "androidx.annotation.Nullable" 308 isNonNullAnnotation(qualifiedName) -> "androidx.annotation.NonNull" 309 310 // Support library annotations are all included, as is the built-in stuff like @Retention 311 qualifiedName.startsWith(ANDROIDX_ANNOTATION_PREFIX) -> return qualifiedName 312 qualifiedName.startsWith(JAVA_LANG_PREFIX) -> return qualifiedName 313 314 // Unknown Android platform annotations 315 qualifiedName.startsWith("android.annotation.") -> { 316 // Remove, unless specifically included in --showAnnotations 317 return if (options.showAnnotations.contains(qualifiedName)) { 318 qualifiedName 319 } else { 320 null 321 } 322 } 323 324 qualifiedName.startsWith(ANDROID_SUPPORT_ANNOTATION_PREFIX) -> { 325 return mapName( 326 codebase, 327 ANDROIDX_ANNOTATION_PREFIX + qualifiedName.substring(ANDROID_SUPPORT_ANNOTATION_PREFIX.length), 328 filter 329 ) 330 } 331 332 else -> { 333 // Remove, unless (a) public or (b) specifically included in --showAnnotations 334 return if (options.showAnnotations.contains(qualifiedName)) { 335 qualifiedName 336 } else if (filter != null) { 337 val cls = codebase.findClass(qualifiedName) 338 if (cls != null && filter.test(cls)) { 339 qualifiedName 340 } else { 341 null 342 } 343 } else { 344 qualifiedName 345 } 346 } 347 } 348 } 349 } 350 } 351 352 /** 353 * Given a "full" annotation name, shortens it by removing redundant package names. 354 * This is intended to be used by the [Options.omitCommonPackages] flag 355 * to reduce clutter in signature files. 356 * 357 * For example, this method will convert `@androidx.annotation.Nullable` to just 358 * `@Nullable`, and `@androidx.annotation.IntRange(from=20)` to `IntRange(from=20)`. 359 */ 360 fun shortenAnnotation(source: String): String { 361 return when { 362 source.startsWith("android.annotation.", 1) -> { 363 "@" + source.substring("@android.annotation.".length) 364 } 365 source.startsWith(ANDROID_SUPPORT_ANNOTATION_PREFIX, 1) -> { 366 "@" + source.substring("@android.support.annotation.".length) 367 } 368 source.startsWith(ANDROIDX_ANNOTATION_PREFIX, 1) -> { 369 "@" + source.substring("@androidx.annotation.".length) 370 } 371 else -> source 372 } 373 } 374 375 /** 376 * Reverses the [shortenAnnotation] method. Intended for use when reading in signature files 377 * that contain shortened type references. 378 */ 379 fun unshortenAnnotation(source: String): String { 380 return when { 381 // These 3 annotations are in the android.annotation. package, not android.support.annotation 382 source.startsWith("@SystemService") || 383 source.startsWith("@TargetApi") || 384 source.startsWith("@SuppressLint") -> 385 "@android.annotation." + source.substring(1) 386 else -> { 387 "@androidx.annotation." + source.substring(1) 388 } 389 } 390 } 391 } 392 } 393 394 /** An attribute of an annotation, such as "value" */ 395 interface AnnotationAttribute { 396 /** The name of the annotation */ 397 val name: String 398 /** The annotation value */ 399 val value: AnnotationAttributeValue 400 401 /** 402 * Return all leaf values; this flattens the complication of handling 403 * {@code @SuppressLint("warning")} and {@code @SuppressLint({"warning1","warning2"}) 404 */ 405 fun leafValues(): List<AnnotationAttributeValue> { 406 val result = mutableListOf<AnnotationAttributeValue>() 407 AnnotationAttributeValue.addValues(value, result) 408 return result 409 } 410 } 411 412 /** An annotation value */ 413 interface AnnotationAttributeValue { 414 /** Generates source code for this annotation value */ 415 fun toSource(): String 416 417 /** The value of the annotation */ 418 fun value(): Any? 419 420 /** If the annotation declaration references a field (or class etc), return the resolved class */ 421 fun resolve(): Item? 422 423 companion object { 424 fun addValues(value: AnnotationAttributeValue, into: MutableList<AnnotationAttributeValue>) { 425 if (value is AnnotationArrayAttributeValue) { 426 for (v in value.values) { 427 addValues(v, into) 428 } 429 } else if (value is AnnotationSingleAttributeValue) { 430 into.add(value) 431 } 432 } 433 } 434 } 435 436 /** An annotation value (for a single item, not an array) */ 437 interface AnnotationSingleAttributeValue : AnnotationAttributeValue { 438 /** The annotation value, expressed as source code */ 439 val valueSource: String 440 /** The annotation value */ 441 val value: Any? 442 443 override fun value() = value 444 } 445 446 /** An annotation value for an array of items */ 447 interface AnnotationArrayAttributeValue : AnnotationAttributeValue { 448 /** The annotation values */ 449 val values: List<AnnotationAttributeValue> 450 451 override fun resolve(): Item? { 452 error("resolve() should not be called on an array value") 453 } 454 455 override fun value() = values.mapNotNull { it.value() }.toTypedArray() 456 } 457 458 class DefaultAnnotationAttribute( 459 override val name: String, 460 override val value: DefaultAnnotationValue 461 ) : AnnotationAttribute { 462 companion object { 463 fun create(name: String, value: String): DefaultAnnotationAttribute { 464 return DefaultAnnotationAttribute(name, DefaultAnnotationValue.create(value)) 465 } 466 467 fun createList(source: String): List<AnnotationAttribute> { 468 val list = mutableListOf<AnnotationAttribute>() 469 if (source.contains("{")) { 470 assert( 471 source.indexOf('{', source.indexOf('{', source.indexOf('{') + 1) + 1) != -1 472 ) { "Multiple arrays not supported: $source" } 473 val split = source.indexOf('=') 474 val name: String 475 val value: String 476 if (split == -1) { 477 name = "value" 478 value = source.substring(source.indexOf('{')) 479 } else { 480 name = source.substring(0, split).trim() 481 value = source.substring(split + 1).trim() 482 } 483 list.add(DefaultAnnotationAttribute.create(name, value)) 484 return list 485 } 486 487 source.split(",").forEach { declaration -> 488 val split = declaration.indexOf('=') 489 val name: String 490 val value: String 491 if (split == -1) { 492 name = "value" 493 value = declaration.trim() 494 } else { 495 name = declaration.substring(0, split).trim() 496 value = declaration.substring(split + 1).trim() 497 } 498 list.add(DefaultAnnotationAttribute.create(name, value)) 499 } 500 return list 501 } 502 } 503 } 504 505 abstract class DefaultAnnotationValue : AnnotationAttributeValue { 506 companion object { 507 fun create(value: String): DefaultAnnotationValue { 508 return if (value.startsWith("{")) { // Array 509 DefaultAnnotationArrayAttributeValue(value) 510 } else { 511 DefaultAnnotationSingleAttributeValue(value) 512 } 513 } 514 } 515 516 override fun toString(): String = toSource() 517 } 518 519 class DefaultAnnotationSingleAttributeValue(override val valueSource: String) : DefaultAnnotationValue(), 520 AnnotationSingleAttributeValue { 521 @Suppress("IMPLICIT_CAST_TO_ANY") 522 override val value = when { 523 valueSource == SdkConstants.VALUE_TRUE -> true 524 valueSource == SdkConstants.VALUE_FALSE -> false 525 valueSource.startsWith("\"") -> valueSource.removeSurrounding("\"") 526 valueSource.startsWith('\'') -> valueSource.removeSurrounding("'")[0] 527 else -> try { 528 if (valueSource.contains(".")) { 529 valueSource.toDouble() 530 } else { 531 valueSource.toLong() 532 } 533 } catch (e: NumberFormatException) { 534 valueSource 535 } 536 } 537 538 override fun resolve(): Item? = null 539 540 override fun toSource() = valueSource 541 } 542 543 class DefaultAnnotationArrayAttributeValue(val value: String) : DefaultAnnotationValue(), 544 AnnotationArrayAttributeValue { 545 init { 546 assert(value.startsWith("{") && value.endsWith("}")) { value } 547 } 548 549 override val values = value.substring(1, value.length - 1).split(",").map { 550 DefaultAnnotationValue.create(it.trim()) 551 }.toList() 552 553 override fun toSource() = value 554 } 555