• 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 kotlin.reflect.KClass
20 
21 fun isNullnessAnnotation(qualifiedName: String): Boolean =
22     isNullableAnnotation(qualifiedName) || isNonNullAnnotation(qualifiedName)
23 
24 fun isNullableAnnotation(qualifiedName: String): Boolean {
25     return qualifiedName == "Nullable" ||
26         qualifiedName.endsWith(".RecentlyNullable") ||
27         qualifiedName.endsWith(".Nullable") ||
28         qualifiedName.endsWith(".NullableType")
29 }
30 
isNonNullAnnotationnull31 fun isNonNullAnnotation(qualifiedName: String): Boolean {
32     return qualifiedName == "NonNull" ||
33         qualifiedName.endsWith(".RecentlyNonNull") ||
34         qualifiedName.endsWith(".NonNull") ||
35         qualifiedName.endsWith(".NotNull") ||
36         qualifiedName.endsWith(".Nonnull")
37 }
38 
isJvmSyntheticAnnotationnull39 fun isJvmSyntheticAnnotation(qualifiedName: String): Boolean {
40     return qualifiedName == "kotlin.jvm.JvmSynthetic"
41 }
42 
43 interface AnnotationItem {
44     val codebase: Codebase
45 
46     /** Fully qualified name of the annotation */
47     val qualifiedName: String?
48 
49     /**
50      * Determines the effect that this will have on whether an item annotated with this annotation
51      * will be shown as part of the API or not.
52      */
53     val showability: Showability
54 
55     /** Generates source code for this annotation (using fully qualified names) */
toSourcenull56     fun toSource(
57         target: AnnotationTarget = AnnotationTarget.SIGNATURE_FILE,
58         showDefaultAttrs: Boolean = true
59     ): String
60 
61     /** The applicable targets for this annotation */
62     val targets: Set<AnnotationTarget>
63 
64     /** Attributes of the annotation; may be empty. */
65     val attributes: List<AnnotationAttribute>
66 
67     /**
68      * The [TypeNullability] associated with this or `null` if this is not a nullability annotation.
69      */
70     val typeNullability: TypeNullability?
71 
72     /** True if this annotation represents @Nullable or @NonNull (or some synonymous annotation) */
73     fun isNullnessAnnotation(): Boolean
74 
75     /** True if this annotation represents @Nullable (or some synonymous annotation) */
76     fun isNullable(): Boolean
77 
78     /** True if this annotation represents @NonNull (or some synonymous annotation) */
79     fun isNonNull(): Boolean
80 
81     /** True if this annotation represents @Retention (either the Java or Kotlin version) */
82     fun isRetention(): Boolean = isRetention(qualifiedName)
83 
84     /** True if this annotation represents @JvmSynthetic */
85     fun isJvmSynthetic(): Boolean {
86         return isJvmSyntheticAnnotation(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 (ANDROIDX_INT_DEF == name ||
96             ANDROIDX_STRING_DEF == name ||
97             ANDROIDX_LONG_DEF == 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
105      * annotation). 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
113      * annotation). 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 ?: ANNOTATION_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 
128     /** If this annotation has a typedef annotation associated with it, return it */
129     fun findTypedefAnnotation(): AnnotationItem?
130 
131     /**
132      * Returns true iff the annotation is a show annotation.
133      *
134      * If `true` then an item annotated with this annotation (and any contents) will be added to the
135      * API.
136      *
137      * e.g. if a class is annotated with this then it will also apply (unless overridden by a closer
138      * annotation) to all its contents like nested classes, methods, fields, constructors,
139      * properties, etc.
140      */
141     fun isShowAnnotation(): Boolean
142 
143     /**
144      * Returns true iff this annotation is a show for stubs purposes annotation.
145      *
146      * If `true` then an item annotated with this annotation (and any contents) which are not
147      * annotated with another [isShowAnnotation] will be added to the stubs but not the API.
148      *
149      * e.g. if a class is annotated with this then it will also apply (unless overridden by a closer
150      * annotation) to all its contents like nested classes, methods, fields, constructors,
151      * properties, etc.
152      */
153     fun isShowForStubPurposes(): Boolean
154 
155     /**
156      * Returns true iff this annotation is a hide annotation.
157      *
158      * Hide annotations can either be explicitly specified when creating the [Codebase] or they can
159      * be any annotation that is annotated with a hide meta-annotation (see [isHideMetaAnnotation]).
160      *
161      * If `true` then an item annotated with this annotation (and any contents) will be excluded
162      * from the API.
163      *
164      * e.g. if a class is annotated with this then it will also apply (unless overridden by a closer
165      * annotation) to all its contents like nested classes, methods, fields, constructors,
166      * properties, etc.
167      */
168     fun isHideAnnotation(): Boolean
169 
170     fun isSuppressCompatibilityAnnotation(): Boolean
171 
172     /**
173      * Returns true iff this annotation is a showability annotation, i.e. one that will affect
174      * [showability].
175      */
176     fun isShowabilityAnnotation(): Boolean
177 
178     /** Returns the retention of this annotation */
179     val retention: AnnotationRetention
180         get() {
181             val cls = resolve()
182             if (cls != null) {
183                 if (cls.isAnnotationType()) {
184                     return cls.getRetention()
185                 }
186             }
187 
188             return AnnotationRetention.getDefault()
189         }
190 
191     companion object {
192         /**
193          * The simple name of an annotation, which is the annotation name (not qualified name)
194          * prefixed by @
195          */
simpleNamenull196         fun simpleName(item: AnnotationItem): String {
197             return item.qualifiedName?.let { "@${it.substringAfterLast('.')}" }.orEmpty()
198         }
199 
200         /**
201          * Given a "full" annotation name, shortens it by removing redundant package names. This is
202          * intended to be used to reduce clutter in signature files.
203          *
204          * For example, this method will convert `@androidx.annotation.Nullable` to just
205          * `@Nullable`, and `@androidx.annotation.IntRange(from=20)` to `IntRange(from=20)`.
206          */
shortenAnnotationnull207         fun shortenAnnotation(source: String): String {
208             return when {
209                 source == "@java.lang.Deprecated" -> "@Deprecated"
210                 source.startsWith(ANDROID_ANNOTATION_PREFIX, 1) -> {
211                     "@" + source.substring(ANDROID_ANNOTATION_PREFIX.length + 1)
212                 }
213                 source.startsWith(ANDROIDX_ANNOTATION_PREFIX, 1) -> {
214                     "@" + source.substring(ANDROIDX_ANNOTATION_PREFIX.length + 1)
215                 }
216                 else -> source
217             }
218         }
219 
220         /**
221          * Reverses the [shortenAnnotation] method. Intended for use when reading in signature files
222          * that contain shortened type references.
223          */
unshortenAnnotationnull224         fun unshortenAnnotation(source: String): String {
225             return when {
226                 source == "@Deprecated" -> "@java.lang.Deprecated"
227                 // The first 4 annotations are in the android.annotation. package, not
228                 // androidx.annotation
229                 // Nullability annotations are written as @NonNull and @Nullable in API text files,
230                 // and these should be linked no android.annotation package when generating stubs.
231                 source.startsWith("@SystemService") ||
232                     source.startsWith("@TargetApi") ||
233                     source.startsWith("@SuppressLint") ||
234                     source.startsWith("@FlaggedApi") ||
235                     source.startsWith("@Nullable") ||
236                     source.startsWith("@NonNull") -> "@android.annotation." + source.substring(1)
237                 // If the first character of the name (after "@") is lower-case, then
238                 // assume it's a package name, so no need to shorten it.
239                 source.startsWith("@") && source[1].isLowerCase() -> source
240                 else -> {
241                     "@androidx.annotation." + source.substring(1)
242                 }
243             }
244         }
245     }
246 }
247 
248 /** Get the [TypeNullability] from a list of [AnnotationItem]s. */
249 val List<AnnotationItem>.typeNullability
<lambda>null250     get() = mapNotNull { it.typeNullability }.firstOrNull()
251 
252 /**
253  * Get the value of the named attribute as an object of the specified type or null if the attribute
254  * could not be found.
255  *
256  * This can only be called for attributes which have a single value, it will throw an exception if
257  * called for an attribute whose value is any array type. See [getAttributeValues] instead.
258  *
259  * This supports the following types for [T]:
260  * * [String] - the attribute must be of type [String] or [Class].
261  * * [AnnotationItem] - the attribute must be of an annotation type.
262  * * [Boolean] - the attribute must be of type [Boolean].
263  * * [Byte] - the attribute must be of type [Byte].
264  * * [Char] - the attribute must be of type [Char].
265  * * [Double] - the attribute must be of type [Double].
266  * * [Float] - the attribute must be of type [Float].
267  * * [Int] - the attribute must be of type [Int].
268  * * [Long] - the attribute must be of type [Long].
269  * * [Short] - the attribute must be of type [Short].
270  *
271  * Any other types will result in a [ClassCastException].
272  */
getAttributeValuenull273 inline fun <reified T : Any> AnnotationItem.getAttributeValue(name: String): T? {
274     val value = nonInlineGetAttributeValue(T::class, name) ?: return null
275     return value as T
276 }
277 
278 /**
279  * Non-inline portion of functionality needed by [getAttributeValue]; separated to reduce the cost
280  * of inlining [getAttributeValue].
281  */
282 @PublishedApi
nonInlineGetAttributeValuenull283 internal fun AnnotationItem.nonInlineGetAttributeValue(kClass: KClass<*>, name: String): Any? {
284     val attributeValue = findAttribute(name)?.value ?: return null
285     val value =
286         when (attributeValue) {
287             is AnnotationArrayAttributeValue ->
288                 throw IllegalStateException("Annotation attribute is of type array")
289             else -> attributeValue.value()
290         }
291             ?: return null
292 
293     return convertValue(codebase, kClass, value)
294 }
295 
296 /**
297  * Get the values of the named attribute as a list of objects of the specified type or null if the
298  * attribute could not be found.
299  *
300  * This can be used to get the value of an attribute that is either one of the types in
301  * [getAttributeValue] (in which case this returns a list containing a single item), or an array of
302  * one of the types in [getAttributeValue] (in which case this returns a list containing all the
303  * items in the array).
304  */
getAttributeValuesnull305 inline fun <reified T : Any> AnnotationItem.getAttributeValues(name: String): List<T>? {
306     return nonInlineGetAttributeValues(T::class, name) { it as T }
307 }
308 
309 /**
310  * Non-inline portion of functionality needed by [getAttributeValues]; separated to reduce the cost
311  * of inlining [getAttributeValues].
312  */
313 @PublishedApi
nonInlineGetAttributeValuesnull314 internal fun <T : Any> AnnotationItem.nonInlineGetAttributeValues(
315     kClass: KClass<*>,
316     name: String,
317     caster: (Any) -> T
318 ): List<T>? {
319     val attributeValue = findAttribute(name)?.value ?: return null
320     val values =
321         when (attributeValue) {
322             is AnnotationArrayAttributeValue -> attributeValue.values.mapNotNull { it.value() }
323             else -> listOfNotNull(attributeValue.value())
324         }
325 
326     return values.map { caster(convertValue(codebase, kClass, it)) }
327 }
328 
329 /**
330  * Perform some conversions to try and make [value] to be an instance of [kClass].
331  *
332  * This fixes up some known issues with [value] not corresponding to the expected type but otherwise
333  * simply returns the value it is given. It is the caller's responsibility to actually cast the
334  * returned value to the correct type.
335  */
convertValuenull336 private fun convertValue(codebase: Codebase, kClass: KClass<*>, value: Any): Any {
337     // The value stored for number types is not always the same as the type of the annotation
338     // attributes. This is for a number of reasons, e.g.
339     // * In a .class file annotation values are stored in the constant pool and some number types do
340     //   not have their own constant form (or their own array constant form) so are stored as
341     //   instances of a wider type. They need to be converted to the correct type.
342     // * In signature files annotation values are not always stored as the narrowest type, may not
343     //   have a suffix and type information may not always be available when parsing.
344     if (Number::class.java.isAssignableFrom(kClass.java)) {
345         value as Number
346         return when (kClass) {
347             // Byte does have its own constant form but when stored in an array it is stored as an
348             // int.
349             Byte::class -> value.toByte()
350             // DefaultAnnotationValue.create() always reads integers as longs.
351             Int::class -> value.toInt()
352             // DefaultAnnotationValue.create() always reads floating point as doubles.
353             Float::class -> value.toFloat()
354             // Short does not have its own constant form.
355             Short::class -> value.toShort()
356             else -> value
357         }
358     }
359 
360     // TODO: Push down into the model as that is likely to be more efficient.
361     if (kClass == AnnotationItem::class) {
362         return DefaultAnnotationItem.create(codebase, value as String)
363     }
364 
365     return value
366 }
367 
368 /** Default implementation of an annotation item */
369 open class DefaultAnnotationItem
370 /** The primary constructor is private to force sub-classes to use the secondary constructor. */
371 private constructor(
372     override val codebase: Codebase,
373 
374     /** Fully qualified name of the annotation (prior to name mapping) */
375     protected val originalName: String?,
376 
377     /** Fully qualified name of the annotation (after name mapping) */
378     final override val qualifiedName: String?,
379 
380     /** Possibly empty list of attributes. */
381     attributesGetter: () -> List<AnnotationAttribute>,
382 ) : AnnotationItem {
383 
384     /**
385      * This constructor is needed to initialize [qualifiedName] using the [codebase] parameter
386      * instead of the [DefaultAnnotationItem.codebase] property which is overridden by subclasses
387      * and will not be initialized at the time it is used.
388      */
389     constructor(
390         codebase: Codebase,
391         originalName: String?,
392         attributesGetter: () -> List<AnnotationAttribute>,
393     ) : this(
394         codebase,
395         originalName,
396         qualifiedName = codebase.annotationManager.normalizeInputName(originalName),
397         attributesGetter,
398     )
399 
<lambda>null400     override val targets: Set<AnnotationTarget> by lazy {
401         codebase.annotationManager.computeTargets(this, codebase::findClass)
402     }
403 
404     final override val attributes: List<AnnotationAttribute> by lazy(attributesGetter)
405 
406     /** Information that metalava has gathered about this annotation item. */
<lambda>null407     val info: AnnotationInfo by lazy { codebase.annotationManager.getAnnotationInfo(this) }
408 
409     override val typeNullability: TypeNullability?
410         get() = info.typeNullability
411 
isNullnessAnnotationnull412     override fun isNullnessAnnotation(): Boolean {
413         return info.typeNullability != null
414     }
415 
isNullablenull416     override fun isNullable(): Boolean {
417         return info.typeNullability == TypeNullability.NULLABLE
418     }
419 
isNonNullnull420     override fun isNonNull(): Boolean {
421         return info.typeNullability == TypeNullability.NONNULL
422     }
423 
424     override val showability: Showability
425         get() = info.showability
426 
resolvenull427     override fun resolve(): ClassItem? {
428         return codebase.findClass(originalName ?: return null)
429     }
430 
431     /** If this annotation has a typedef annotation associated with it, return it */
findTypedefAnnotationnull432     override fun findTypedefAnnotation(): AnnotationItem? {
433         val className = originalName ?: return null
434         return codebase
435             .findClass(className)
436             ?.modifiers
437             ?.findAnnotation(AnnotationItem::isTypeDefAnnotation)
438     }
439 
isShowAnnotationnull440     override fun isShowAnnotation(): Boolean = info.showability.show()
441 
442     override fun isShowForStubPurposes(): Boolean = info.showability.showForStubsOnly()
443 
444     override fun isHideAnnotation(): Boolean = info.showability.hide()
445 
446     override fun isSuppressCompatibilityAnnotation(): Boolean = info.suppressCompatibility
447 
448     override fun isShowabilityAnnotation(): Boolean = info.showability != Showability.NO_EFFECT
449 
450     override fun equals(other: Any?): Boolean {
451         if (other !is AnnotationItem) return false
452         return qualifiedName == other.qualifiedName && attributes == other.attributes
453     }
454 
hashCodenull455     override fun hashCode(): Int {
456         var result = qualifiedName?.hashCode() ?: 0
457         result = 31 * result + attributes.hashCode()
458         return result
459     }
460 
toSourcenull461     override fun toSource(target: AnnotationTarget, showDefaultAttrs: Boolean): String {
462         val qualifiedName =
463             codebase.annotationManager.normalizeOutputName(qualifiedName, target) ?: return ""
464 
465         return formatAnnotationItem(qualifiedName, attributes)
466     }
467 
toStringnull468     final override fun toString() = toSource()
469 
470     companion object {
471         fun formatAnnotationItem(
472             qualifiedName: String,
473             attributes: List<AnnotationAttribute>,
474         ): String {
475             return buildString {
476                 append("@")
477                 append(qualifiedName)
478                 if (attributes.isNotEmpty()) {
479                     val suppressDefaultAnnotationAttribute = attributes.size == 1
480                     append("(")
481                     attributes.forEachIndexed { i, attribute ->
482                         if (i != 0) {
483                             append(", ")
484                         }
485                         if (
486                             !suppressDefaultAnnotationAttribute ||
487                                 attribute.name != ANNOTATION_ATTR_VALUE
488                         ) {
489                             append(attribute.name)
490                             append("=")
491                         }
492                         append(attribute.value)
493                     }
494                     append(")")
495                 }
496             }
497         }
498 
499         fun create(codebase: Codebase, source: String): AnnotationItem {
500             val index = source.indexOf("(")
501             val originalName =
502                 if (index == -1) source.substring(1) // Strip @
503                 else source.substring(1, index)
504 
505             fun attributes(): List<AnnotationAttribute> =
506                 if (index == -1) {
507                     emptyList()
508                 } else {
509                     DefaultAnnotationAttribute.createList(
510                         source.substring(index + 1, source.lastIndexOf(')'))
511                     )
512                 }
513 
514             return DefaultAnnotationItem(codebase, originalName, ::attributes)
515         }
516 
517         fun create(
518             codebase: Codebase,
519             originalName: String,
520             attributes: List<AnnotationAttribute> = emptyList(),
521             context: Item? = null
522         ): AnnotationItem {
523             val source = formatAnnotationItem(originalName, attributes)
524             return codebase.createAnnotation(source, context)
525         }
526     }
527 }
528 
529 /** The default annotation attribute name when no name is provided. */
530 const val ANNOTATION_ATTR_VALUE = "value"
531 
532 /** An attribute of an annotation, such as "value" */
533 interface AnnotationAttribute {
534     /** The name of the annotation */
535     val name: String
536     /** The annotation value */
537     val value: AnnotationAttributeValue
538 
539     /**
540      * Return all leaf values; this flattens the complication of handling
541      * {@code @SuppressLint("warning")} and {@code @SuppressLint({"warning1","warning2"})
542      */
leafValuesnull543     fun leafValues(): List<AnnotationAttributeValue> {
544         val result = mutableListOf<AnnotationAttributeValue>()
545         AnnotationAttributeValue.addValues(value, result)
546         return result
547     }
548 }
549 
550 const val ANNOTATION_VALUE_FALSE = "false"
551 const val ANNOTATION_VALUE_TRUE = "true"
552 
553 /** An annotation value */
554 interface AnnotationAttributeValue {
555     /** Generates source code for this annotation value */
toSourcenull556     fun toSource(): String
557 
558     /** The value of the annotation */
559     fun value(): Any?
560 
561     /**
562      * If the annotation declaration references a field (or class etc.), return the resolved class
563      */
564     fun resolve(): Item?
565 
566     companion object {
567         fun addValues(
568             value: AnnotationAttributeValue,
569             into: MutableList<AnnotationAttributeValue>
570         ) {
571             if (value is AnnotationArrayAttributeValue) {
572                 for (v in value.values) {
573                     addValues(v, into)
574                 }
575             } else if (value is AnnotationSingleAttributeValue) {
576                 into.add(value)
577             }
578         }
579     }
580 }
581 
582 /** An annotation value (for a single item, not an array) */
583 interface AnnotationSingleAttributeValue : AnnotationAttributeValue {
584     val value: Any?
585 
valuenull586     override fun value() = value
587 }
588 
589 /** An annotation value for an array of items */
590 interface AnnotationArrayAttributeValue : AnnotationAttributeValue {
591     /** The annotation values */
592     val values: List<AnnotationAttributeValue>
593 
594     override fun resolve(): Item? {
595         error("resolve() should not be called on an array value")
596     }
597 
598     override fun value() = values.mapNotNull { it.value() }.toTypedArray()
599 }
600 
601 class DefaultAnnotationAttribute(
602     override val name: String,
603     override val value: AnnotationAttributeValue
604 ) : AnnotationAttribute {
605     companion object {
createnull606         fun create(name: String, value: String): DefaultAnnotationAttribute {
607             return DefaultAnnotationAttribute(name, DefaultAnnotationValue.create(value))
608         }
609 
createListnull610         fun createList(source: String): List<AnnotationAttribute> {
611             val list = mutableListOf<AnnotationAttribute>() // TODO: default size = 2
612             var begin = 0
613             var index = 0
614             val length = source.length
615             while (index < length) {
616                 val c = source[index]
617                 if (c == '{') {
618                     index = findEnd(source, index + 1, length, '}')
619                 } else if (c == '"') {
620                     index = findEnd(source, index + 1, length, '"')
621                 } else if (c == ',') {
622                     addAttribute(list, source, begin, index)
623                     index++
624                     begin = index
625                     continue
626                 } else if (c == ' ' && index == begin) {
627                     begin++
628                 }
629 
630                 index++
631             }
632 
633             if (begin < length) {
634                 addAttribute(list, source, begin, length)
635             }
636 
637             return list
638         }
639 
findEndnull640         private fun findEnd(source: String, from: Int, to: Int, sentinel: Char): Int {
641             var i = from
642             while (i < to) {
643                 val c = source[i]
644                 if (c == '\\') {
645                     i++
646                 } else if (c == sentinel) {
647                     return i
648                 }
649                 i++
650             }
651             return to
652         }
653 
addAttributenull654         private fun addAttribute(
655             list: MutableList<AnnotationAttribute>,
656             source: String,
657             from: Int,
658             to: Int
659         ) {
660             var split = source.indexOf('=', from)
661             if (split >= to) {
662                 split = -1
663             }
664             val name: String
665             val value: String
666             val valueBegin: Int
667             val valueEnd: Int
668             if (split == -1) {
669                 valueBegin = from
670                 valueEnd = to
671                 name = "value"
672             } else {
673                 name = source.substring(from, split).trim()
674                 valueBegin = split + 1
675                 valueEnd = to
676             }
677             value = source.substring(valueBegin, valueEnd).trim()
678             if (!value.isEmpty()) {
679                 list.add(create(name, value))
680             }
681         }
682     }
683 
toStringnull684     override fun toString(): String {
685         return "$name=$value"
686     }
687 
equalsnull688     override fun equals(other: Any?): Boolean {
689         if (other !is AnnotationAttribute) return false
690         return name == other.name && value == other.value
691     }
692 
hashCodenull693     override fun hashCode(): Int {
694         var result = name.hashCode()
695         result = 31 * result + value.hashCode()
696         return result
697     }
698 }
699 
700 abstract class DefaultAnnotationValue(sourceGetter: () -> String) : AnnotationAttributeValue {
701     companion object {
createnull702         fun create(valueSource: String): DefaultAnnotationValue {
703             return if (valueSource.startsWith("{")) { // Array
704                 DefaultAnnotationArrayAttributeValue(
705                     { valueSource },
706                     {
707                         assert(valueSource.startsWith("{") && valueSource.endsWith("}")) {
708                             valueSource
709                         }
710                         valueSource
711                             .substring(1, valueSource.length - 1)
712                             .split(",")
713                             .map { create(it.trim()) }
714                             .toList()
715                     },
716                 )
717             } else {
718                 DefaultAnnotationSingleAttributeValue(
719                     { valueSource },
720                     {
721                         when {
722                             valueSource == ANNOTATION_VALUE_TRUE -> true
723                             valueSource == ANNOTATION_VALUE_FALSE -> false
724                             valueSource.startsWith("\"") -> valueSource.removeSurrounding("\"")
725                             valueSource.startsWith('\'') -> valueSource.removeSurrounding("'")[0]
726                             else ->
727                                 try {
728                                     if (valueSource.contains(".")) {
729                                         valueSource.toDouble()
730                                     } else {
731                                         valueSource.toLong()
732                                     }
733                                 } catch (e: NumberFormatException) {
734                                     valueSource
735                                 }
736                         }
737                     },
738                 )
739             }
740         }
741     }
742 
743     /** The annotation value, expressed as source code */
744     private val valueSource: String by lazy(LazyThreadSafetyMode.NONE, sourceGetter)
745 
toSourcenull746     override fun toSource() = valueSource
747 
748     override fun toString(): String = toSource()
749 }
750 
751 open class DefaultAnnotationSingleAttributeValue(
752     sourceGetter: () -> String,
753     valueGetter: () -> Any?
754 ) : DefaultAnnotationValue(sourceGetter), AnnotationSingleAttributeValue {
755 
756     override val value by lazy(LazyThreadSafetyMode.NONE, valueGetter)
757 
758     override fun resolve(): Item? = null
759 
760     override fun equals(other: Any?): Boolean {
761         if (other !is AnnotationSingleAttributeValue) return false
762         return value == other.value
763     }
764 
765     override fun hashCode(): Int {
766         return value.hashCode()
767     }
768 }
769 
770 class DefaultAnnotationArrayAttributeValue(
771     sourceGetter: () -> String,
772     valuesGetter: () -> List<AnnotationAttributeValue>
773 ) : DefaultAnnotationValue(sourceGetter), AnnotationArrayAttributeValue {
774 
775     override val values by lazy(LazyThreadSafetyMode.NONE, valuesGetter)
776 
equalsnull777     override fun equals(other: Any?): Boolean {
778         if (other !is AnnotationArrayAttributeValue) return false
779         return values == other.values
780     }
781 
hashCodenull782     override fun hashCode(): Int {
783         return values.hashCode()
784     }
785 }
786