Merge "Expose a list of default values for XAnnotation" into androidx-main
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XAnnotation.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XAnnotation.kt
index a0e190e..3df0f9f 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XAnnotation.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XAnnotation.kt
@@ -74,6 +74,9 @@
     /** All values declared in the annotation class. */
     val annotationValues: List<XAnnotationValue>
 
+    /** All default values declared in the annotation class. */
+    val defaultValues: List<XAnnotationValue>
+
     /** Returns the value of the given [methodName] as a type reference. */
     fun getAsType(methodName: String): XType = getAnnotationValue(methodName).asType()
 
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacAnnotation.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacAnnotation.kt
index a1cd205..1582a0e 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacAnnotation.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacAnnotation.kt
@@ -21,7 +21,6 @@
 import androidx.room.compiler.processing.XAnnotationValue
 import androidx.room.compiler.processing.XNullability
 import androidx.room.compiler.processing.XType
-import androidx.room.compiler.processing.compat.XConverters.toXProcessing
 import com.google.auto.common.AnnotationMirrors
 import com.google.auto.common.MoreTypes
 import javax.lang.model.element.AnnotationMirror
@@ -49,10 +48,23 @@
         annotationValues.filter { explicitValues.contains(it.name) }
     }
 
+    override val defaultValues: List<XAnnotationValue> by lazy {
+        annotationValues.mapNotNull {
+            val method = (it as JavacAnnotationValue).method
+            method.element.getDefaultValue()?.let { value ->
+                JavacAnnotationValue(env, method, value)
+            }
+        }
+    }
+
     override val annotationValues: List<XAnnotationValue> by lazy {
         AnnotationMirrors.getAnnotationValuesWithDefaults(mirror)
             .map { (executableElement, annotationValue) ->
-                annotationValue.toXProcessing(executableElement, env)
+                JavacAnnotationValue(
+                    env,
+                    env.wrapExecutableElement(executableElement) as JavacMethodElement,
+                    annotationValue
+                )
             }
     }
 
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacAnnotationValue.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacAnnotationValue.kt
index c4eb412..fb9cdaa 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacAnnotationValue.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacAnnotationValue.kt
@@ -31,7 +31,7 @@
 
 internal class JavacAnnotationValue(
     val env: JavacProcessingEnv,
-    private val method: JavacMethodElement,
+    val method: JavacMethodElement,
     val annotationValue: AnnotationValue,
     override val valueType: XType = method.returnType,
     private val valueProvider: () -> Any? = {
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacKmAnnotation.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacKmAnnotation.kt
index 00cafd2..023311f 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacKmAnnotation.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacKmAnnotation.kt
@@ -67,4 +67,8 @@
             }
         }
     }
+
+    override val defaultValues: List<XAnnotationValue> by lazy {
+        typeElement.getDeclaredMethods().mapNotNull { it.defaultValue }
+    }
 }
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotation.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotation.kt
index 2012535..1e244b3 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotation.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotation.kt
@@ -23,6 +23,7 @@
 import com.google.devtools.ksp.getConstructors
 import com.google.devtools.ksp.symbol.KSAnnotation
 import com.google.devtools.ksp.symbol.KSType
+import com.google.devtools.ksp.symbol.KSValueArgument
 import com.google.devtools.ksp.symbol.Origin
 
 internal class KspAnnotation(
@@ -50,32 +51,20 @@
         }
     }
 
+    override val defaultValues: List<XAnnotationValue> by lazy {
+        wrap(ksAnnotated.defaultArguments)
+    }
+
     override val annotationValues: List<XAnnotationValue> by lazy {
-        // Whether the annotation value is being treated as property or abstract method depends on
-        // the actual usage of the annotation. If the annotation is being used on Java source, then
-        // the annotation value will have a corresponding method element, otherwise, it will become
-        // a kotlin property.
-        val typesByName =
-            buildMap {
-                typeElement.getDeclaredMethods()
-                    .filter {
-                        if ((typeElement as KspTypeElement).declaration
-                                .getConstructors()
-                                .single().parameters
-                                .isNotEmpty()) {
-                            it.isKotlinPropertyMethod()
-                        } else {
-                            it.isAbstract()
-                        }
-                    }.forEach {
-                        put(it.name, it.returnType)
-                        put(it.jvmName, it.returnType)
-                    }
-            }
-        // KSAnnotated.arguments isn't guaranteed to have the same ordering as declared in the
-        // annotation declaration, so we order it manually using a map from name to index.
+        wrap(ksAnnotated.arguments)
+    }
+
+    private fun wrap(source: List<KSValueArgument>): List<XAnnotationValue> {
+        // KSAnnotated.arguments / KSAnnotated.defaultArguments isn't guaranteed to have the same
+        // ordering as declared in the annotation declaration, so we order it manually using a map
+        // from name to index.
         val indexByName = typesByName.keys.mapIndexed { index, name -> name to index }.toMap()
-        ksAnnotated.arguments.map {
+        return source.map {
             val valueName = it.name?.asString()
                 ?: error("Value argument $it does not have a name.")
             val valueType = typesByName[valueName]
@@ -84,6 +73,31 @@
         }.sortedBy { indexByName[it.name] }
     }
 
+    // A map of annotation value name to type.
+    private val typesByName: Map<String, XType> by lazy {
+        buildMap {
+            typeElement.getDeclaredMethods()
+                .filter {
+                      // Whether the annotation value is being treated as property or
+                      // abstract method depends on the actual usage of the annotation.
+                      // If the annotation is being used on Java source, then the annotation
+                      // value will have a corresponding method element, otherwise, it
+                      // will become a kotlin property.
+                    if ((typeElement as KspTypeElement).declaration
+                            .getConstructors()
+                            .single().parameters
+                            .isNotEmpty()) {
+                        it.isKotlinPropertyMethod()
+                    } else {
+                        it.isAbstract()
+                    }
+                }.forEach {
+                    put(it.name, it.returnType)
+                    put(it.jvmName, it.returnType)
+                }
+            }
+    }
+
     override fun <T : Annotation> asAnnotationBox(annotationClass: Class<T>): XAnnotationBox<T> {
         return KspAnnotationBox(
             env = env,
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
index 2953253..71d5c12 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
@@ -708,6 +708,20 @@
                 }.forEach { typeElement ->
                     val annotation = typeElement.requireAnnotation<JavaAnnotationWithDefaults>()
 
+                    assertThat(annotation.defaultValues.map { it.name })
+                        .containsExactly(
+                            "stringVal",
+                            "stringArrayVal",
+                            "typeVal",
+                            "typeArrayVal",
+                            "intVal",
+                            "intArrayVal",
+                            "enumVal",
+                            "enumArrayVal",
+                            "otherAnnotationVal",
+                            "otherAnnotationArrayVal"
+                        ).inOrder()
+
                     assertThat(annotation.get<Int>("intVal"))
                         .isEqualTo(3)
                     assertThat(annotation.get<List<Int>>("intArrayVal"))