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