• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * 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.tools.metalava.model.api.flags.ApiFlag
20 import com.android.tools.metalava.model.api.flags.ApiFlags
21 import com.android.tools.metalava.reporter.FileLocation
22 import kotlin.reflect.KClass
23 
24 fun isNullnessAnnotation(qualifiedName: String): Boolean =
25     isNullableAnnotation(qualifiedName) || isNonNullAnnotation(qualifiedName)
26 
27 fun isNullableAnnotation(qualifiedName: String): Boolean {
28     return qualifiedName == "Nullable" ||
29         qualifiedName.endsWith(".RecentlyNullable") ||
30         qualifiedName.endsWith(".Nullable") ||
31         qualifiedName.endsWith(".NullableType")
32 }
33 
isNonNullAnnotationnull34 fun isNonNullAnnotation(qualifiedName: String): Boolean {
35     return qualifiedName == "NonNull" ||
36         qualifiedName.endsWith(".RecentlyNonNull") ||
37         qualifiedName.endsWith(".NonNull") ||
38         qualifiedName.endsWith(".NotNull") ||
39         qualifiedName.endsWith(".Nonnull")
40 }
41 
isJvmSyntheticAnnotationnull42 fun isJvmSyntheticAnnotation(qualifiedName: String): Boolean {
43     return qualifiedName == "kotlin.jvm.JvmSynthetic"
44 }
45 
46 sealed interface AnnotationItem {
47     val annotationContext: AnnotationContext
48 
49     /**
50      * The location of this annotation with the source file.
51      *
52      * Will be [FileLocation.UNKNOWN] if the location cannot be determined, e.g. because it is from
53      * a `.class` file.
54      */
55     val fileLocation: FileLocation
56 
57     /** Fully qualified name of the annotation */
58     val qualifiedName: String
59 
60     /**
61      * Determines the effect that this will have on whether an item annotated with this annotation
62      * will be shown as part of the API or not.
63      */
64     val showability: Showability
65 
66     /**
67      * The [ApiFlag] referenced by this [AnnotationItem].
68      *
69      * This will be `null` if no [ApiFlags] have been provided or this [AnnotationItem]'s type is
70      * not [ANDROID_FLAGGED_API]. Otherwise, it will be one of the instances of [ApiFlag], e.g.
71      * [ApiFlag.REVERT_FLAGGED_API].
72      */
73     val apiFlag: ApiFlag?
74 
75     /** Generates source code for this annotation (using fully qualified names) */
toSourcenull76     fun toSource(
77         target: AnnotationTarget = AnnotationTarget.SIGNATURE_FILE,
78         showDefaultAttrs: Boolean = true
79     ): String
80 
81     /** The applicable targets for this annotation */
82     val targets: Set<AnnotationTarget>
83 
84     /** Attributes of the annotation; may be empty. */
85     val attributes: List<AnnotationAttribute>
86 
87     /**
88      * The [TypeNullability] associated with this or `null` if this is not a nullability annotation.
89      */
90     val typeNullability: TypeNullability?
91 
92     /** True if this annotation represents @Nullable or @NonNull (or some synonymous annotation) */
93     fun isNullnessAnnotation(): Boolean
94 
95     /** True if this annotation represents @Nullable (or some synonymous annotation) */
96     fun isNullable(): Boolean
97 
98     /** True if this annotation represents @NonNull (or some synonymous annotation) */
99     fun isNonNull(): Boolean
100 
101     /** True if this annotation represents @Retention (either the Java or Kotlin version) */
102     fun isRetention(): Boolean = isRetention(qualifiedName)
103 
104     /** True if this annotation represents @JvmSynthetic */
105     fun isJvmSynthetic(): Boolean {
106         return isJvmSyntheticAnnotation(qualifiedName)
107     }
108 
109     /** True if this annotation represents @IntDef, @LongDef or @StringDef */
isTypeDefAnnotationnull110     fun isTypeDefAnnotation(): Boolean {
111         val name = qualifiedName
112         if (!(name.endsWith("Def"))) {
113             return false
114         }
115         return (ANDROIDX_INT_DEF == name ||
116             ANDROIDX_STRING_DEF == name ||
117             ANDROIDX_LONG_DEF == name ||
118             ANDROID_INT_DEF == name ||
119             ANDROID_STRING_DEF == name ||
120             ANDROID_LONG_DEF == name)
121     }
122 
123     /** Returns the given named attribute if specified */
findAttributenull124     fun findAttribute(name: String?): AnnotationAttribute? {
125         val actualName = name ?: ANNOTATION_ATTR_VALUE
126         return attributes.firstOrNull { it.name == actualName }
127     }
128 
129     /** Find the class declaration for the given annotation */
resolvenull130     fun resolve(): ClassItem?
131 
132     /** If this annotation has a typedef annotation associated with it, return it */
133     fun findTypedefAnnotation(): AnnotationItem?
134 
135     /**
136      * Returns true iff the annotation is a show annotation.
137      *
138      * If `true` then an item annotated with this annotation (and any contents) will be added to the
139      * API.
140      *
141      * e.g. if a class is annotated with this then it will also apply (unless overridden by a closer
142      * annotation) to all its contents like nested classes, methods, fields, constructors,
143      * properties, etc.
144      */
145     fun isShowAnnotation(): Boolean
146 
147     /**
148      * Returns true iff this annotation is a show for stubs purposes annotation.
149      *
150      * If `true` then an item annotated with this annotation (and any contents) which are not
151      * annotated with another [isShowAnnotation] will be added to the stubs but not the API.
152      *
153      * e.g. if a class is annotated with this then it will also apply (unless overridden by a closer
154      * annotation) to all its contents like nested classes, methods, fields, constructors,
155      * properties, etc.
156      */
157     fun isShowForStubPurposes(): Boolean
158 
159     /**
160      * Returns true iff this annotation is a hide annotation.
161      *
162      * Hide annotations can either be explicitly specified when creating the [Codebase] or they can
163      * be any annotation that is annotated with a hide meta-annotation (see [isHideMetaAnnotation]).
164      *
165      * If `true` then an item annotated with this annotation (and any contents) will be excluded
166      * from the API.
167      *
168      * e.g. if a class is annotated with this then it will also apply (unless overridden by a closer
169      * annotation) to all its contents like nested classes, methods, fields, constructors,
170      * properties, etc.
171      */
172     fun isHideAnnotation(): Boolean
173 
174     fun isSuppressCompatibilityAnnotation(): Boolean
175 
176     /**
177      * Returns true iff this annotation is a showability annotation, i.e. one that will affect
178      * [showability].
179      */
180     fun isShowabilityAnnotation(): Boolean
181 
182     /** Returns the retention of this annotation */
183     val retention: AnnotationRetention
184         get() {
185             val cls = resolve()
186             if (cls != null) {
187                 if (cls.isAnnotationType()) {
188                     return cls.getRetention()
189                 }
190             }
191 
192             return AnnotationRetention.getDefault()
193         }
194 
195     /** Take a snapshot of this [AnnotationItem] suitable for use in [Codebase]. */
snapshotnull196     fun snapshot(targetCodebase: Codebase): AnnotationItem
197 
198     companion object {
199         /**
200          * The simple name of an annotation, which is the annotation name (not qualified name)
201          * prefixed by @
202          */
203         fun simpleName(item: AnnotationItem): String {
204             return item.qualifiedName.let { "@${it.substringAfterLast('.')}" }
205         }
206 
207         /**
208          * Given a "full" annotation name, shortens it by removing redundant package names. This is
209          * intended to be used to reduce clutter in signature files.
210          *
211          * For example, this method will convert `@androidx.annotation.Nullable` to just
212          * `@Nullable`, and `@androidx.annotation.IntRange(from=20)` to `IntRange(from=20)`.
213          */
214         fun shortenAnnotation(source: String): String {
215             return when {
216                 source == "@java.lang.Deprecated" -> "@Deprecated"
217                 source.startsWith(ANDROID_ANNOTATION_PREFIX, 1) -> {
218                     "@" + source.substring(ANDROID_ANNOTATION_PREFIX.length + 1)
219                 }
220                 source.startsWith(ANDROIDX_ANNOTATION_PREFIX, 1) -> {
221                     "@" + source.substring(ANDROIDX_ANNOTATION_PREFIX.length + 1)
222                 }
223                 else -> source
224             }
225         }
226 
227         /**
228          * Reverses the [shortenAnnotation] method. Intended for use when reading in signature files
229          * that contain shortened type references.
230          */
231         fun unshortenAnnotation(source: String): String {
232             return when {
233                 source == "@Deprecated" -> "@java.lang.Deprecated"
234                 // The first 4 annotations are in the android.annotation. package, not
235                 // androidx.annotation
236                 // Nullability annotations are written as @NonNull and @Nullable in API text files,
237                 // and these should be linked no android.annotation package when generating stubs.
238                 source.startsWith("@SystemService") ||
239                     source.startsWith("@TargetApi") ||
240                     source.startsWith("@SuppressLint") ||
241                     source.startsWith("@FlaggedApi") ||
242                     source.startsWith("@Nullable") ||
243                     source.startsWith("@NonNull") -> "@android.annotation." + source.substring(1)
244                 // If the first character of the name (after "@") is lower-case, then
245                 // assume it's a package name, so no need to shorten it.
246                 source.startsWith("@") && source[1].isLowerCase() -> source
247                 else -> {
248                     "@androidx.annotation." + source.substring(1)
249                 }
250             }
251         }
252     }
253 }
254 
255 /** Get the [TypeNullability] from a list of [AnnotationItem]s. */
256 val List<AnnotationItem>.typeNullability
<lambda>null257     get() = mapNotNull { it.typeNullability }.firstOrNull()
258 
259 /**
260  * Get the value of the named attribute as an object of the specified type or null if the attribute
261  * could not be found.
262  *
263  * This can only be called for attributes which have a single value, it will throw an exception if
264  * called for an attribute whose value is any array type. See [getAttributeValues] instead.
265  *
266  * This supports the following types for [T]:
267  * * [String] - the attribute must be of type [String] or [Class].
268  * * [AnnotationItem] - the attribute must be of an annotation type.
269  * * [Boolean] - the attribute must be of type [Boolean].
270  * * [Byte] - the attribute must be of type [Byte].
271  * * [Char] - the attribute must be of type [Char].
272  * * [Double] - the attribute must be of type [Double].
273  * * [Float] - the attribute must be of type [Float].
274  * * [Int] - the attribute must be of type [Int].
275  * * [Long] - the attribute must be of type [Long].
276  * * [Short] - the attribute must be of type [Short].
277  *
278  * Any other types will result in a [ClassCastException].
279  */
getAttributeValuenull280 inline fun <reified T : Any> AnnotationItem.getAttributeValue(name: String): T? {
281     val value = nonInlineGetAttributeValue(T::class, name) ?: return null
282     return value as T
283 }
284 
285 /**
286  * Non-inline portion of functionality needed by [getAttributeValue]; separated to reduce the cost
287  * of inlining [getAttributeValue].
288  */
289 @PublishedApi
nonInlineGetAttributeValuenull290 internal fun AnnotationItem.nonInlineGetAttributeValue(kClass: KClass<*>, name: String): Any? {
291     val attributeValue = findAttribute(name)?.legacyValue ?: return null
292     val value =
293         when (attributeValue) {
294             is AnnotationArrayAttributeValue ->
295                 throw IllegalStateException("Annotation attribute is of type array")
296             else -> attributeValue.value()
297         }
298             ?: return null
299 
300     return convertValue(annotationContext, kClass, value)
301 }
302 
303 /**
304  * Get the values of the named attribute as a list of objects of the specified type or null if the
305  * attribute could not be found.
306  *
307  * This can be used to get the value of an attribute that is either one of the types in
308  * [getAttributeValue] (in which case this returns a list containing a single item), or an array of
309  * one of the types in [getAttributeValue] (in which case this returns a list containing all the
310  * items in the array).
311  */
getAttributeValuesnull312 inline fun <reified T : Any> AnnotationItem.getAttributeValues(name: String): List<T>? {
313     return nonInlineGetAttributeValues(T::class, name) { it as T }
314 }
315 
316 /**
317  * Non-inline portion of functionality needed by [getAttributeValues]; separated to reduce the cost
318  * of inlining [getAttributeValues].
319  */
320 @PublishedApi
nonInlineGetAttributeValuesnull321 internal fun <T : Any> AnnotationItem.nonInlineGetAttributeValues(
322     kClass: KClass<*>,
323     name: String,
324     caster: (Any) -> T
325 ): List<T>? {
326     val attributeValue = findAttribute(name)?.legacyValue ?: return null
327     val values =
328         when (attributeValue) {
329             is AnnotationArrayAttributeValue -> attributeValue.values.mapNotNull { it.value() }
330             else -> listOfNotNull(attributeValue.value())
331         }
332 
333     return values.mapNotNull { convertValue(annotationContext, kClass, it) }.map { caster(it) }
334 }
335 
336 /**
337  * Perform some conversions to try and make [value] to be an instance of [kClass].
338  *
339  * This fixes up some known issues with [value] not corresponding to the expected type but otherwise
340  * simply returns the value it is given. It is the caller's responsibility to actually cast the
341  * returned value to the correct type.
342  */
convertValuenull343 private fun convertValue(
344     annotationContext: AnnotationContext,
345     kClass: KClass<*>,
346     value: Any
347 ): Any? {
348     // The value stored for number types is not always the same as the type of the annotation
349     // attributes. This is for a number of reasons, e.g.
350     // * In a .class file annotation values are stored in the constant pool and some number types do
351     //   not have their own constant form (or their own array constant form) so are stored as
352     //   instances of a wider type. They need to be converted to the correct type.
353     // * In signature files annotation values are not always stored as the narrowest type, may not
354     //   have a suffix and type information may not always be available when parsing.
355     if (Number::class.java.isAssignableFrom(kClass.java)) {
356         value as Number
357         return when (kClass) {
358             // Byte does have its own constant form but when stored in an array it is stored as an
359             // int.
360             Byte::class -> value.toByte()
361             // DefaultAnnotationValue.create() always reads integers as longs.
362             Int::class -> value.toInt()
363             // DefaultAnnotationValue.create() always reads floating point as doubles.
364             Float::class -> value.toFloat()
365             // Short does not have its own constant form.
366             Short::class -> value.toShort()
367             else -> value
368         }
369     }
370 
371     // TODO: Push down into the model as that is likely to be more efficient.
372     if (kClass == AnnotationItem::class) {
373         return DefaultAnnotationItem.create(annotationContext, value as String)
374     }
375 
376     return value
377 }
378 
379 /** Provides contextual information needed by [AnnotationItem]s. */
380 interface AnnotationContext : ClassResolver {
381     /** The manager of annotations within this context. */
382     val annotationManager: AnnotationManager
383 
384     /**
385      * Creates an annotation item for the given (fully qualified) Java source.
386      *
387      * Returns `null` if the source contains an annotation that is not recognized by Metalava.
388      */
createAnnotationnull389     fun createAnnotation(
390         source: String,
391         context: Item? = null,
392     ): AnnotationItem?
393 }
394 
395 /** Default implementation of an annotation item */
396 open class DefaultAnnotationItem
397 /** The primary constructor is private to force subclasses to use the secondary constructor. */
398 protected constructor(
399     override val annotationContext: AnnotationContext,
400     override val fileLocation: FileLocation,
401 
402     /** Fully qualified name of the annotation (prior to name mapping) */
403     protected val originalName: String,
404 
405     /** Fully qualified name of the annotation (after name mapping) */
406     final override val qualifiedName: String,
407 
408     /** Possibly empty list of attributes. */
409     attributesGetter: (AnnotationItem) -> List<AnnotationAttribute>,
410 ) : AnnotationItem {
411 
412     override val targets
413         get() = info.targets
414 
415     final override val attributes: List<AnnotationAttribute> by
416         lazy(LazyThreadSafetyMode.NONE) { attributesGetter(this) }
417 
418     /** Information that metalava has gathered about this annotation item. */
419     internal val info: AnnotationInfo by lazy {
420         annotationContext.annotationManager.getAnnotationInfo(this)
421     }
422 
423     override val typeNullability: TypeNullability?
424         get() = info.typeNullability
425 
426     override fun isNullnessAnnotation(): Boolean {
427         return info.typeNullability != null
428     }
429 
430     override fun isNullable(): Boolean {
431         return info.typeNullability == TypeNullability.NULLABLE
432     }
433 
434     override fun isNonNull(): Boolean {
435         return info.typeNullability == TypeNullability.NONNULL
436     }
437 
438     override val showability: Showability
439         get() = info.showability
440 
441     override val apiFlag: ApiFlag?
442         get() = info.apiFlag
443 
444     override fun resolve(): ClassItem? {
445         return annotationContext.resolveClass(originalName)
446     }
447 
448     /** If this annotation has a typedef annotation associated with it, return it */
449     override fun findTypedefAnnotation(): AnnotationItem? {
450         return resolve()?.modifiers?.findAnnotation(AnnotationItem::isTypeDefAnnotation)
451     }
452 
453     override fun isShowAnnotation(): Boolean = info.showability.show()
454 
455     override fun isShowForStubPurposes(): Boolean = info.showability.showForStubsOnly()
456 
457     override fun isHideAnnotation(): Boolean = info.showability.hide()
458 
459     override fun isSuppressCompatibilityAnnotation(): Boolean = info.suppressCompatibility
460 
461     override fun isShowabilityAnnotation(): Boolean = info.showability != Showability.NO_EFFECT
462 
463     override fun snapshot(targetCodebase: Codebase): AnnotationItem {
464         // Force the info property to be initialized which will cause the AnnotationInfo for
465         // annotations of the same class as this to be created based off this AnnotationItem and
466         // not the snapshot AnnotationItem. That is important because the AnnotationInfo
467         // properties depends on accessing information like the ApiVariantSelectors which is
468         // discarded when creating the snapshot. The snapshot AnnotationItem will retrieve the
469         // cached version of the AnnotationInfo from the AnnotationManager.
470         info
471 
472         return DefaultAnnotationItem(
473             targetCodebase,
474             fileLocation,
475             originalName,
476             qualifiedName,
477         ) {
478             attributes.map { DefaultAnnotationAttribute(it.name, it.legacyValue.snapshot()) }
479         }
480     }
481 
482     override fun equals(other: Any?): Boolean {
483         if (other !is AnnotationItem) return false
484         return qualifiedName == other.qualifiedName && attributes == other.attributes
485     }
486 
487     override fun hashCode(): Int {
488         var result = qualifiedName.hashCode()
489         result = 31 * result + attributes.hashCode()
490         return result
491     }
492 
493     override fun toSource(target: AnnotationTarget, showDefaultAttrs: Boolean): String {
494         val qualifiedName =
495             annotationContext.annotationManager.normalizeOutputName(qualifiedName, target)
496                 ?: return ""
497 
498         return formatAnnotationItem(qualifiedName, attributes)
499     }
500 
501     final override fun toString() = toSource()
502 
503     companion object {
504         fun formatAnnotationItem(
505             qualifiedName: String,
506             attributes: List<AnnotationAttribute>,
507         ): String {
508             return buildString {
509                 append("@")
510                 append(qualifiedName)
511                 if (attributes.isNotEmpty()) {
512                     val suppressDefaultAnnotationAttribute = attributes.size == 1
513                     append("(")
514                     attributes.forEachIndexed { i, attribute ->
515                         if (i != 0) {
516                             append(", ")
517                         }
518                         if (
519                             !suppressDefaultAnnotationAttribute ||
520                                 attribute.name != ANNOTATION_ATTR_VALUE
521                         ) {
522                             append(attribute.name)
523                             append("=")
524                         }
525                         append(attribute.legacyValue)
526                     }
527                     append(")")
528                 }
529             }
530         }
531 
532         fun create(annotationContext: AnnotationContext, source: String): AnnotationItem? {
533             val index = source.indexOf("(")
534             val originalName =
535                 if (index == -1) source.substring(1) // Strip @
536                 else source.substring(1, index)
537 
538             @Suppress("UNUSED_PARAMETER")
539             fun attributes(annotationItem: AnnotationItem): List<AnnotationAttribute> =
540                 if (index == -1) {
541                     emptyList()
542                 } else {
543                     DefaultAnnotationAttribute.createList(
544                         source.substring(index + 1, source.lastIndexOf(')'))
545                     )
546                 }
547 
548             return create(annotationContext, FileLocation.UNKNOWN, originalName, ::attributes)
549         }
550 
551         fun create(
552             annotationContext: AnnotationContext,
553             originalName: String,
554             attributes: List<AnnotationAttribute> = emptyList(),
555             context: Item? = null
556         ): AnnotationItem? {
557             val source = formatAnnotationItem(originalName, attributes)
558             return annotationContext.createAnnotation(source, context)
559         }
560 
561         /**
562          * Create a [DefaultAnnotationItem] by mapping the [originalName] to a [qualifiedName] by
563          * using the [annotationContext]'s [AnnotationManager.normalizeInputName].
564          */
565         fun create(
566             annotationContext: AnnotationContext,
567             fileLocation: FileLocation,
568             originalName: String,
569             attributesGetter: (AnnotationItem) -> List<AnnotationAttribute>,
570         ): AnnotationItem? {
571             val qualifiedName =
572                 annotationContext.annotationManager.normalizeInputName(originalName) ?: return null
573             return DefaultAnnotationItem(
574                 annotationContext = annotationContext,
575                 fileLocation = fileLocation,
576                 originalName = originalName,
577                 qualifiedName = qualifiedName,
578                 attributesGetter = attributesGetter,
579             )
580         }
581     }
582 }
583 
584 /** The default annotation attribute name when no name is provided. */
585 const val ANNOTATION_ATTR_VALUE = "value"
586 
587 /** An attribute of an annotation, such as "value" */
588 sealed interface AnnotationAttribute {
589     /** The name of the annotation */
590     val name: String
591 
592     /**
593      * The legacy annotation value.
594      *
595      * This is called `legacy` because this an old, inconsistent representation of an attribute
596      * value that exposes implementation details. It will be replaced by a properly modelled value
597      * representation.
598      */
599     val legacyValue: AnnotationAttributeValue
600 
601     /**
602      * Return all leaf values; this flattens the complication of handling
603      * {@code @SuppressLint("warning")} and {@code @SuppressLint({"warning1","warning2"})
604      */
leafValuesnull605     fun leafValues(): List<AnnotationAttributeValue> {
606         val result = mutableListOf<AnnotationAttributeValue>()
607         AnnotationAttributeValue.addValues(legacyValue, result)
608         return result
609     }
610 }
611 
612 const val ANNOTATION_VALUE_FALSE = "false"
613 const val ANNOTATION_VALUE_TRUE = "true"
614 
615 /** An annotation value */
616 sealed interface AnnotationAttributeValue {
617     /** Generates source code for this annotation value */
toSourcenull618     fun toSource(): String
619 
620     /** The value of the annotation */
621     fun value(): Any?
622 
623     /**
624      * If the annotation declaration references a field (or class etc.), return the resolved class
625      */
626     fun resolve(): Item?
627 
628     /**
629      * Take a snapshot of this [AnnotationAttributeValue] suitable for use in a snapshot [Codebase].
630      */
631     fun snapshot(): AnnotationAttributeValue
632 
633     companion object {
634         fun addValues(
635             value: AnnotationAttributeValue,
636             into: MutableList<AnnotationAttributeValue>
637         ) {
638             if (value is AnnotationArrayAttributeValue) {
639                 for (v in value.values) {
640                     addValues(v, into)
641                 }
642             } else if (value is AnnotationSingleAttributeValue) {
643                 into.add(value)
644             }
645         }
646     }
647 }
648 
649 /** An annotation value (for a single item, not an array) */
650 sealed interface AnnotationSingleAttributeValue : AnnotationAttributeValue {
651     val value: Any?
652 
valuenull653     override fun value() = value
654 }
655 
656 /** An annotation value for an array of items */
657 sealed interface AnnotationArrayAttributeValue : AnnotationAttributeValue {
658     /** The annotation values */
659     val values: List<AnnotationAttributeValue>
660 
661     override fun resolve(): Item? {
662         error("resolve() should not be called on an array value")
663     }
664 
665     override fun value() = values.mapNotNull { it.value() }.toTypedArray()
666 }
667 
668 class DefaultAnnotationAttribute(
669     override val name: String,
670     override val legacyValue: AnnotationAttributeValue
671 ) : AnnotationAttribute {
672     companion object {
createnull673         fun create(name: String, value: String): DefaultAnnotationAttribute {
674             return DefaultAnnotationAttribute(name, DefaultAnnotationValue.create(value))
675         }
676 
createListnull677         fun createList(source: String): List<AnnotationAttribute> {
678             val list = mutableListOf<AnnotationAttribute>() // TODO: default size = 2
679             var begin = 0
680             var index = 0
681             val length = source.length
682             while (index < length) {
683                 val c = source[index]
684                 if (c == '{') {
685                     index = findEnd(source, index + 1, length, '}')
686                 } else if (c == '"') {
687                     index = findEnd(source, index + 1, length, '"')
688                 } else if (c == ',') {
689                     addAttribute(list, source, begin, index)
690                     index++
691                     begin = index
692                     continue
693                 } else if (c == ' ' && index == begin) {
694                     begin++
695                 }
696 
697                 index++
698             }
699 
700             if (begin < length) {
701                 addAttribute(list, source, begin, length)
702             }
703 
704             return list
705         }
706 
findEndnull707         private fun findEnd(source: String, from: Int, to: Int, sentinel: Char): Int {
708             var i = from
709             while (i < to) {
710                 val c = source[i]
711                 if (c == '\\') {
712                     i++
713                 } else if (c == sentinel) {
714                     return i
715                 }
716                 i++
717             }
718             return to
719         }
720 
addAttributenull721         private fun addAttribute(
722             list: MutableList<AnnotationAttribute>,
723             source: String,
724             from: Int,
725             to: Int
726         ) {
727             var split = source.indexOf('=', from)
728             if (split >= to) {
729                 split = -1
730             }
731             val name: String
732             val value: String
733             val valueBegin: Int
734             val valueEnd: Int
735             if (split == -1) {
736                 valueBegin = from
737                 valueEnd = to
738                 name = "value"
739             } else {
740                 name = source.substring(from, split).trim()
741                 valueBegin = split + 1
742                 valueEnd = to
743             }
744             value = source.substring(valueBegin, valueEnd).trim()
745             if (!value.isEmpty()) {
746                 list.add(create(name, value))
747             }
748         }
749     }
750 
toStringnull751     override fun toString(): String {
752         return "$name=$legacyValue"
753     }
754 
equalsnull755     override fun equals(other: Any?): Boolean {
756         if (other !is AnnotationAttribute) return false
757         return name == other.name && legacyValue == other.legacyValue
758     }
759 
hashCodenull760     override fun hashCode(): Int {
761         var result = name.hashCode()
762         result = 31 * result + legacyValue.hashCode()
763         return result
764     }
765 }
766 
767 abstract class DefaultAnnotationValue(sourceGetter: () -> String) : AnnotationAttributeValue {
768     companion object {
createnull769         fun create(valueSource: String): DefaultAnnotationValue {
770             return if (valueSource.startsWith("{")) { // Array
771                 DefaultAnnotationArrayAttributeValue(
772                     { valueSource },
773                     {
774                         assert(valueSource.startsWith("{") && valueSource.endsWith("}")) {
775                             valueSource
776                         }
777                         valueSource
778                             .substring(1, valueSource.length - 1)
779                             .split(",")
780                             .map { create(it.trim()) }
781                             .toList()
782                     },
783                 )
784             } else {
785                 DefaultAnnotationSingleAttributeValue(
786                     { valueSource },
787                     {
788                         when {
789                             valueSource == ANNOTATION_VALUE_TRUE -> true
790                             valueSource == ANNOTATION_VALUE_FALSE -> false
791                             valueSource.startsWith("\"") -> valueSource.removeSurrounding("\"")
792                             valueSource.startsWith('\'') -> valueSource.removeSurrounding("'")[0]
793                             else ->
794                                 try {
795                                     if (valueSource.contains(".")) {
796                                         valueSource.toDouble()
797                                     } else {
798                                         valueSource.toLong()
799                                     }
800                                 } catch (e: NumberFormatException) {
801                                     valueSource
802                                 }
803                         }
804                     },
805                 )
806             }
807         }
808     }
809 
810     /** The annotation value, expressed as source code */
811     private val valueSource: String by lazy(LazyThreadSafetyMode.NONE, sourceGetter)
812 
toSourcenull813     override fun toSource() = valueSource
814 
815     override fun toString(): String = toSource()
816 }
817 
818 open class DefaultAnnotationSingleAttributeValue(
819     sourceGetter: () -> String,
820     valueGetter: () -> Any?
821 ) : DefaultAnnotationValue(sourceGetter), AnnotationSingleAttributeValue {
822 
823     override val value by lazy(LazyThreadSafetyMode.NONE, valueGetter)
824 
825     override fun resolve(): Item? = null
826 
827     override fun snapshot(): AnnotationSingleAttributeValue {
828         // Take a snapshot of the value and sources by immediately forcing them to be initialized
829         // from their respective getters. That way there will be no connection to the original
830         // attribute value.
831         val newValue = value
832         val newSource = toSource()
833         return DefaultAnnotationSingleAttributeValue(
834             sourceGetter = { newSource },
835             valueGetter = { newValue },
836         )
837     }
838 
839     override fun equals(other: Any?): Boolean {
840         if (other !is AnnotationSingleAttributeValue) return false
841         return value == other.value
842     }
843 
844     override fun hashCode(): Int {
845         return value.hashCode()
846     }
847 }
848 
849 class DefaultAnnotationArrayAttributeValue(
850     sourceGetter: () -> String,
851     valuesGetter: () -> List<AnnotationAttributeValue>
852 ) : DefaultAnnotationValue(sourceGetter), AnnotationArrayAttributeValue {
853 
854     override val values by lazy(LazyThreadSafetyMode.NONE, valuesGetter)
855 
snapshotnull856     override fun snapshot(): AnnotationArrayAttributeValue {
857         // Take a snapshot of the values and sources by immediately forcing them to be initialized
858         // from their respective getters. That way there will be no connection to the original
859         // attribute value.
860         val newValues = values.map { it.snapshot() }
861         val newSource = toSource()
862         return DefaultAnnotationArrayAttributeValue(
863             sourceGetter = { newSource },
864             valuesGetter = { newValues },
865         )
866     }
867 
equalsnull868     override fun equals(other: Any?): Boolean {
869         if (other !is AnnotationArrayAttributeValue) return false
870         return values == other.values
871     }
872 
hashCodenull873     override fun hashCode(): Int {
874         return values.hashCode()
875     }
876 }
877