Merge "Generating Kotlin in DataSourceFactoryQueryResultBinder and PositionalDataSourceQueryResultBinder." into androidx-main
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt b/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt
index a0a0a64..5ae096f 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt
@@ -83,8 +83,7 @@
         XClassName.get("$ROOM_PACKAGE.util", "TableInfo", "Index")
     val FTS_TABLE_INFO = XClassName.get("$ROOM_PACKAGE.util", "FtsTableInfo")
     val VIEW_INFO = XClassName.get("$ROOM_PACKAGE.util", "ViewInfo")
-    val LIMIT_OFFSET_DATA_SOURCE: ClassName =
-        ClassName.get("$ROOM_PACKAGE.paging", "LimitOffsetDataSource")
+    val LIMIT_OFFSET_DATA_SOURCE = XClassName.get("$ROOM_PACKAGE.paging", "LimitOffsetDataSource")
     val DB_UTIL = XClassName.get("$ROOM_PACKAGE.util", "DBUtil")
     val CURSOR_UTIL = XClassName.get("$ROOM_PACKAGE.util", "CursorUtil")
     val MIGRATION = XClassName.get("$ROOM_PACKAGE.migration", "Migration")
@@ -99,8 +98,7 @@
         ClassName.get(PAGING_PACKAGE, "DataSource")
     val POSITIONAL_DATA_SOURCE: ClassName =
         ClassName.get(PAGING_PACKAGE, "PositionalDataSource")
-    val DATA_SOURCE_FACTORY: ClassName =
-        ClassName.get(PAGING_PACKAGE, "DataSource", "Factory")
+    val DATA_SOURCE_FACTORY = XClassName.get(PAGING_PACKAGE, "DataSource", "Factory")
     val PAGING_SOURCE = XClassName.get(PAGING_PACKAGE, "PagingSource")
     val LISTENABLE_FUTURE_PAGING_SOURCE =
         XClassName.get(PAGING_PACKAGE, "ListenableFuturePagingSource")
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/DataSourceFactoryQueryResultBinderProvider.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/DataSourceFactoryQueryResultBinderProvider.kt
index 88dabbe..71827d3 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/DataSourceFactoryQueryResultBinderProvider.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/DataSourceFactoryQueryResultBinderProvider.kt
@@ -31,7 +31,7 @@
 
 class DataSourceFactoryQueryResultBinderProvider(val context: Context) : QueryResultBinderProvider {
     private val dataSourceFactoryType: XRawType? by lazy {
-        context.processingEnv.findType(PagingTypeNames.DATA_SOURCE_FACTORY)?.rawType
+        context.processingEnv.findType(PagingTypeNames.DATA_SOURCE_FACTORY.canonicalName)?.rawType
     }
 
     override fun provide(
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/DataSourceFactoryQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/DataSourceFactoryQueryResultBinder.kt
index e8246ad..ca1736b 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/DataSourceFactoryQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/DataSourceFactoryQueryResultBinder.kt
@@ -16,22 +16,19 @@
 
 package androidx.room.solver.query.result
 
+import androidx.room.compiler.codegen.VisibilityModifier
+import androidx.room.compiler.codegen.XFunSpec
 import androidx.room.compiler.codegen.XPropertySpec
-import androidx.room.ext.L
+import androidx.room.compiler.codegen.XTypeName
+import androidx.room.compiler.codegen.XTypeSpec
 import androidx.room.ext.PagingTypeNames
-import androidx.room.ext.typeName
 import androidx.room.solver.CodeGenScope
-import com.squareup.javapoet.MethodSpec
-import com.squareup.javapoet.ParameterizedTypeName
-import com.squareup.javapoet.TypeName
-import com.squareup.javapoet.TypeSpec
-import javax.lang.model.element.Modifier
 
 class DataSourceFactoryQueryResultBinder(
     val positionalDataSourceQueryResultBinder: PositionalDataSourceQueryResultBinder
 ) : QueryResultBinder(positionalDataSourceQueryResultBinder.listAdapter) {
 
-    val typeName: TypeName = positionalDataSourceQueryResultBinder.itemTypeName
+    val typeName: XTypeName = positionalDataSourceQueryResultBinder.itemTypeName
 
     override fun convertAndReturn(
         roomSQLiteQueryVar: String,
@@ -40,48 +37,51 @@
         inTransaction: Boolean,
         scope: CodeGenScope
     ) {
-        scope.builder().apply {
-            val pagedListProvider = TypeSpec.anonymousClassBuilder("")
+        scope.builder.apply {
+            val pagedListProvider = XTypeSpec.anonymousClassBuilder(language)
                 .apply {
                     superclass(
-                        ParameterizedTypeName.get(
-                            PagingTypeNames.DATA_SOURCE_FACTORY,
-                            Integer::class.typeName,
+                        PagingTypeNames.DATA_SOURCE_FACTORY.parametrizedBy(
+                            XTypeName.BOXED_INT,
                             typeName
                         )
                     )
-
-                    addMethod(
-                        createCreateMethod(
-                            roomSQLiteQueryVar = roomSQLiteQueryVar,
-                            dbProperty = dbProperty,
-                            inTransaction = inTransaction,
-                            scope = scope
-                        )
+                    addCreateMethod(
+                        roomSQLiteQueryVar = roomSQLiteQueryVar,
+                        dbProperty = dbProperty,
+                        inTransaction = inTransaction,
+                        scope = scope
                     )
                 }
                 .build()
-            addStatement("return $L", pagedListProvider)
+            addStatement("return %L", pagedListProvider)
         }
     }
 
-    private fun createCreateMethod(
+    private fun XTypeSpec.Builder.addCreateMethod(
         roomSQLiteQueryVar: String,
         dbProperty: XPropertySpec,
         inTransaction: Boolean,
         scope: CodeGenScope
-    ): MethodSpec = MethodSpec.methodBuilder("create").apply {
-        addAnnotation(Override::class.java)
-        addModifiers(Modifier.PUBLIC)
-        returns(positionalDataSourceQueryResultBinder.typeName)
-        val countedBinderScope = scope.fork()
-        positionalDataSourceQueryResultBinder.convertAndReturn(
-            roomSQLiteQueryVar = roomSQLiteQueryVar,
-            canReleaseQuery = true,
-            dbProperty = dbProperty,
-            inTransaction = inTransaction,
-            scope = countedBinderScope
+    ) {
+        addFunction(
+            XFunSpec.builder(
+                language = language,
+                name = "create",
+                visibility = VisibilityModifier.PUBLIC,
+                isOverride = true
+            ).apply {
+                returns(positionalDataSourceQueryResultBinder.typeName)
+                val countedBinderScope = scope.fork()
+                positionalDataSourceQueryResultBinder.convertAndReturn(
+                    roomSQLiteQueryVar = roomSQLiteQueryVar,
+                    canReleaseQuery = true,
+                    dbProperty = dbProperty,
+                    inTransaction = inTransaction,
+                    scope = countedBinderScope
+                )
+                addCode(countedBinderScope.generate())
+            }.build()
         )
-        addCode(countedBinderScope.builder().build())
-    }.build()
+    }
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt
index d5b08e8..94725a6 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt
@@ -16,20 +16,16 @@
 
 package androidx.room.solver.query.result
 
+import androidx.room.compiler.codegen.VisibilityModifier
+import androidx.room.compiler.codegen.XFunSpec
+import androidx.room.compiler.codegen.XFunSpec.Builder.Companion.addStatement
 import androidx.room.compiler.codegen.XPropertySpec
-import androidx.room.compiler.codegen.toJavaPoet
+import androidx.room.compiler.codegen.XTypeName
+import androidx.room.compiler.codegen.XTypeSpec
 import androidx.room.ext.AndroidTypeNames.CURSOR
 import androidx.room.ext.CommonTypeNames.LIST
-import androidx.room.ext.L
-import androidx.room.ext.N
 import androidx.room.ext.RoomTypeNames
 import androidx.room.solver.CodeGenScope
-import com.squareup.javapoet.MethodSpec
-import com.squareup.javapoet.ParameterSpec
-import com.squareup.javapoet.ParameterizedTypeName
-import com.squareup.javapoet.TypeName
-import com.squareup.javapoet.TypeSpec
-import javax.lang.model.element.Modifier
 
 /**
  * Used by Paging2 pipeline
@@ -38,11 +34,9 @@
     val listAdapter: ListQueryResultAdapter?,
     val tableNames: Set<String>,
 ) : QueryResultBinder(listAdapter) {
-    val itemTypeName: TypeName =
-        listAdapter?.rowAdapters?.firstOrNull()?.out?.typeName ?: TypeName.OBJECT
-    val typeName: ParameterizedTypeName = ParameterizedTypeName.get(
-        RoomTypeNames.LIMIT_OFFSET_DATA_SOURCE, itemTypeName
-    )
+    val itemTypeName: XTypeName =
+        listAdapter?.rowAdapters?.firstOrNull()?.out?.asTypeName() ?: XTypeName.ANY_OBJECT
+    val typeName: XTypeName = RoomTypeNames.LIMIT_OFFSET_DATA_SOURCE.parametrizedBy(itemTypeName)
 
     override fun convertAndReturn(
         roomSQLiteQueryVar: String,
@@ -51,35 +45,45 @@
         inTransaction: Boolean,
         scope: CodeGenScope
     ) {
-        val dbField = dbProperty.toJavaPoet()
         // first comma for table names comes from the string since it might be empty in which case
         // we don't need a comma. If list is empty, this prevents generating bad code (it is still
         // an error to have empty list but that is already reported while item is processed)
         val tableNamesList = tableNames.joinToString("") { ", \"$it\"" }
-        val spec = TypeSpec.anonymousClassBuilder(
-            "$N, $L, $L, $L $L",
-            dbField, roomSQLiteQueryVar, inTransaction, true, tableNamesList
+        val spec = XTypeSpec.anonymousClassBuilder(
+            language = scope.language,
+            "%N, %L, %L, %L%L",
+            dbProperty,
+            roomSQLiteQueryVar,
+            inTransaction,
+            true,
+            tableNamesList
         ).apply {
             superclass(typeName)
-            addMethod(createConvertRowsMethod(scope))
+            addConvertRowsMethod(scope)
         }.build()
-        scope.builder().apply {
-            addStatement("return $L", spec)
-        }
+        scope.builder.addStatement("return %L", spec)
     }
 
-    private fun createConvertRowsMethod(scope: CodeGenScope): MethodSpec =
-        MethodSpec.methodBuilder("convertRows").apply {
-            addAnnotation(Override::class.java)
-            addModifiers(Modifier.PROTECTED)
-            returns(ParameterizedTypeName.get(LIST.toJavaPoet(), itemTypeName))
-            val cursorParam = ParameterSpec.builder(CURSOR.toJavaPoet(), "cursor")
-                .build()
-            addParameter(cursorParam)
-            val resultVar = scope.getTmpVar("_res")
-            val rowsScope = scope.fork()
-            listAdapter?.convert(resultVar, cursorParam.name, rowsScope)
-            addCode(rowsScope.builder().build())
-            addStatement("return $L", resultVar)
-        }.build()
+    private fun XTypeSpec.Builder.addConvertRowsMethod(scope: CodeGenScope) {
+        addFunction(
+            XFunSpec.builder(
+                language = language,
+                name = "convertRows",
+                visibility = VisibilityModifier.PROTECTED,
+                isOverride = true
+            ).apply {
+                returns(LIST.parametrizedBy(itemTypeName))
+                val cursorParamName = "cursor"
+                addParameter(
+                    CURSOR,
+                    cursorParamName
+                )
+                val resultVar = scope.getTmpVar("_res")
+                val rowsScope = scope.fork()
+                listAdapter?.convert(resultVar, cursorParamName, rowsScope)
+                addCode(rowsScope.generate())
+                addStatement("return %L", resultVar)
+            }.build()
+        )
+    }
 }
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
index 4cd4627..2419724 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
@@ -30,6 +30,7 @@
 import androidx.room.compiler.processing.util.runProcessorTest
 import androidx.room.ext.CommonTypeNames
 import androidx.room.ext.CommonTypeNames.LIST
+import androidx.room.ext.CommonTypeNames.STRING
 import androidx.room.ext.GuavaUtilConcurrentTypeNames
 import androidx.room.ext.KotlinTypeNames
 import androidx.room.ext.LifecyclesTypeNames
@@ -646,17 +647,17 @@
         singleQueryMethod<ReadQueryMethod>(
             """
                 @Query("select name from user")
-                abstract ${PagingTypeNames.DATA_SOURCE_FACTORY}<Integer, String>
+                abstract ${PagingTypeNames.DATA_SOURCE_FACTORY.canonicalName}<Integer, String>
                 nameDataSourceFactory();
                 """
         ) { parsedQuery, _ ->
             assertThat(
-                parsedQuery.returnType.typeName,
+                parsedQuery.returnType.asTypeName(),
                 `is`(
-                    ParameterizedTypeName.get(
-                        PagingTypeNames.DATA_SOURCE_FACTORY,
-                        Integer::class.typeName, String::class.typeName
-                    ) as TypeName
+                    PagingTypeNames.DATA_SOURCE_FACTORY.parametrizedBy(
+                        XTypeName.BOXED_INT,
+                        STRING
+                    )
                 )
             )
             assertThat(
@@ -675,17 +676,17 @@
         singleQueryMethod<ReadQueryMethod>(
             """
                 @Query("select name from User u LEFT OUTER JOIN Book b ON u.uid == b.uid")
-                abstract ${PagingTypeNames.DATA_SOURCE_FACTORY}<Integer, String>
+                abstract ${PagingTypeNames.DATA_SOURCE_FACTORY.canonicalName}<Integer, String>
                 nameDataSourceFactory();
                 """
         ) { parsedQuery, _ ->
             assertThat(
-                parsedQuery.returnType.typeName,
+                parsedQuery.returnType.asTypeName(),
                 `is`(
-                    ParameterizedTypeName.get(
-                        PagingTypeNames.DATA_SOURCE_FACTORY,
-                        Integer::class.typeName, String::class.typeName
-                    ) as TypeName
+                    PagingTypeNames.DATA_SOURCE_FACTORY.parametrizedBy(
+                        XTypeName.BOXED_INT,
+                        STRING
+                    )
                 )
             )
             assertThat(
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/RawQueryMethodProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/RawQueryMethodProcessorTest.kt
index 325996b..549ec1c 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/RawQueryMethodProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/RawQueryMethodProcessorTest.kt
@@ -137,7 +137,7 @@
         singleQueryMethod(
             """
                 @RawQuery
-                abstract public ${PagingTypeNames.DATA_SOURCE_FACTORY}<Integer, User> getOne();
+                abstract public ${PagingTypeNames.DATA_SOURCE_FACTORY.canonicalName}<Integer, User> getOne();
                 """
         ) { _, invocation ->
             invocation.assertCompilationResult {
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/testing/test_util.kt b/room/room-compiler/src/test/kotlin/androidx/room/testing/test_util.kt
index 17d7b1a..0cb969c 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/testing/test_util.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/testing/test_util.kt
@@ -29,7 +29,6 @@
 import androidx.room.ext.GuavaUtilConcurrentTypeNames
 import androidx.room.ext.KotlinTypeNames
 import androidx.room.ext.LifecyclesTypeNames
-import androidx.room.ext.PagingTypeNames
 import androidx.room.ext.ReactiveStreamsTypeNames
 import androidx.room.ext.RoomGuavaTypeNames
 import androidx.room.ext.RoomRxJava2TypeNames
@@ -194,13 +193,12 @@
     }
 
     val DATA_SOURCE_FACTORY by lazy {
-        loadJavaCode("common/input/DataSource.java", "androidx.paging.DataSource")
+        loadKotlinCode("common/input/DataSource.kt")
     }
 
     val POSITIONAL_DATA_SOURCE by lazy {
-        loadJavaCode(
-            "common/input/PositionalDataSource.java",
-            PagingTypeNames.POSITIONAL_DATA_SOURCE.toString()
+        loadKotlinCode(
+            "common/input/PositionalDataSource.kt"
         )
     }
 
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt
index 380deea..6a738b3 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt
@@ -1586,4 +1586,38 @@
             expectedFilePath = getTestGoldenPath(testName)
         )
     }
+
+    @Test
+    fun dataSource() {
+        val testName = object {}.javaClass.enclosingMethod!!.name
+        val src = Source.kotlin(
+            "MyDao.kt",
+            """
+            import androidx.room.*
+            import androidx.paging.*
+
+            @Dao
+            abstract class MyDao {
+                @Query("SELECT * from MyEntity")
+                abstract fun getDataSourceFactory(): DataSource.Factory<Int, MyEntity>
+            }
+
+            @Entity
+            data class MyEntity(
+                @PrimaryKey
+                val pk: Int,
+                val other: String
+            )
+            """.trimIndent()
+        )
+        runTest(
+            sources = listOf(
+                src,
+                databaseSrc,
+                COMMON.DATA_SOURCE_FACTORY,
+                COMMON.POSITIONAL_DATA_SOURCE
+            ),
+            expectedFilePath = getTestGoldenPath(testName)
+        )
+    }
 }
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/common/input/DataSource.java b/room/room-compiler/src/test/test-data/common/input/DataSource.kt
similarity index 75%
rename from room/room-compiler/src/test/test-data/common/input/DataSource.java
rename to room/room-compiler/src/test/test-data/common/input/DataSource.kt
index 157750a..6fa77e8 100644
--- a/room/room-compiler/src/test/test-data/common/input/DataSource.java
+++ b/room/room-compiler/src/test/test-data/common/input/DataSource.kt
@@ -13,10 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package androidx.paging
 
-package androidx.paging;
-
-abstract public class DataSource<K, T> {
-    public interface Factory<Key, Value> {
+public abstract class DataSource<Key : Any, Value : Any> {
+    public abstract class Factory<Key : Any, Value : Any> {
+        public abstract fun create(): DataSource<Key, Value>
     }
-}
+}
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/common/input/PositionalDataSource.java b/room/room-compiler/src/test/test-data/common/input/PositionalDataSource.java
deleted file mode 100644
index ff5338a..0000000
--- a/room/room-compiler/src/test/test-data/common/input/PositionalDataSource.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package androidx.paging;
-
-public abstract class PositionalDataSource<T> extends DataSource<Integer, T> {
-
-}
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/common/input/PositionalDataSource.kt b/room/room-compiler/src/test/test-data/common/input/PositionalDataSource.kt
new file mode 100644
index 0000000..a837ed1
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/common/input/PositionalDataSource.kt
@@ -0,0 +1,3 @@
+package androidx.paging
+
+abstract class PositionalDataSource<T : Any> : DataSource<Int, T>()
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/dataSource.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/dataSource.kt
new file mode 100644
index 0000000..25c7425
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/dataSource.kt
@@ -0,0 +1,57 @@
+import android.database.Cursor
+import androidx.paging.DataSource
+import androidx.room.RoomDatabase
+import androidx.room.RoomSQLiteQuery
+import androidx.room.RoomSQLiteQuery.Companion.acquire
+import androidx.room.paging.LimitOffsetDataSource
+import androidx.room.util.getColumnIndexOrThrow
+import java.lang.Class
+import java.util.ArrayList
+import javax.`annotation`.processing.Generated
+import kotlin.Int
+import kotlin.String
+import kotlin.Suppress
+import kotlin.collections.List
+import kotlin.collections.MutableList
+import kotlin.jvm.JvmStatic
+
+@Generated(value = ["androidx.room.RoomProcessor"])
+@Suppress(names = ["UNCHECKED_CAST", "DEPRECATION"])
+public class MyDao_Impl(
+    __db: RoomDatabase,
+) : MyDao() {
+    private val __db: RoomDatabase
+    init {
+        this.__db = __db
+    }
+
+    public override fun getDataSourceFactory(): DataSource.Factory<Int, MyEntity> {
+        val _sql: String = "SELECT * from MyEntity"
+        val _statement: RoomSQLiteQuery = acquire(_sql, 0)
+        return object : DataSource.Factory<Int, MyEntity>() {
+            public override fun create(): LimitOffsetDataSource<MyEntity> = object :
+                LimitOffsetDataSource<MyEntity>(__db, _statement, false, true, "MyEntity") {
+                protected override fun convertRows(cursor: Cursor): List<MyEntity> {
+                    val _cursorIndexOfPk: Int = getColumnIndexOrThrow(cursor, "pk")
+                    val _cursorIndexOfOther: Int = getColumnIndexOrThrow(cursor, "other")
+                    val _res: MutableList<MyEntity> = ArrayList<MyEntity>(cursor.getCount())
+                    while (cursor.moveToNext()) {
+                        val _item: MyEntity
+                        val _tmpPk: Int
+                        _tmpPk = cursor.getInt(_cursorIndexOfPk)
+                        val _tmpOther: String
+                        _tmpOther = cursor.getString(_cursorIndexOfOther)
+                        _item = MyEntity(_tmpPk,_tmpOther)
+                        _res.add(_item)
+                    }
+                    return _res
+                }
+            }
+        }
+    }
+
+    public companion object {
+        @JvmStatic
+        public fun getRequiredConverters(): List<Class<*>> = emptyList()
+    }
+}
\ No newline at end of file