Home | History | Annotate | Download | only in model
      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