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