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