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