1efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar/*
2efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar * Copyright (C) 2016 The Android Open Source Project
3efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar *
4efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
5efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar * you may not use this file except in compliance with the License.
6efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar * You may obtain a copy of the License at
7efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar *
8efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
9efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar *
10efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar * Unless required by applicable law or agreed to in writing, software
11efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
12efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar * See the License for the specific language governing permissions and
14efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar * limitations under the License.
15efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar */
16efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar
17ba069d50913c3fb250bb60ec310439db36895337Alan Viverettepackage androidx.room.writer
18efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar
19ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.ext.L
20ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.ext.N
21ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.ext.RoomTypeNames
22ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.ext.SupportDbTypeNames
23ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.ext.T
24ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.ext.typeName
25ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.parser.QueryType
26ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.processor.OnConflictProcessor
27ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.solver.CodeGenScope
28ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.vo.Dao
29ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.vo.Entity
30ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.vo.InsertionMethod
31ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.vo.QueryMethod
32ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.vo.RawQueryMethod
33ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.vo.ShortcutMethod
34ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.vo.TransactionMethod
352236f645056249c7b7b7bd9cfb67d8e5256c446dYigit Boyarimport com.google.auto.common.MoreTypes
36efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyarimport com.squareup.javapoet.ClassName
37efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyarimport com.squareup.javapoet.CodeBlock
38efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyarimport com.squareup.javapoet.FieldSpec
39efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyarimport com.squareup.javapoet.MethodSpec
40efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyarimport com.squareup.javapoet.ParameterSpec
414f0db7db556b473393dfc31bba5ea67def574877Yigit Boyarimport com.squareup.javapoet.TypeName
42efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyarimport com.squareup.javapoet.TypeSpec
43470f5b14fe734bf35294404d02bd2304a30d1c4eYigit Boyarimport me.eugeniomarletti.kotlin.metadata.shadow.load.java.JvmAbi
444f0db7db556b473393dfc31bba5ea67def574877Yigit Boyarimport stripNonJava
452236f645056249c7b7b7bd9cfb67d8e5256c446dYigit Boyarimport javax.annotation.processing.ProcessingEnvironment
46efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyarimport javax.lang.model.element.ElementKind
474f0db7db556b473393dfc31bba5ea67def574877Yigit Boyarimport javax.lang.model.element.ExecutableElement
48aa82fce1d73394bdc7f4c2510cf94a3572032b24Yigit Boyarimport javax.lang.model.element.Modifier.FINAL
49aa82fce1d73394bdc7f4c2510cf94a3572032b24Yigit Boyarimport javax.lang.model.element.Modifier.PRIVATE
50aa82fce1d73394bdc7f4c2510cf94a3572032b24Yigit Boyarimport javax.lang.model.element.Modifier.PUBLIC
512236f645056249c7b7b7bd9cfb67d8e5256c446dYigit Boyarimport javax.lang.model.type.DeclaredType
52b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Arakiimport javax.lang.model.type.TypeKind
53efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar
54efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar/**
55efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar * Creates the implementation for a class annotated with Dao.
56efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar */
572236f645056249c7b7b7bd9cfb67d8e5256c446dYigit Boyarclass DaoWriter(val dao: Dao, val processingEnv: ProcessingEnvironment)
582236f645056249c7b7b7bd9cfb67d8e5256c446dYigit Boyar    : ClassWriter(dao.typeName) {
5906baf181601292a02278faf8e628efd2a9f20e4aYigit Boyar    private val declaredDao = MoreTypes.asDeclared(dao.element.asType())
60de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar
61efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar    companion object {
62645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar        // TODO nothing prevents this from conflicting, we should fix.
634f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar        val dbField: FieldSpec = FieldSpec
64efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar                .builder(RoomTypeNames.ROOM_DB, "__db", PRIVATE, FINAL)
65efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar                .build()
66645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar
67645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar        private fun typeNameToFieldName(typeName: TypeName?): String {
68645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar            if (typeName is ClassName) {
69645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar                return typeName.simpleName()
70645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar            } else {
71645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar                return typeName.toString().replace('.', '_').stripNonJava()
72645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar            }
73645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar        }
74efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar    }
75efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar
76645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar    override fun createTypeSpecBuilder(): TypeSpec.Builder {
775ce90e9725b8c9e8fdcef114694d6984a0ef3141Yigit Boyar        val builder = TypeSpec.classBuilder(dao.implTypeName)
783db6d309415576ef0080c29705dc30496636a986Yigit Boyar        /**
7907a5d70dc1f53f9948d61853c4ae84df9e11d7a1Yigit Boyar         * if delete / update query method wants to return modified rows, we need prepared query.
803db6d309415576ef0080c29705dc30496636a986Yigit Boyar         * in that case, if args are dynamic, we cannot re-use the query, if not, we should re-use
813db6d309415576ef0080c29705dc30496636a986Yigit Boyar         * it. this requires more work but creates good performance.
823db6d309415576ef0080c29705dc30496636a986Yigit Boyar         */
8307a5d70dc1f53f9948d61853c4ae84df9e11d7a1Yigit Boyar        val groupedDeleteUpdate = dao.queryMethods
8407a5d70dc1f53f9948d61853c4ae84df9e11d7a1Yigit Boyar                .filter { it.query.type == QueryType.DELETE || it.query.type == QueryType.UPDATE }
853db6d309415576ef0080c29705dc30496636a986Yigit Boyar                .groupBy { it.parameters.any { it.queryParamAdapter?.isMultiple ?: true } }
863db6d309415576ef0080c29705dc30496636a986Yigit Boyar        // delete queries that can be prepared ahead of time
8707a5d70dc1f53f9948d61853c4ae84df9e11d7a1Yigit Boyar        val preparedDeleteOrUpdateQueries = groupedDeleteUpdate[false] ?: emptyList()
883db6d309415576ef0080c29705dc30496636a986Yigit Boyar        // delete queries that must be rebuild every single time
8907a5d70dc1f53f9948d61853c4ae84df9e11d7a1Yigit Boyar        val oneOffDeleteOrUpdateQueries = groupedDeleteUpdate[true] ?: emptyList()
90645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar        val shortcutMethods = createInsertionMethods() +
91b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki                createDeletionMethods() + createUpdateMethods() + createTransactionMethods() +
92645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar                createPreparedDeleteOrUpdateQueries(preparedDeleteOrUpdateQueries)
933db6d309415576ef0080c29705dc30496636a986Yigit Boyar
94efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar        builder.apply {
95efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar            addModifiers(PUBLIC)
96efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar            if (dao.element.kind == ElementKind.INTERFACE) {
97efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar                addSuperinterface(dao.typeName)
98efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar            } else {
99efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar                superclass(dao.typeName)
100efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar            }
101efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar            addField(dbField)
10217caba59e1fd850fe1381d7311d23afc4e07cdfbYuichi Araki            val dbParam = ParameterSpec
10317caba59e1fd850fe1381d7311d23afc4e07cdfbYuichi Araki                    .builder(dao.constructorParamType ?: dbField.type, dbField.name).build()
1044f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar
10517caba59e1fd850fe1381d7311d23afc4e07cdfbYuichi Araki            addMethod(createConstructor(dbParam, shortcutMethods, dao.constructorParamType != null))
1064f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar
107958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar            shortcutMethods.forEach {
108645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar                addMethod(it.methodImpl)
1094f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            }
1104f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar
1113db6d309415576ef0080c29705dc30496636a986Yigit Boyar            dao.queryMethods.filter { it.query.type == QueryType.SELECT }.forEach { method ->
1123db6d309415576ef0080c29705dc30496636a986Yigit Boyar                addMethod(createSelectMethod(method))
1133db6d309415576ef0080c29705dc30496636a986Yigit Boyar            }
11407a5d70dc1f53f9948d61853c4ae84df9e11d7a1Yigit Boyar            oneOffDeleteOrUpdateQueries.forEach {
11507a5d70dc1f53f9948d61853c4ae84df9e11d7a1Yigit Boyar                addMethod(createDeleteOrUpdateQueryMethod(it))
1164f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            }
117de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar            dao.rawQueryMethods.forEach {
118de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                addMethod(createRawQueryMethod(it))
119de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar            }
120efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar        }
121645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar        return builder
122efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar    }
123efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar
1246f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    private fun createPreparedDeleteOrUpdateQueries(
1256f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas            preparedDeleteQueries: List<QueryMethod>): List<PreparedStmtQuery> {
1263db6d309415576ef0080c29705dc30496636a986Yigit Boyar        return preparedDeleteQueries.map { method ->
127fb4fcc8caf2a1908843bd18298447ff6fc498896Yigit Boyar            val fieldSpec = getOrCreateField(PreparedStatementField(method))
1283db6d309415576ef0080c29705dc30496636a986Yigit Boyar            val queryWriter = QueryWriter(method)
129645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar            val fieldImpl = PreparedStatementWriter(queryWriter)
130645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar                    .createAnonymous(this@DaoWriter, dbField)
1313db6d309415576ef0080c29705dc30496636a986Yigit Boyar            val methodBody = createPreparedDeleteQueryMethodBody(method, fieldSpec, queryWriter)
132e088306a4368329d3e00b3da0f4682fc37e54cb8Yuichi Araki            PreparedStmtQuery(mapOf(PreparedStmtQuery.NO_PARAM_FIELD
13386b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                    to (fieldSpec to fieldImpl)), methodBody)
1343db6d309415576ef0080c29705dc30496636a986Yigit Boyar        }
1353db6d309415576ef0080c29705dc30496636a986Yigit Boyar    }
1363db6d309415576ef0080c29705dc30496636a986Yigit Boyar
1376f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    private fun createPreparedDeleteQueryMethodBody(
1386f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas            method: QueryMethod,
1396f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas            preparedStmtField: FieldSpec,
1406f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas            queryWriter: QueryWriter
1416f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    ): MethodSpec {
142645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar        val scope = CodeGenScope(this)
1432236f645056249c7b7b7bd9cfb67d8e5256c446dYigit Boyar        val methodBuilder = overrideWithoutAnnotations(method.element, declaredDao).apply {
1443db6d309415576ef0080c29705dc30496636a986Yigit Boyar            val stmtName = scope.getTmpVar("_stmt")
1453db6d309415576ef0080c29705dc30496636a986Yigit Boyar            addStatement("final $T $L = $N.acquire()",
1463db6d309415576ef0080c29705dc30496636a986Yigit Boyar                    SupportDbTypeNames.SQLITE_STMT, stmtName, preparedStmtField)
14734e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar            addStatement("$N.beginTransaction()", dbField)
1483db6d309415576ef0080c29705dc30496636a986Yigit Boyar            beginControlFlow("try").apply {
1493db6d309415576ef0080c29705dc30496636a986Yigit Boyar                val bindScope = scope.fork()
1503db6d309415576ef0080c29705dc30496636a986Yigit Boyar                queryWriter.bindArgs(stmtName, emptyList(), bindScope)
1513db6d309415576ef0080c29705dc30496636a986Yigit Boyar                addCode(bindScope.builder().build())
15234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                if (method.returnsValue) {
15334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                    val resultVar = scope.getTmpVar("_result")
15434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                    addStatement("final $L $L = $L.executeUpdateDelete()",
15534e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                            method.returnType.typeName(), resultVar, stmtName)
15634e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                    addStatement("$N.setTransactionSuccessful()", dbField)
15734e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                    addStatement("return $L", resultVar)
15834e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                } else {
15934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                    addStatement("$L.executeUpdateDelete()", stmtName)
16034e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                    addStatement("$N.setTransactionSuccessful()", dbField)
16134e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                }
1623db6d309415576ef0080c29705dc30496636a986Yigit Boyar            }
1633db6d309415576ef0080c29705dc30496636a986Yigit Boyar            nextControlFlow("finally").apply {
16434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                addStatement("$N.endTransaction()", dbField)
1653db6d309415576ef0080c29705dc30496636a986Yigit Boyar                addStatement("$N.release($L)", preparedStmtField, stmtName)
1663db6d309415576ef0080c29705dc30496636a986Yigit Boyar            }
1673db6d309415576ef0080c29705dc30496636a986Yigit Boyar            endControlFlow()
1683db6d309415576ef0080c29705dc30496636a986Yigit Boyar        }
1693db6d309415576ef0080c29705dc30496636a986Yigit Boyar        return methodBuilder.build()
1703db6d309415576ef0080c29705dc30496636a986Yigit Boyar    }
1713db6d309415576ef0080c29705dc30496636a986Yigit Boyar
172b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki    private fun createTransactionMethods(): List<PreparedStmtQuery> {
173b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki        return dao.transactionMethods.map {
174b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki            PreparedStmtQuery(emptyMap(), createTransactionMethodBody(it))
175b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki        }
176b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki    }
177b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki
178b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki    private fun createTransactionMethodBody(method: TransactionMethod): MethodSpec {
179b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki        val scope = CodeGenScope(this)
180b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki        val methodBuilder = overrideWithoutAnnotations(method.element, declaredDao).apply {
181b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki            addStatement("$N.beginTransaction()", dbField)
182b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki            beginControlFlow("try").apply {
183b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki                val returnsValue = method.element.returnType.kind != TypeKind.VOID
184b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki                val resultVar = if (returnsValue) {
185b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki                    scope.getTmpVar("_result")
186b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki                } else {
187b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki                    null
188b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki                }
1893b909e309e35eef52ae8d6de906f956a771deb31Yuichi Araki                addDelegateToSuperStatement(method.element, method.callType, resultVar)
190b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki                addStatement("$N.setTransactionSuccessful()", dbField)
191b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki                if (returnsValue) {
192b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki                    addStatement("return $N", resultVar)
193b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki                }
194b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki            }
195b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki            nextControlFlow("finally").apply {
196b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki                addStatement("$N.endTransaction()", dbField)
197b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki            }
198b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki            endControlFlow()
199b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki        }
200b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki        return methodBuilder.build()
201b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki    }
202b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki
203de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar    private fun MethodSpec.Builder.addDelegateToSuperStatement(
204de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar            element: ExecutableElement,
2053b909e309e35eef52ae8d6de906f956a771deb31Yuichi Araki            callType: TransactionMethod.CallType,
206de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar            result: String?) {
207b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki        val params: MutableList<Any> = mutableListOf()
208b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki        val format = buildString {
209b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki            if (result != null) {
210b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki                append("$T $L = ")
211b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki                params.add(element.returnType)
212b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki                params.add(result)
213b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki            }
2143b909e309e35eef52ae8d6de906f956a771deb31Yuichi Araki            when (callType) {
2153b909e309e35eef52ae8d6de906f956a771deb31Yuichi Araki                TransactionMethod.CallType.CONCRETE -> {
2163b909e309e35eef52ae8d6de906f956a771deb31Yuichi Araki                    append("super.$N(")
2173b909e309e35eef52ae8d6de906f956a771deb31Yuichi Araki                    params.add(element.simpleName)
2183b909e309e35eef52ae8d6de906f956a771deb31Yuichi Araki                }
21926549cc5374db3cbd1c1553069a3cfff02ea37adYuichi Araki                TransactionMethod.CallType.DEFAULT_JAVA8 -> {
2203b909e309e35eef52ae8d6de906f956a771deb31Yuichi Araki                    append("$N.super.$N(")
2213b909e309e35eef52ae8d6de906f956a771deb31Yuichi Araki                    params.add(element.enclosingElement.simpleName)
2223b909e309e35eef52ae8d6de906f956a771deb31Yuichi Araki                    params.add(element.simpleName)
2233b909e309e35eef52ae8d6de906f956a771deb31Yuichi Araki                }
22426549cc5374db3cbd1c1553069a3cfff02ea37adYuichi Araki                TransactionMethod.CallType.DEFAULT_KOTLIN -> {
22526549cc5374db3cbd1c1553069a3cfff02ea37adYuichi Araki                    append("$N.$N.$N(this, ")
22626549cc5374db3cbd1c1553069a3cfff02ea37adYuichi Araki                    params.add(element.enclosingElement.simpleName)
22726549cc5374db3cbd1c1553069a3cfff02ea37adYuichi Araki                    params.add(JvmAbi.DEFAULT_IMPLS_CLASS_NAME)
22826549cc5374db3cbd1c1553069a3cfff02ea37adYuichi Araki                    params.add(element.simpleName)
22926549cc5374db3cbd1c1553069a3cfff02ea37adYuichi Araki                }
2303b909e309e35eef52ae8d6de906f956a771deb31Yuichi Araki            }
231b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki            var first = true
232b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki            element.parameters.forEach {
233b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki                if (first) {
234b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki                    first = false
235b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki                } else {
236b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki                    append(", ")
237b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki                }
238b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki                append(L)
239b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki                params.add(it.simpleName)
240b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki            }
241b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki            append(")")
242b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki        }
243b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki        addStatement(format, *params.toTypedArray())
244b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki    }
245b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki
246de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar    private fun createConstructor(
247de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar            dbParam: ParameterSpec,
248de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar            shortcutMethods: List<PreparedStmtQuery>,
249de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar            callSuper: Boolean): MethodSpec {
2504f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar        return MethodSpec.constructorBuilder().apply {
2514f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            addParameter(dbParam)
2524f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            addModifiers(PUBLIC)
25317caba59e1fd850fe1381d7311d23afc4e07cdfbYuichi Araki            if (callSuper) {
25417caba59e1fd850fe1381d7311d23afc4e07cdfbYuichi Araki                addStatement("super($N)", dbParam)
25517caba59e1fd850fe1381d7311d23afc4e07cdfbYuichi Araki            }
2564f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            addStatement("this.$N = $N", dbField, dbParam)
257958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar            shortcutMethods.filterNot {
25886b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                it.fields.isEmpty()
25986b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki            }.map {
26086b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                it.fields.values
26186b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki            }.flatten().groupBy {
26286b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                it.first.name
263645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar            }.map {
264645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar                it.value.first()
2654f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            }.forEach {
26686b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                addStatement("this.$N = $L", it.first, it.second)
2674f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            }
2684f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar        }.build()
2694f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar    }
2704f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar
271645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar    private fun createSelectMethod(method: QueryMethod): MethodSpec {
2722236f645056249c7b7b7bd9cfb67d8e5256c446dYigit Boyar        return overrideWithoutAnnotations(method.element, declaredDao).apply {
2734f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            addCode(createQueryMethodBody(method))
2744f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar        }.build()
2754f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar    }
2764f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar
277de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar    private fun createRawQueryMethod(method: RawQueryMethod): MethodSpec {
278de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar        return overrideWithoutAnnotations(method.element, declaredDao).apply {
279de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar            val scope = CodeGenScope(this@DaoWriter)
280de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar            val roomSQLiteQueryVar: String
281de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar            val queryParam = method.runtimeQueryParam
282de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar            val shouldReleaseQuery: Boolean
283de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar
284de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar            when {
285de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                queryParam?.isString() == true -> {
286de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                    roomSQLiteQueryVar = scope.getTmpVar("_statement")
287de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                    shouldReleaseQuery = true
288de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                    addStatement("$T $L = $T.acquire($L, 0)",
289de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                            RoomTypeNames.ROOM_SQL_QUERY,
290de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                            roomSQLiteQueryVar,
291de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                            RoomTypeNames.ROOM_SQL_QUERY,
292de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                            queryParam.paramName)
293de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                }
294de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                queryParam?.isSupportQuery() == true -> {
295de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                    shouldReleaseQuery = false
296533bde46114bae04323d2a7c29fdba3829713a20Yigit Boyar                    roomSQLiteQueryVar = scope.getTmpVar("_internalQuery")
297533bde46114bae04323d2a7c29fdba3829713a20Yigit Boyar                    // move it to a final variable so that the generated code can use it inside
298533bde46114bae04323d2a7c29fdba3829713a20Yigit Boyar                    // callback blocks in java 7
299533bde46114bae04323d2a7c29fdba3829713a20Yigit Boyar                    addStatement("final $T $L = $N",
300533bde46114bae04323d2a7c29fdba3829713a20Yigit Boyar                            queryParam.type,
301533bde46114bae04323d2a7c29fdba3829713a20Yigit Boyar                            roomSQLiteQueryVar,
302533bde46114bae04323d2a7c29fdba3829713a20Yigit Boyar                            queryParam.paramName)
303de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                }
304de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                else -> {
305de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                    // try to generate compiling code. we would've already reported this error
306de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                    roomSQLiteQueryVar = scope.getTmpVar("_statement")
307de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                    shouldReleaseQuery = false
308de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                    addStatement("$T $L = $T.acquire($L, 0)",
309de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                            RoomTypeNames.ROOM_SQL_QUERY,
310de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                            roomSQLiteQueryVar,
311de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                            RoomTypeNames.ROOM_SQL_QUERY,
312de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                            "missing query parameter")
313de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                }
314de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar            }
315de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar            if (method.returnsValue) {
316de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                // don't generate code because it will create 1 more error. The original error is
317de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                // already reported by the processor.
318de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                method.queryResultBinder.convertAndReturn(
319de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                        roomSQLiteQueryVar = roomSQLiteQueryVar,
320de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                        canReleaseQuery = shouldReleaseQuery,
321de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                        dbField = dbField,
322de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                        inTransaction = method.inTransaction,
323de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                        scope = scope)
324de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar            }
325de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar            addCode(scope.builder().build())
326de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar        }.build()
327de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar    }
328de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar
329645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar    private fun createDeleteOrUpdateQueryMethod(method: QueryMethod): MethodSpec {
3302236f645056249c7b7b7bd9cfb67d8e5256c446dYigit Boyar        return overrideWithoutAnnotations(method.element, declaredDao).apply {
33107a5d70dc1f53f9948d61853c4ae84df9e11d7a1Yigit Boyar            addCode(createDeleteOrUpdateQueryMethodBody(method))
3323db6d309415576ef0080c29705dc30496636a986Yigit Boyar        }.build()
3333db6d309415576ef0080c29705dc30496636a986Yigit Boyar    }
3343db6d309415576ef0080c29705dc30496636a986Yigit Boyar
3354f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar    /**
3364f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar     * Groups all insertion methods based on the insert statement they will use then creates all
3374f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar     * field specs, EntityInsertionAdapterWriter and actual insert methods.
3384f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar     */
339645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar    private fun createInsertionMethods(): List<PreparedStmtQuery> {
3404f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar        return dao.insertionMethods
341645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar                .map { insertionMethod ->
34274b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar                    val onConflict = OnConflictProcessor.onConflictText(insertionMethod.onConflict)
34386b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                    val entities = insertionMethod.entities
344645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar
34586b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                    val fields = entities.mapValues {
34686b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                        val spec = getOrCreateField(InsertionMethodField(it.value, onConflict))
34786b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                        val impl = EntityInsertionAdapterWriter(it.value, onConflict)
348645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar                                .createAnonymous(this@DaoWriter, dbField.name)
34986b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                        spec to impl
350645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar                    }
35186b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                    val methodImpl = overrideWithoutAnnotations(insertionMethod.element,
35286b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                            declaredDao).apply {
35386b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                        addCode(createInsertionMethodBody(insertionMethod, fields))
35486b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                    }.build()
35586b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                    PreparedStmtQuery(fields, methodImpl)
356c21394d1dcb24518061aabde879baff891a426e3Sergey Vasilinets                }
3574f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar    }
3584f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar
3596f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    private fun createInsertionMethodBody(
3606f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas            method: InsertionMethod,
3616f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas            insertionAdapters: Map<String, Pair<FieldSpec, TypeSpec>>
3626f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    ): CodeBlock {
3634f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar        val insertionType = method.insertionType
36486b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki        if (insertionAdapters.isEmpty() || insertionType == null) {
3654f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            return CodeBlock.builder().build()
3664f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar        }
367645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar        val scope = CodeGenScope(this)
3684f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar
3694f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar        return scope.builder().apply {
3704f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            // TODO assert thread
3714f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            // TODO collect results
3724f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            addStatement("$N.beginTransaction()", dbField)
373f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar            val needsReturnType = insertionType != InsertionMethod.Type.INSERT_VOID
374f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar            val resultVar = if (needsReturnType) {
375f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar                scope.getTmpVar("_result")
376f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar            } else {
377f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar                null
378f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar            }
379f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar
3804f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            beginControlFlow("try").apply {
3814f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar                method.parameters.forEach { param ->
38286b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                    val insertionAdapter = insertionAdapters[param.name]?.first
383f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar                    if (needsReturnType) {
384f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar                        // if it has more than 1 parameter, we would've already printed the error
385f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar                        // so we don't care about re-declaring the variable here
386f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar                        addStatement("$T $L = $N.$L($L)",
387f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar                                insertionType.returnTypeName, resultVar,
388f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar                                insertionAdapter, insertionType.methodName,
389f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar                                param.name)
390f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar                    } else {
391f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar                        addStatement("$N.$L($L)", insertionAdapter, insertionType.methodName,
392f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar                                param.name)
393f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar                    }
3944f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar                }
3954f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar                addStatement("$N.setTransactionSuccessful()", dbField)
396f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar                if (needsReturnType) {
397f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar                    addStatement("return $L", resultVar)
398f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar                }
3994f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            }
4004f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            nextControlFlow("finally").apply {
4014f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar                addStatement("$N.endTransaction()", dbField)
4024f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            }
4034f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            endControlFlow()
4044f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar        }.build()
4054f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar    }
4064f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar
407958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar    /**
40874b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar     * Creates EntityUpdateAdapter for each deletion method.
409958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar     */
410645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar    private fun createDeletionMethods(): List<PreparedStmtQuery> {
411d2649c45a34db46fb03a24dcb2a443a92298b5adSergey Vasilinets        return createShortcutMethods(dao.deletionMethods, "deletion", { _, entity ->
41274b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar            EntityDeletionAdapterWriter(entity)
41374b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar                    .createAnonymous(this@DaoWriter, dbField.name)
41474b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar        })
41574b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar    }
416645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar
41774b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar    /**
41874b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar     * Creates EntityUpdateAdapter for each @Update method.
41974b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar     */
42074b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar    private fun createUpdateMethods(): List<PreparedStmtQuery> {
42174b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar        return createShortcutMethods(dao.updateMethods, "update", { update, entity ->
42274b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar            val onConflict = OnConflictProcessor.onConflictText(update.onConflictStrategy)
42374b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar            EntityUpdateAdapterWriter(entity, onConflict)
42474b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar                    .createAnonymous(this@DaoWriter, dbField.name)
42574b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar        })
42674b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar    }
427645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar
4286f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    private fun <T : ShortcutMethod> createShortcutMethods(
4296f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas            methods: List<T>, methodPrefix: String,
4306f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas            implCallback: (T, Entity) -> TypeSpec
4316f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    ): List<PreparedStmtQuery> {
432c21394d1dcb24518061aabde879baff891a426e3Sergey Vasilinets        return methods.mapNotNull { method ->
43386b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki            val entities = method.entities
43474b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar
43586b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki            if (entities.isEmpty()) {
43674b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar                null
43774b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar            } else {
43886b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                val fields = entities.mapValues {
43986b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                    val spec = getOrCreateField(DeleteOrUpdateAdapterField(it.value, methodPrefix))
44086b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                    val impl = implCallback(method, it.value)
44186b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                    spec to impl
44286b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                }
4432236f645056249c7b7b7bd9cfb67d8e5256c446dYigit Boyar                val methodSpec = overrideWithoutAnnotations(method.element, declaredDao).apply {
44486b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                    addCode(createDeleteOrUpdateMethodBody(method, fields))
44574b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar                }.build()
44686b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                PreparedStmtQuery(fields, methodSpec)
44774b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar            }
448c21394d1dcb24518061aabde879baff891a426e3Sergey Vasilinets        }
449958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar    }
450958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar
4516f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    private fun createDeleteOrUpdateMethodBody(
4526f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas            method: ShortcutMethod,
4536f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas            adapters: Map<String, Pair<FieldSpec, TypeSpec>>
4546f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    ): CodeBlock {
45586b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki        if (adapters.isEmpty()) {
456958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar            return CodeBlock.builder().build()
457958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar        }
458645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar        val scope = CodeGenScope(this)
459958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar        val resultVar = if (method.returnCount) {
460958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar            scope.getTmpVar("_total")
461958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar        } else {
462958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar            null
463958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar        }
464958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar        return scope.builder().apply {
465958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar            if (resultVar != null) {
466958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar                addStatement("$T $L = 0", TypeName.INT, resultVar)
467958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar            }
468958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar            addStatement("$N.beginTransaction()", dbField)
469958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar            beginControlFlow("try").apply {
470958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar                method.parameters.forEach { param ->
47186b3f8d9bd637749668174e0736fe9fbecbcfb09Yuichi Araki                    val adapter = adapters[param.name]?.first
472958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar                    addStatement("$L$N.$L($L)",
473958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar                            if (resultVar == null) "" else "$resultVar +=",
47474b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar                            adapter, param.handleMethodName(), param.name)
475958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar                }
476958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar                addStatement("$N.setTransactionSuccessful()", dbField)
477958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar                if (resultVar != null) {
478958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar                    addStatement("return $L", resultVar)
479958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar                }
480958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar            }
481958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar            nextControlFlow("finally").apply {
482958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar                addStatement("$N.endTransaction()", dbField)
483958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar            }
484958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar            endControlFlow()
485958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar        }.build()
486958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar    }
487958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar
4883db6d309415576ef0080c29705dc30496636a986Yigit Boyar    /**
4893db6d309415576ef0080c29705dc30496636a986Yigit Boyar     * @Query with delete action
4903db6d309415576ef0080c29705dc30496636a986Yigit Boyar     */
49107a5d70dc1f53f9948d61853c4ae84df9e11d7a1Yigit Boyar    private fun createDeleteOrUpdateQueryMethodBody(method: QueryMethod): CodeBlock {
4923db6d309415576ef0080c29705dc30496636a986Yigit Boyar        val queryWriter = QueryWriter(method)
493645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar        val scope = CodeGenScope(this)
4943db6d309415576ef0080c29705dc30496636a986Yigit Boyar        val sqlVar = scope.getTmpVar("_sql")
4953db6d309415576ef0080c29705dc30496636a986Yigit Boyar        val stmtVar = scope.getTmpVar("_stmt")
496d9a1c84670c4f684943c23efc8813c20ef965ecaYigit Boyar        val listSizeArgs = queryWriter.prepareQuery(sqlVar, scope)
4973db6d309415576ef0080c29705dc30496636a986Yigit Boyar        scope.builder().apply {
4983db6d309415576ef0080c29705dc30496636a986Yigit Boyar            addStatement("$T $L = $N.compileStatement($L)",
4993db6d309415576ef0080c29705dc30496636a986Yigit Boyar                    SupportDbTypeNames.SQLITE_STMT, stmtVar, dbField, sqlVar)
500d9a1c84670c4f684943c23efc8813c20ef965ecaYigit Boyar            queryWriter.bindArgs(stmtVar, listSizeArgs, scope)
50134e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar            addStatement("$N.beginTransaction()", dbField)
50234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar            beginControlFlow("try").apply {
50334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                if (method.returnsValue) {
50434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                    val resultVar = scope.getTmpVar("_result")
50534e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                    addStatement("final $L $L = $L.executeUpdateDelete()",
50634e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                            method.returnType.typeName(), resultVar, stmtVar)
50734e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                    addStatement("$N.setTransactionSuccessful()", dbField)
50834e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                    addStatement("return $L", resultVar)
50934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                } else {
51034e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                    addStatement("$L.executeUpdateDelete()", stmtVar)
51134e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                    addStatement("$N.setTransactionSuccessful()", dbField)
51234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                }
51334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar            }
51434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar            nextControlFlow("finally").apply {
51534e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                addStatement("$N.endTransaction()", dbField)
51634e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar            }
51734e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar            endControlFlow()
5183db6d309415576ef0080c29705dc30496636a986Yigit Boyar        }
5193db6d309415576ef0080c29705dc30496636a986Yigit Boyar        return scope.builder().build()
5203db6d309415576ef0080c29705dc30496636a986Yigit Boyar    }
5213db6d309415576ef0080c29705dc30496636a986Yigit Boyar
5224f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar    private fun createQueryMethodBody(method: QueryMethod): CodeBlock {
523efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar        val queryWriter = QueryWriter(method)
524645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar        val scope = CodeGenScope(this)
525efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar        val sqlVar = scope.getTmpVar("_sql")
526b030dcb5b7a62854c0bfe85bf04eaf60caeb83bbYigit Boyar        val roomSQLiteQueryVar = scope.getTmpVar("_statement")
527b030dcb5b7a62854c0bfe85bf04eaf60caeb83bbYigit Boyar        queryWriter.prepareReadAndBind(sqlVar, roomSQLiteQueryVar, scope)
52825b465c796ebee5bd7d304becbcf6a42fed53056Yigit Boyar        method.queryResultBinder.convertAndReturn(
52925b465c796ebee5bd7d304becbcf6a42fed53056Yigit Boyar                roomSQLiteQueryVar = roomSQLiteQueryVar,
530de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar                canReleaseQuery = true,
53125b465c796ebee5bd7d304becbcf6a42fed53056Yigit Boyar                dbField = dbField,
53225b465c796ebee5bd7d304becbcf6a42fed53056Yigit Boyar                inTransaction = method.inTransaction,
53325b465c796ebee5bd7d304becbcf6a42fed53056Yigit Boyar                scope = scope)
534efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar        return scope.builder().build()
535efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar    }
5364f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar
537de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar    private fun overrideWithoutAnnotations(
538de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar            elm: ExecutableElement,
539de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar            owner: DeclaredType): MethodSpec.Builder {
5402236f645056249c7b7b7bd9cfb67d8e5256c446dYigit Boyar        val baseSpec = MethodSpec.overriding(elm, owner, processingEnv.typeUtils).build()
5414f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar        return MethodSpec.methodBuilder(baseSpec.name).apply {
5424f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            addAnnotation(Override::class.java)
5434f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            addModifiers(baseSpec.modifiers)
5444f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            addParameters(baseSpec.parameters)
5454f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            varargs(baseSpec.varargs)
5464f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            returns(baseSpec.returnType)
5474f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar        }
5484f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar    }
5494f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar
550e088306a4368329d3e00b3da0f4682fc37e54cb8Yuichi Araki    /**
551e088306a4368329d3e00b3da0f4682fc37e54cb8Yuichi Araki     * Represents a query statement prepared in Dao implementation.
552e088306a4368329d3e00b3da0f4682fc37e54cb8Yuichi Araki     *
553e088306a4368329d3e00b3da0f4682fc37e54cb8Yuichi Araki     * @param fields This map holds all the member fields necessary for this query. The key is the
554e088306a4368329d3e00b3da0f4682fc37e54cb8Yuichi Araki     * corresponding parameter name in the defining query method. The value is a pair from the field
555e088306a4368329d3e00b3da0f4682fc37e54cb8Yuichi Araki     * declaration to definition.
556e088306a4368329d3e00b3da0f4682fc37e54cb8Yuichi Araki     * @param methodImpl The body of the query method implementation.
557e088306a4368329d3e00b3da0f4682fc37e54cb8Yuichi Araki     */
558de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar    data class PreparedStmtQuery(
559de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar            val fields: Map<String, Pair<FieldSpec, TypeSpec>>,
560de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar            val methodImpl: MethodSpec) {
561e088306a4368329d3e00b3da0f4682fc37e54cb8Yuichi Araki        companion object {
562e088306a4368329d3e00b3da0f4682fc37e54cb8Yuichi Araki            // The key to be used in `fields` where the method requires a field that is not
563e088306a4368329d3e00b3da0f4682fc37e54cb8Yuichi Araki            // associated with any of its parameters
564e088306a4368329d3e00b3da0f4682fc37e54cb8Yuichi Araki            const val NO_PARAM_FIELD = "-"
565e088306a4368329d3e00b3da0f4682fc37e54cb8Yuichi Araki        }
566e088306a4368329d3e00b3da0f4682fc37e54cb8Yuichi Araki    }
567645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar
568645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar    private class InsertionMethodField(val entity: Entity, val onConflictText: String)
569645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar        : SharedFieldSpec(
570645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar            "insertionAdapterOf${Companion.typeNameToFieldName(entity.typeName)}",
571645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar            RoomTypeNames.INSERTION_ADAPTER) {
572645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar
573645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar        override fun getUniqueKey(): String {
574645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar            return "${entity.typeName} $onConflictText"
575645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar        }
576645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar
577275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        override fun prepare(writer: ClassWriter, builder: FieldSpec.Builder) {
578645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar            builder.addModifiers(FINAL, PRIVATE)
5794f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar        }
5804f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar    }
5814f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar
58274b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar    class DeleteOrUpdateAdapterField(val entity: Entity, val methodPrefix: String)
58374b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar        : SharedFieldSpec(
58474b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar            "${methodPrefix}AdapterOf${Companion.typeNameToFieldName(entity.typeName)}",
585645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar            RoomTypeNames.DELETE_OR_UPDATE_ADAPTER) {
586275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        override fun prepare(writer: ClassWriter, builder: FieldSpec.Builder) {
587645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar            builder.addModifiers(PRIVATE, FINAL)
588645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar        }
589645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar
590645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar        override fun getUniqueKey(): String {
59174b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar            return entity.typeName.toString() + methodPrefix
592645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar        }
593645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar    }
594645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar
595645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar    class PreparedStatementField(val method: QueryMethod) : SharedFieldSpec(
596645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar            "preparedStmtOf${method.name.capitalize()}", RoomTypeNames.SHARED_SQLITE_STMT) {
597275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        override fun prepare(writer: ClassWriter, builder: FieldSpec.Builder) {
598645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar            builder.addModifiers(PRIVATE, FINAL)
599645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar        }
600645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar
601645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar        override fun getUniqueKey(): String {
602645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar            return method.query.original
603645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar        }
604645abf12d5a13dae5c2271cedd0563a580871a2bYigit Boyar    }
605efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar}
606