• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ANDROIDX_NONNULL
29 import com.android.tools.metalava.ANDROIDX_NULLABLE
30 import com.android.tools.metalava.ANDROID_NONNULL
31 import com.android.tools.metalava.ANDROID_NULLABLE
32 import com.android.tools.metalava.ANDROID_SUPPORT_ANNOTATION_PREFIX
33 import com.android.tools.metalava.Compatibility
34 import com.android.tools.metalava.JAVA_LANG_PREFIX
35 import com.android.tools.metalava.Options
36 import com.android.tools.metalava.RECENTLY_NONNULL
37 import com.android.tools.metalava.RECENTLY_NULLABLE
38 import com.android.tools.metalava.doclava1.ApiPredicate
39 import com.android.tools.metalava.model.psi.PsiBasedCodebase
40 import com.android.tools.metalava.options
41 import java.util.function.Predicate
42 
isNullableAnnotationnull43 fun isNullableAnnotation(qualifiedName: String): Boolean {
44     return qualifiedName.endsWith("Nullable")
45 }
46 
isNonNullAnnotationnull47 fun isNonNullAnnotation(qualifiedName: String): Boolean {
48     return qualifiedName.endsWith("NonNull") ||
49         qualifiedName.endsWith("NotNull") ||
50         qualifiedName.endsWith("Nonnull")
51 }
52 
53 interface AnnotationItem {
54     val codebase: Codebase
55 
56     /** Fully qualified name of the annotation */
qualifiedNamenull57     fun qualifiedName(): String?
58 
59     /** Fully qualified name of the annotation (prior to name mapping) */
60     fun originalName(): String?
61 
62     /** Generates source code for this annotation (using fully qualified names) */
63     fun toSource(
64         target: AnnotationTarget = AnnotationTarget.SIGNATURE_FILE,
65         showDefaultAttrs: Boolean = true
66     ): String
67 
68     /** The applicable targets for this annotation */
69     fun targets(): Set<AnnotationTarget>
70 
71     /** Attributes of the annotation (may be empty) */
72     fun attributes(): List<AnnotationAttribute>
73 
74     /** True if this annotation represents @Nullable or @NonNull (or some synonymous annotation) */
75     fun isNullnessAnnotation(): Boolean {
76         return isNullable() || isNonNull()
77     }
78 
79     /** True if this annotation represents @Nullable (or some synonymous annotation) */
isNullablenull80     fun isNullable(): Boolean {
81         return isNullableAnnotation(qualifiedName() ?: return false)
82     }
83 
84     /** True if this annotation represents @NonNull (or some synonymous annotation) */
isNonNullnull85     fun isNonNull(): Boolean {
86         return isNonNullAnnotation(qualifiedName() ?: return false)
87     }
88 
89     /** True if this annotation represents @IntDef, @LongDef or @StringDef */
isTypeDefAnnotationnull90     fun isTypeDefAnnotation(): Boolean {
91         val name = qualifiedName() ?: return false
92         if (!(name.endsWith("Def"))) {
93             return false
94         }
95         return (INT_DEF_ANNOTATION.isEquals(name) ||
96             STRING_DEF_ANNOTATION.isEquals(name) ||
97             LONG_DEF_ANNOTATION.isEquals(name) ||
98             ANDROID_INT_DEF == name ||
99             ANDROID_STRING_DEF == name ||
100             ANDROID_LONG_DEF == name)
101     }
102 
103     /**
104      * True if this annotation represents a @ParameterName annotation (or some synonymous annotation).
105      * The parameter name should be the default attribute or "value".
106      */
isParameterNamenull107     fun isParameterName(): Boolean {
108         return qualifiedName()?.endsWith(".ParameterName") ?: return false
109     }
110 
111     /**
112      * True if this annotation represents a @DefaultValue annotation (or some synonymous annotation).
113      * The default value should be the default attribute or "value".
114      */
isDefaultValuenull115     fun isDefaultValue(): Boolean {
116         return qualifiedName()?.endsWith(".DefaultValue") ?: return false
117     }
118 
119     /** Returns the given named attribute if specified */
findAttributenull120     fun findAttribute(name: String?): AnnotationAttribute? {
121         val actualName = name ?: ATTR_VALUE
122         return attributes().firstOrNull { it.name == actualName }
123     }
124 
125     /** Find the class declaration for the given annotation */
resolvenull126     fun resolve(): ClassItem? {
127         return codebase.findClass(qualifiedName() ?: return null)
128     }
129 
130     /** If this annotation has a typedef annotation associated with it, return it */
findTypedefAnnotationnull131     fun findTypedefAnnotation(): AnnotationItem? {
132         val className = originalName() ?: return null
133         return codebase.findClass(className)?.modifiers?.annotations()?.firstOrNull { it.isTypeDefAnnotation() }
134     }
135 
136     /** Returns the retention of this annotation */
137     val retention: AnnotationRetention
138         get() {
139             val name = qualifiedName()
140             if (name != null) {
141                 val cls = codebase.findClass(name) ?: (codebase as? PsiBasedCodebase)?.findOrCreateClass(name)
142                 if (cls != null) {
143                     if (cls.isAnnotationType()) {
144                         return cls.getRetention()
145                     }
146                 }
147             }
148 
149             return AnnotationRetention.CLASS
150         }
151 
152     companion object {
153         /** The simple name of an annotation, which is the annotation name (not qualified name) prefixed by @ */
simpleNamenull154         fun simpleName(item: AnnotationItem): String {
155             val qualifiedName = item.qualifiedName() ?: return ""
156             return "@${qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1)}"
157         }
158 
159         /**
160          * Maps an annotation name to the name to be used in signatures/stubs/external annotation files.
161          * Annotations that should not be exported are mapped to null.
162          */
mapNamenull163         fun mapName(
164             codebase: Codebase,
165             qualifiedName: String?,
166             filter: Predicate<Item>? = null,
167             target: AnnotationTarget = AnnotationTarget.SIGNATURE_FILE
168         ): String? {
169             qualifiedName ?: return null
170             if (options.passThroughAnnotations.contains(qualifiedName)) {
171                 return qualifiedName
172             }
173             when (qualifiedName) {
174                 // Resource annotations
175                 "android.support.annotation.AnimRes",
176                 "android.annotation.AnimRes" -> return "androidx.annotation.AnimRes"
177                 "android.support.annotation.AnimatorRes",
178                 "android.annotation.AnimatorRes" -> return "androidx.annotation.AnimatorRes"
179                 "android.support.annotation.AnyRes",
180                 "android.annotation.AnyRes" -> return "androidx.annotation.AnyRes"
181                 "android.support.annotation.ArrayRes",
182                 "android.annotation.ArrayRes" -> return "androidx.annotation.ArrayRes"
183                 "android.support.annotation.AttrRes",
184                 "android.annotation.AttrRes" -> return "androidx.annotation.AttrRes"
185                 "android.support.annotation.BoolRes",
186                 "android.annotation.BoolRes" -> return "androidx.annotation.BoolRes"
187                 "android.support.annotation.ColorRes",
188                 "android.annotation.ColorRes" -> return "androidx.annotation.ColorRes"
189                 "android.support.annotation.DimenRes",
190                 "android.annotation.DimenRes" -> return "androidx.annotation.DimenRes"
191                 "android.support.annotation.DrawableRes",
192                 "android.annotation.DrawableRes" -> return "androidx.annotation.DrawableRes"
193                 "android.support.annotation.FontRes",
194                 "android.annotation.FontRes" -> return "androidx.annotation.FontRes"
195                 "android.support.annotation.FractionRes",
196                 "android.annotation.FractionRes" -> return "androidx.annotation.FractionRes"
197                 "android.support.annotation.IdRes",
198                 "android.annotation.IdRes" -> return "androidx.annotation.IdRes"
199                 "android.support.annotation.IntegerRes",
200                 "android.annotation.IntegerRes" -> return "androidx.annotation.IntegerRes"
201                 "android.support.annotation.InterpolatorRes",
202                 "android.annotation.InterpolatorRes" -> return "androidx.annotation.InterpolatorRes"
203                 "android.support.annotation.LayoutRes",
204                 "android.annotation.LayoutRes" -> return "androidx.annotation.LayoutRes"
205                 "android.support.annotation.MenuRes",
206                 "android.annotation.MenuRes" -> return "androidx.annotation.MenuRes"
207                 "android.support.annotation.PluralsRes",
208                 "android.annotation.PluralsRes" -> return "androidx.annotation.PluralsRes"
209                 "android.support.annotation.RawRes",
210                 "android.annotation.RawRes" -> return "androidx.annotation.RawRes"
211                 "android.support.annotation.StringRes",
212                 "android.annotation.StringRes" -> return "androidx.annotation.StringRes"
213                 "android.support.annotation.StyleRes",
214                 "android.annotation.StyleRes" -> return "androidx.annotation.StyleRes"
215                 "android.support.annotation.StyleableRes",
216                 "android.annotation.StyleableRes" -> return "androidx.annotation.StyleableRes"
217                 "android.support.annotation.TransitionRes",
218                 "android.annotation.TransitionRes" -> return "androidx.annotation.TransitionRes"
219                 "android.support.annotation.XmlRes",
220                 "android.annotation.XmlRes" -> return "androidx.annotation.XmlRes"
221 
222                 // Threading
223                 "android.support.annotation.AnyThread",
224                 "android.annotation.AnyThread" -> return "androidx.annotation.AnyThread"
225                 "android.support.annotation.BinderThread",
226                 "android.annotation.BinderThread" -> return "androidx.annotation.BinderThread"
227                 "android.support.annotation.MainThread",
228                 "android.annotation.MainThread" -> return "androidx.annotation.MainThread"
229                 "android.support.annotation.UiThread",
230                 "android.annotation.UiThread" -> return "androidx.annotation.UiThread"
231                 "android.support.annotation.WorkerThread",
232                 "android.annotation.WorkerThread" -> return "androidx.annotation.WorkerThread"
233 
234                 // Colors
235                 "android.support.annotation.ColorInt",
236                 "android.annotation.ColorInt" -> return "androidx.annotation.ColorInt"
237                 "android.support.annotation.ColorLong",
238                 "android.annotation.ColorLong" -> return "androidx.annotation.ColorLong"
239                 "android.support.annotation.HalfFloat",
240                 "android.annotation.HalfFloat" -> return "androidx.annotation.HalfFloat"
241 
242                 // Ranges and sizes
243                 "android.support.annotation.FloatRange",
244                 "android.annotation.FloatRange" -> return "androidx.annotation.FloatRange"
245                 "android.support.annotation.IntRange",
246                 "android.annotation.IntRange" -> return "androidx.annotation.IntRange"
247                 "android.support.annotation.Size",
248                 "android.annotation.Size" -> return "androidx.annotation.Size"
249                 "android.support.annotation.Px",
250                 "android.annotation.Px" -> return "androidx.annotation.Px"
251                 "android.support.annotation.Dimension",
252                 "android.annotation.Dimension" -> return "androidx.annotation.Dimension"
253 
254                 // Null
255                 // We only change recently/newly nullable annotation in stubs
256                 RECENTLY_NULLABLE -> return if (target == AnnotationTarget.SDK_STUBS_FILE) qualifiedName else ANDROIDX_NULLABLE
257                 RECENTLY_NONNULL -> return if (target == AnnotationTarget.SDK_STUBS_FILE) qualifiedName else ANDROIDX_NONNULL
258 
259                 ANDROIDX_NULLABLE,
260                 ANDROID_NULLABLE,
261                 "android.support.annotation.Nullable",
262                 "libcore.util.Nullable",
263                 "org.jetbrains.annotations.Nullable" -> return nullableAnnotationName(target)
264 
265                 ANDROIDX_NONNULL,
266                 ANDROID_NONNULL,
267                 "android.support.annotation.NonNull",
268                 "libcore.util.NonNull",
269                 "org.jetbrains.annotations.NotNull" -> return nonNullAnnotationName(target)
270 
271                 // Typedefs
272                 "android.support.annotation.IntDef",
273                 "android.annotation.IntDef" -> return "androidx.annotation.IntDef"
274                 "android.support.annotation.StringDef",
275                 "android.annotation.StringDef" -> return "androidx.annotation.StringDef"
276                 "android.support.annotation.LongDef",
277                 "android.annotation.LongDef" -> return "androidx.annotation.LongDef"
278 
279                 // Misc
280                 "android.support.annotation.CallSuper",
281                 "android.annotation.CallSuper" -> return "androidx.annotation.CallSuper"
282                 "android.support.annotation.CheckResult",
283                 "android.annotation.CheckResult" -> return "androidx.annotation.CheckResult"
284                 "android.support.annotation.RequiresPermission",
285                 "android.annotation.RequiresPermission" -> return "androidx.annotation.RequiresPermission"
286                 "android.annotation.RequiresPermission.Read" -> return "androidx.annotation.RequiresPermission.Read"
287                 "android.annotation.RequiresPermission.Write" -> return "androidx.annotation.RequiresPermission.Write"
288 
289                 // These aren't support annotations, but could/should be:
290                 "android.annotation.CurrentTimeMillisLong",
291                 "android.annotation.DurationMillisLong",
292                 "android.annotation.ElapsedRealtimeLong",
293                 "android.annotation.UserIdInt",
294                 "android.annotation.BytesLong",
295 
296                 // These aren't support annotations
297                 "android.annotation.AppIdInt",
298                 "android.annotation.SuppressAutoDoc",
299                 "android.annotation.SystemApi",
300                 "android.annotation.TestApi",
301                 "android.annotation.CallbackExecutor",
302                 "android.annotation.Condemned",
303                 "android.annotation.Hide",
304 
305                 "android.annotation.Widget" -> {
306                     // Remove, unless (a) public or (b) specifically included in --showAnnotations
307                     return if (options.showAnnotations.matches(qualifiedName)) {
308                         qualifiedName
309                     } else if (filter != null) {
310                         val cls = codebase.findClass(qualifiedName)
311                         if (cls != null && filter.test(cls)) {
312                             qualifiedName
313                         } else {
314                             null
315                         }
316                     } else {
317                         qualifiedName
318                     }
319                 }
320 
321                 // Included for analysis, but should not be exported:
322                 "android.annotation.BroadcastBehavior",
323                 "android.annotation.SdkConstant",
324                 "android.annotation.RequiresFeature",
325                 "android.annotation.SystemService" -> return qualifiedName
326 
327                 // Should not be mapped to a different package name:
328                 "android.annotation.TargetApi",
329                 "android.annotation.SuppressLint" -> return qualifiedName
330 
331                 else -> {
332                     // Some new annotations added to the platform: assume they are support annotations?
333                     return when {
334                         // Special Kotlin annotations recognized by the compiler: map to supported package name
335                         qualifiedName.endsWith(".ParameterName") || qualifiedName.endsWith(".DefaultValue") ->
336                             "kotlin.annotations.jvm.internal${qualifiedName.substring(qualifiedName.lastIndexOf('.'))}"
337 
338                         // Other third party nullness annotations?
339                         isNullableAnnotation(qualifiedName) -> nullableAnnotationName(target)
340                         isNonNullAnnotation(qualifiedName) -> nonNullAnnotationName(target)
341 
342                         // Support library annotations are all included, as is the built-in stuff like @Retention
343                         qualifiedName.startsWith(ANDROIDX_ANNOTATION_PREFIX) -> return qualifiedName
344                         qualifiedName.startsWith(JAVA_LANG_PREFIX) -> return qualifiedName
345 
346                         // Unknown Android platform annotations
347                         qualifiedName.startsWith("android.annotation.") -> {
348                             // Remove, unless specifically included in --showAnnotations
349                             return if (options.showAnnotations.matches(qualifiedName)) {
350                                 qualifiedName
351                             } else {
352                                 null
353                             }
354                         }
355 
356                         qualifiedName.startsWith(ANDROID_SUPPORT_ANNOTATION_PREFIX) -> {
357                             return mapName(
358                                 codebase,
359                                 ANDROIDX_ANNOTATION_PREFIX + qualifiedName.substring(ANDROID_SUPPORT_ANNOTATION_PREFIX.length),
360                                 filter,
361                                 target
362                             )
363                         }
364 
365                         else -> {
366                             // Remove, unless (a) public or (b) specifically included in --showAnnotations
367                             return if (options.showAnnotations.matches(qualifiedName)) {
368                                 qualifiedName
369                             } else if (filter != null) {
370                                 val cls = codebase.findClass(qualifiedName)
371                                 if (cls != null && filter.test(cls)) {
372                                     qualifiedName
373                                 } else {
374                                     null
375                                 }
376                             } else {
377                                 qualifiedName
378                             }
379                         }
380                     }
381                 }
382             }
383         }
384 
nullableAnnotationNamenull385         private fun nullableAnnotationName(target: AnnotationTarget) =
386             if (target == AnnotationTarget.SDK_STUBS_FILE) ANDROID_NULLABLE else ANDROIDX_NULLABLE
387 
388         private fun nonNullAnnotationName(target: AnnotationTarget) =
389             if (target == AnnotationTarget.SDK_STUBS_FILE) ANDROID_NONNULL else ANDROIDX_NONNULL
390 
391         /** The applicable targets for this annotation */
392         fun computeTargets(annotation: AnnotationItem, codebase: Codebase): Set<AnnotationTarget> {
393             val qualifiedName = annotation.qualifiedName() ?: return NO_ANNOTATION_TARGETS
394             if (options.passThroughAnnotations.contains(qualifiedName)) {
395                 return ANNOTATION_IN_ALL_STUBS
396             }
397             when (qualifiedName) {
398 
399                 // The typedef annotations are special: they should not be in the signature
400                 // files, but we want to include them in the external annotations file such that tools
401                 // can enforce them.
402                 "android.support.annotation.IntDef",
403                 "android.annotation.IntDef",
404                 "androidx.annotation.IntDef",
405                 "android.support.annotation.StringDef",
406                 "android.annotation.StringDef",
407                 "androidx.annotation.StringDef",
408                 "android.support.annotation.LongDef",
409                 "android.annotation.LongDef",
410                 "androidx.annotation.LongDef" -> return ANNOTATION_EXTERNAL_ONLY
411 
412                 // Skip known annotations that we (a) never want in external annotations and (b) we are
413                 // specially overwriting anyway in the stubs (and which are (c) not API significant)
414                 "java.lang.annotation.Native",
415                 "java.lang.SuppressWarnings",
416                 "java.lang.Override" -> return NO_ANNOTATION_TARGETS
417 
418                 "java.lang.Deprecated", // tracked separately as a pseudo-modifier
419 
420                 // Below this when-statement we perform the correct lookup: check API predicate, and check
421                 // that retention is class or runtime, but we've hardcoded the answers here
422                 // for some common annotations.
423 
424                 "android.view.ViewDebug.ExportedProperty",
425                 "android.widget.RemoteViews.RemoteView",
426                 "android.view.ViewDebug.CapturedViewProperty",
427 
428                 "kotlin.annotation.Target",
429                 "kotlin.annotation.Retention",
430                 "kotlin.annotation.Repeatable",
431                 "kotlin.annotation.MustBeDocumented",
432                 "kotlin.DslMarker",
433                 "kotlin.PublishedApi",
434                 "kotlin.ExtensionFunctionType",
435 
436                 "java.lang.FunctionalInterface",
437                 "java.lang.SafeVarargs",
438                 "java.lang.annotation.Documented",
439                 "java.lang.annotation.Inherited",
440                 "java.lang.annotation.Repeatable",
441                 "java.lang.annotation.Retention",
442                 "java.lang.annotation.Target" -> return ANNOTATION_IN_ALL_STUBS
443             }
444 
445             // @android.annotation.Nullable and NonNullable specially recognized annotations by the Kotlin
446             // compiler 1.3 and above: they always go in the stubs.
447             if (qualifiedName == ANDROID_NULLABLE ||
448                 qualifiedName == ANDROID_NONNULL ||
449                 qualifiedName == ANDROIDX_NULLABLE ||
450                 qualifiedName == ANDROIDX_NONNULL
451             ) {
452                 return ANNOTATION_IN_ALL_STUBS
453             }
454 
455             if (qualifiedName.startsWith("android.annotation.")) {
456                 // internal annotations not mapped to androidx: things like @SystemApi. Skip from
457                 // stubs, external annotations, signature files, etc.
458                 return NO_ANNOTATION_TARGETS
459             }
460 
461             // @RecentlyNullable and @RecentlyNonNull are specially recognized annotations by the Kotlin
462             // compiler: they always go in the stubs.
463             if (qualifiedName == RECENTLY_NULLABLE ||
464                 qualifiedName == RECENTLY_NONNULL
465             ) {
466                 return ANNOTATION_IN_ALL_STUBS
467             }
468 
469             // Determine the retention of the annotation: source retention annotations go
470             // in the external annotations file, class and runtime annotations go in
471             // the stubs files (except for the androidx annotations which are not included
472             // in the SDK and therefore cannot be referenced from it due to apt's unfortunate
473             // habit of loading all annotation classes it encounters.)
474 
475             if (qualifiedName.startsWith("androidx.annotation.")) {
476                 if (options.includeSourceRetentionAnnotations) {
477                     return ANNOTATION_IN_ALL_STUBS
478                 }
479 
480                 if (qualifiedName == ANDROIDX_NULLABLE || qualifiedName == ANDROIDX_NONNULL) {
481                     // Right now, nullness annotations (other than @RecentlyNullable and @RecentlyNonNull)
482                     // have to go in external annotations since they aren't in the class path for
483                     // annotation processors. However, we do want them showing up in the documentation using
484                     // their real annotation names.
485                     return ANNOTATION_IN_DOC_STUBS_AND_EXTERNAL
486                 }
487 
488                 return ANNOTATION_EXTERNAL
489             }
490 
491             // See if the annotation is pointing to an annotation class that is part of the API; if not, skip it.
492             val cls = codebase.findClass(qualifiedName) ?: return NO_ANNOTATION_TARGETS
493             if (!ApiPredicate().test(cls)) {
494                 if (options.typedefMode != Options.TypedefMode.NONE) {
495                     if (cls.modifiers.annotations().any { it.isTypeDefAnnotation() }) {
496                         return ANNOTATION_SIGNATURE_ONLY
497                     }
498                 }
499 
500                 return NO_ANNOTATION_TARGETS
501             }
502 
503             if (cls.isAnnotationType()) {
504                 val retention = cls.getRetention()
505                 if (retention == AnnotationRetention.RUNTIME || retention == AnnotationRetention.CLASS) {
506                     return ANNOTATION_IN_SDK_STUBS
507                 }
508             }
509 
510             return ANNOTATION_EXTERNAL
511         }
512 
513         /**
514          * Given a "full" annotation name, shortens it by removing redundant package names.
515          * This is intended to be used by the [Compatibility.omitCommonPackages] flag
516          * to reduce clutter in signature files.
517          *
518          * For example, this method will convert `@androidx.annotation.Nullable` to just
519          * `@Nullable`, and `@androidx.annotation.IntRange(from=20)` to `IntRange(from=20)`.
520          */
shortenAnnotationnull521         fun shortenAnnotation(source: String): String {
522             return when {
523                 source == "@java.lang.Deprecated" -> "@Deprecated"
524                 source.startsWith("android.annotation.", 1) -> {
525                     "@" + source.substring("@android.annotation.".length)
526                 }
527                 source.startsWith(ANDROID_SUPPORT_ANNOTATION_PREFIX, 1) -> {
528                     "@" + source.substring("@android.support.annotation.".length)
529                 }
530                 source.startsWith(ANDROIDX_ANNOTATION_PREFIX, 1) -> {
531                     "@" + source.substring("@androidx.annotation.".length)
532                 }
533                 else -> source
534             }
535         }
536 
537         /**
538          * Reverses the [shortenAnnotation] method. Intended for use when reading in signature files
539          * that contain shortened type references.
540          */
unshortenAnnotationnull541         fun unshortenAnnotation(source: String): String {
542             return when {
543                 source == "@Deprecated" -> "@java.lang.Deprecated"
544                 // These 3 annotations are in the android.annotation. package, not android.support.annotation
545                 source.startsWith("@SystemService") ||
546                     source.startsWith("@TargetApi") ||
547                     source.startsWith("@SuppressLint") ->
548                     "@android.annotation." + source.substring(1)
549                 // If the first character of the name (after "@") is lower-case, then
550                 // assume it's a package name, so no need to shorten it.
551                 source.startsWith("@") && source[1].isLowerCase() -> source
552                 else -> {
553                     "@androidx.annotation." + source.substring(1)
554                 }
555             }
556         }
557 
558         /**
559          * If the given element has an *implicit* nullness, return it. This returns
560          * true for implicitly nullable elements, such as the parameter to the equals
561          * method, false for implicitly non null elements (such as annotation type
562          * members), and null if there is no implicit nullness.
563          */
getImplicitNullnessnull564         fun getImplicitNullness(item: Item): Boolean? {
565             var nullable: Boolean? = null
566 
567             // Constant field not initialized to null?
568             if (item is FieldItem &&
569                 (item.isEnumConstant() || item.modifiers.isFinal() && item.initialValue(false) != null)
570             ) {
571                 // Assigned to constant: not nullable
572                 nullable = false
573             }
574 
575             // Annotation type members cannot be null
576             if (item is MemberItem && item.containingClass().isAnnotationType()) {
577                 nullable = false
578             }
579 
580             // Equals and toString have known nullness
581             if (item is MethodItem && item.name() == "toString" && item.parameters().isEmpty()) {
582                 nullable = false
583             } else if (item is ParameterItem && item.containingMethod().name() == "equals" &&
584                 item.containingMethod().parameters().size == 1
585             ) {
586                 nullable = true
587             }
588 
589             return nullable
590         }
591     }
592 }
593 
594 /** Default implementation of an annotation item */
595 abstract class DefaultAnnotationItem(override val codebase: Codebase) : AnnotationItem {
596     private var targets: Set<AnnotationTarget>? = null
597 
targetsnull598     override fun targets(): Set<AnnotationTarget> {
599         if (targets == null) {
600             targets = AnnotationItem.computeTargets(this, codebase)
601         }
602         return targets!!
603     }
604 }
605 
606 /** An attribute of an annotation, such as "value" */
607 interface AnnotationAttribute {
608     /** The name of the annotation */
609     val name: String
610     /** The annotation value */
611     val value: AnnotationAttributeValue
612 
613     /**
614      * Return all leaf values; this flattens the complication of handling
615      * {@code @SuppressLint("warning")} and {@code @SuppressLint({"warning1","warning2"})
616      */
leafValuesnull617     fun leafValues(): List<AnnotationAttributeValue> {
618         val result = mutableListOf<AnnotationAttributeValue>()
619         AnnotationAttributeValue.addValues(value, result)
620         return result
621     }
622 }
623 
624 /** An annotation value */
625 interface AnnotationAttributeValue {
626     /** Generates source code for this annotation value */
toSourcenull627     fun toSource(): String
628 
629     /** The value of the annotation */
630     fun value(): Any?
631 
632     /** If the annotation declaration references a field (or class etc), return the resolved class */
633     fun resolve(): Item?
634 
635     companion object {
636         fun addValues(value: AnnotationAttributeValue, into: MutableList<AnnotationAttributeValue>) {
637             if (value is AnnotationArrayAttributeValue) {
638                 for (v in value.values) {
639                     addValues(v, into)
640                 }
641             } else if (value is AnnotationSingleAttributeValue) {
642                 into.add(value)
643             }
644         }
645     }
646 }
647 
648 /** An annotation value (for a single item, not an array) */
649 interface AnnotationSingleAttributeValue : AnnotationAttributeValue {
650     /** The annotation value, expressed as source code */
651     val valueSource: String
652     /** The annotation value */
653     val value: Any?
654 
valuenull655     override fun value() = value
656 }
657 
658 /** An annotation value for an array of items */
659 interface AnnotationArrayAttributeValue : AnnotationAttributeValue {
660     /** The annotation values */
661     val values: List<AnnotationAttributeValue>
662 
663     override fun resolve(): Item? {
664         error("resolve() should not be called on an array value")
665     }
666 
667     override fun value() = values.mapNotNull { it.value() }.toTypedArray()
668 }
669 
670 class DefaultAnnotationAttribute(
671     override val name: String,
672     override val value: DefaultAnnotationValue
673 ) : AnnotationAttribute {
674     companion object {
createnull675         fun create(name: String, value: String): DefaultAnnotationAttribute {
676             return DefaultAnnotationAttribute(name, DefaultAnnotationValue.create(value))
677         }
678 
createListnull679         fun createList(source: String): List<AnnotationAttribute> {
680             val list = mutableListOf<AnnotationAttribute>() // TODO: default size = 2
681             var begin = 0
682             var index = 0
683             val length = source.length
684             while (index < length) {
685                 val c = source[index]
686                 if (c == '{') {
687                     index = findEnd(source, index + 1, length, '}')
688                 } else if (c == '"') {
689                     index = findEnd(source, index + 1, length, '"')
690                 } else if (c == ',') {
691                     addAttribute(list, source, begin, index)
692                     index++
693                     begin = index
694                     continue
695                 } else if (c == ' ' && index == begin) {
696                     begin++
697                 }
698 
699                 index++
700             }
701 
702             if (begin < length) {
703                 addAttribute(list, source, begin, length)
704             }
705 
706             return list
707         }
708 
findEndnull709         private fun findEnd(source: String, from: Int, to: Int, sentinel: Char): Int {
710             var i = from
711             while (i < to) {
712                 val c = source[i]
713                 if (c == '\\') {
714                     i++
715                 } else if (c == sentinel) {
716                     return i
717                 }
718                 i++
719             }
720             return to
721         }
722 
addAttributenull723         private fun addAttribute(list: MutableList<AnnotationAttribute>, source: String, from: Int, to: Int) {
724             var split = source.indexOf('=', from)
725             if (split >= to) {
726                 split = -1
727             }
728             val name: String
729             val value: String
730             val valueBegin: Int
731             val valueEnd: Int
732             if (split == -1) {
733                 valueBegin = split + 1
734                 valueEnd = to
735                 name = "value"
736             } else {
737                 name = source.substring(from, split).trim()
738                 valueBegin = split + 1
739                 valueEnd = to
740             }
741             value = source.substring(valueBegin, valueEnd).trim()
742             list.add(DefaultAnnotationAttribute.create(name, value))
743         }
744     }
745 
toStringnull746     override fun toString(): String {
747         return "DefaultAnnotationAttribute(name='$name', value=$value)"
748     }
749 }
750 
751 abstract class DefaultAnnotationValue : AnnotationAttributeValue {
752     companion object {
createnull753         fun create(value: String): DefaultAnnotationValue {
754             return if (value.startsWith("{")) { // Array
755                 DefaultAnnotationArrayAttributeValue(value)
756             } else {
757                 DefaultAnnotationSingleAttributeValue(value)
758             }
759         }
760     }
761 
toStringnull762     override fun toString(): String = toSource()
763 }
764 
765 class DefaultAnnotationSingleAttributeValue(override val valueSource: String) : DefaultAnnotationValue(),
766     AnnotationSingleAttributeValue {
767     @Suppress("IMPLICIT_CAST_TO_ANY")
768     override val value = when {
769         valueSource == SdkConstants.VALUE_TRUE -> true
770         valueSource == SdkConstants.VALUE_FALSE -> false
771         valueSource.startsWith("\"") -> valueSource.removeSurrounding("\"")
772         valueSource.startsWith('\'') -> valueSource.removeSurrounding("'")[0]
773         else -> try {
774             if (valueSource.contains(".")) {
775                 valueSource.toDouble()
776             } else {
777                 valueSource.toLong()
778             }
779         } catch (e: NumberFormatException) {
780             valueSource
781         }
782     }
783 
784     override fun resolve(): Item? = null
785 
786     override fun toSource() = valueSource
787 }
788 
789 class DefaultAnnotationArrayAttributeValue(val value: String) : DefaultAnnotationValue(),
790     AnnotationArrayAttributeValue {
791     init {
<lambda>null792         assert(value.startsWith("{") && value.endsWith("}")) { value }
793     }
794 
<lambda>null795     override val values = value.substring(1, value.length - 1).split(",").map {
796         DefaultAnnotationValue.create(it.trim())
797     }.toList()
798 
toSourcenull799     override fun toSource() = value
800 }
801