Merge "Update TypeConverters API to be more explicit" into androidx-main
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/parser/SqlParser.kt b/room/room-compiler/src/main/kotlin/androidx/room/parser/SqlParser.kt
index a9eeb8c..5241975 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/parser/SqlParser.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/parser/SqlParser.kt
@@ -256,7 +256,7 @@
     REAL,
     BLOB;
 
-    fun getTypeMirrors(env: XProcessingEnv): List<XType> {
+    fun getTypeMirrors(env: XProcessingEnv): List<XType>? {
         return when (this) {
             TEXT -> withBoxedAndNullableTypes(env, CommonTypeNames.STRING)
             INTEGER -> withBoxedAndNullableTypes(
@@ -265,7 +265,7 @@
             )
             REAL -> withBoxedAndNullableTypes(env, TypeName.DOUBLE, TypeName.FLOAT)
             BLOB -> withBoxedAndNullableTypes(env, ArrayTypeName.of(TypeName.BYTE))
-            else -> emptyList()
+            else -> null
         }
     }
 
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
index db3b91e..7af6697 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
@@ -122,9 +122,8 @@
      * first type adapter has the highest priority
      */
     private val columnTypeAdapters: List<ColumnTypeAdapter>,
-
-    private val typeConverterStore: TypeConverterStore,
-
+    @VisibleForTesting
+    internal val typeConverterStore: TypeConverterStore,
     private val builtInConverterFlags: BuiltInConverterFlags
 ) {
 
@@ -177,7 +176,10 @@
                 .forEach(::addTypeConverter)
             return TypeAdapterStore(
                 context = context, columnTypeAdapters = adapters,
-                typeConverterStore = TypeConverterStore(converters),
+                typeConverterStore = TypeConverterStore(
+                    typeConverters = converters,
+                    knownColumnTypes = adapters.map { it.out }
+                ),
                 builtInConverterFlags = builtInConverterFlags
             )
         }
@@ -218,11 +220,6 @@
             add(InstantDeleteOrUpdateMethodBinderProvider(context))
         }
 
-    // type mirrors that be converted into columns w/o an extra converter
-    private val knownColumnTypeMirrors by lazy {
-        columnTypeAdapters.map { it.out }
-    }
-
     /**
      * Searches 1 way to bind a value into a statement.
      */
@@ -239,8 +236,11 @@
         }
 
         fun findTypeConverterAdapter(): ColumnTypeAdapter? {
-            val targetTypes = targetTypeMirrorsFor(affinity)
-            val binder = findTypeConverter(input, targetTypes) ?: return null
+            val targetTypes = affinity?.getTypeMirrors(context.processingEnv)
+            val binder = typeConverterStore.findConverterIntoStatement(
+                input = input,
+                columnTypes = targetTypes
+            ) ?: return null
             // columnAdapter should not be null but we are receiving errors on crash in `first()` so
             // this safeguard allows us to dispatch the real problem to the user (e.g. why we couldn't
             // find the right adapter)
@@ -260,18 +260,6 @@
     }
 
     /**
-     * Returns which entities targets the given affinity.
-     */
-    private fun targetTypeMirrorsFor(affinity: SQLTypeAffinity?): List<XType> {
-        val specifiedTargets = affinity?.getTypeMirrors(context.processingEnv)
-        return if (specifiedTargets == null || specifiedTargets.isEmpty()) {
-            knownColumnTypeMirrors
-        } else {
-            specifiedTargets
-        }
-    }
-
-    /**
      * Searches 1 way to read it from cursor
      */
     fun findCursorValueReader(output: XType, affinity: SQLTypeAffinity?): CursorValueReader? {
@@ -285,8 +273,11 @@
         }
 
         fun findTypeConverterAdapter(): ColumnTypeAdapter? {
-            val targetTypes = targetTypeMirrorsFor(affinity)
-            val converter = findTypeConverter(targetTypes, output) ?: return null
+            val targetTypes = affinity?.getTypeMirrors(context.processingEnv)
+            val converter = typeConverterStore.findConverterFromCursor(
+                columnTypes = targetTypes,
+                output = output
+            ) ?: return null
             return CompositeAdapter(
                 output,
                 getAllColumnAdapters(converter.from).first(), null, converter
@@ -308,14 +299,6 @@
     }
 
     /**
-     * Tries to reverse the converter going through the same nodes, if possible.
-     */
-    @VisibleForTesting
-    fun reverse(converter: TypeConverter): TypeConverter? {
-        return typeConverterStore.reverse(converter)
-    }
-
-    /**
      * Finds a two way converter, if you need 1 way, use findStatementValueBinder or
      * findCursorValueReader.
      */
@@ -333,11 +316,14 @@
         }
 
         fun findTypeConverterAdapter(): ColumnTypeAdapter? {
-            val targetTypes = targetTypeMirrorsFor(affinity)
-            val intoStatement = findTypeConverter(out, targetTypes) ?: return null
+            val targetTypes = affinity?.getTypeMirrors(context.processingEnv)
+            val intoStatement = typeConverterStore.findConverterIntoStatement(
+                input = out,
+                columnTypes = targetTypes
+            ) ?: return null
             // ok found a converter, try the reverse now
-            val fromCursor = reverse(intoStatement)
-                ?: findTypeConverter(intoStatement.to, out) ?: return null
+            val fromCursor = typeConverterStore.reverse(intoStatement)
+                ?: typeConverterStore.findTypeConverter(intoStatement.to, out) ?: return null
             return CompositeAdapter(
                 out, getAllColumnAdapters(intoStatement.to).first(), intoStatement, fromCursor
             )
@@ -377,10 +363,6 @@
         }
     }
 
-    fun findTypeConverter(input: XType, output: XType): TypeConverter? {
-        return typeConverterStore.findTypeConverter(listOf(input), listOf(output))
-    }
-
     fun findDeleteOrUpdateMethodBinder(typeMirror: XType): DeleteOrUpdateMethodBinder {
         return deleteOrUpdateBinderProvider.first {
             it.matches(typeMirror)
@@ -811,14 +793,6 @@
         }
     }
 
-    private fun findTypeConverter(input: XType, outputs: List<XType>): TypeConverter? {
-        return typeConverterStore.findTypeConverter(listOf(input), outputs)
-    }
-
-    private fun findTypeConverter(input: List<XType>, output: XType): TypeConverter? {
-        return typeConverterStore.findTypeConverter(input, listOf(output))
-    }
-
     private fun getAllColumnAdapters(input: XType): List<ColumnTypeAdapter> {
         return columnTypeAdapters.filter {
             input.isSameType(it.out)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeConverterStore.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeConverterStore.kt
index 43ac3e3..c1184c5 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeConverterStore.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeConverterStore.kt
@@ -28,15 +28,58 @@
  * provided type converters.
  */
 class TypeConverterStore(
-    private val typeConverters: List<TypeConverter>
+    /**
+     * Available TypeConverters
+     */
+    private val typeConverters: List<TypeConverter>,
+    /**
+     * List of types that can be saved into db/read from without a converter.
+     */
+    private val knownColumnTypes: List<XType>
 ) {
     /**
-     * Finds a type converter that can conver one of the input valuese to one of the output values.
+     * Finds a [TypeConverter] (might be composite) that can convert the given [input] type into
+     * one of the given [columnTypes]. If [columnTypes] is not specified, targets all
+     * [knownColumnTypes].
+     */
+    fun findConverterIntoStatement(
+        input: XType,
+        columnTypes: List<XType>? = null
+    ) = findTypeConverter(
+        inputs = listOf(input),
+        outputs = columnTypes ?: knownColumnTypes
+    )
+
+    /**
+     * Finds a [TypeConverter] (might be composite) that can convert the given [columnTypes] into
+     * the [output] type. If [columnTypes] is not specified, uses all [knownColumnTypes].
+     */
+    fun findConverterFromCursor(
+        columnTypes: List<XType>? = null,
+        output: XType
+    ) = findTypeConverter(
+        inputs = columnTypes ?: knownColumnTypes,
+        outputs = listOf(output)
+    )
+
+    /**
+     * Finds a [TypeConverter] from [input] to [output].
+     */
+    fun findTypeConverter(
+        input: XType,
+        output: XType
+    ) = findTypeConverter(
+        inputs = listOf(input),
+        outputs = listOf(output)
+    )
+
+    /**
+     * Finds a type converter that can convert one of the input values to one of the output values.
      *
      * When multiple conversion paths are possible, shortest path (least amount of conversion) is
      * preferred.
      */
-    fun findTypeConverter(
+    private fun findTypeConverter(
         inputs: List<XType>,
         outputs: List<XType>
     ): TypeConverter? {
@@ -119,6 +162,7 @@
             }
         }
     }
+
     /**
      * Tries to reverse the converter going through the same nodes, if possible.
      */
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/parser/SQLTypeAffinityTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/parser/SQLTypeAffinityTest.kt
index 9f3f8d6..fff0299 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/parser/SQLTypeAffinityTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/parser/SQLTypeAffinityTest.kt
@@ -49,7 +49,7 @@
             }
 
             val result = SQLTypeAffinity.values().associate {
-                it to it.getTypeMirrors(invocation.processingEnv).map(XType::toSignature)
+                it to it.getTypeMirrors(invocation.processingEnv)?.map(XType::toSignature)
             }
             assertThat(result).containsExactlyEntriesIn(
                 if (invocation.isKsp) KSP_MAPPING
@@ -60,7 +60,7 @@
 
     companion object {
         private val KSP_MAPPING = mapOf(
-            SQLTypeAffinity.NULL to listOf(),
+            SQLTypeAffinity.NULL to null,
             SQLTypeAffinity.TEXT to listOf(
                 "java.lang.String!!",
                 "java.lang.String?"
@@ -89,7 +89,7 @@
             )
         )
         private val JAVAC_MAPPING = mapOf(
-            SQLTypeAffinity.NULL to listOf(),
+            SQLTypeAffinity.NULL to null,
             SQLTypeAffinity.TEXT to listOf(
                 "java.lang.String"
             ),
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
index 8919793..5a958c7 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
@@ -421,12 +421,12 @@
                 )
             )
 
-            val converter = store.findTypeConverter(
+            val converter = store.typeConverterStore.findTypeConverter(
                 binders[0].from,
                 invocation.context.COMMON_TYPES.STRING
             )
             assertThat(converter, notNullValue())
-            assertThat(store.reverse(converter!!), `is`(binders[1]))
+            assertThat(store.typeConverterStore.reverse(converter!!), `is`(binders[1]))
         }
     }
 
@@ -449,12 +449,12 @@
             val stmtBinder = store.findStatementValueBinder(binders[0].from, null)
             assertThat(stmtBinder, notNullValue())
 
-            val converter = store.findTypeConverter(
+            val converter = store.typeConverterStore.findTypeConverter(
                 binders[0].from,
                 invocation.context.COMMON_TYPES.STRING
             )
             assertThat(converter, notNullValue())
-            assertThat(store.reverse(converter!!), nullValue())
+            assertThat(store.typeConverterStore.reverse(converter!!), nullValue())
         }
     }
 
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeConverterStoreTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeConverterStoreTest.kt
index 001b1b2..18c67d2 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeConverterStoreTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeConverterStoreTest.kt
@@ -70,7 +70,7 @@
                 invocation.context,
                 BuiltInConverterFlags.DEFAULT,
                 converters.map(::CustomTypeConverterWrapper)
-            )
+            ).typeConverterStore
 
             fun findConverter(from: String, to: String): String? {
                 val input = invocation.processingEnv.requireType(from)