1d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar/* 2d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * Copyright (C) 2016 The Android Open Source Project 3d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * 4d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * Licensed under the Apache License, Version 2.0 (the "License"); 5d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * you may not use this file except in compliance with the License. 6d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * You may obtain a copy of the License at 7d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * 8d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * http://www.apache.org/licenses/LICENSE-2.0 9d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * 10d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * Unless required by applicable law or agreed to in writing, software 11d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * distributed under the License is distributed on an "AS IS" BASIS, 12d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * See the License for the specific language governing permissions and 14d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * limitations under the License. 15d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar */ 16d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar 1764db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarpackage android.arch.persistence.room.writer 18d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar 1964db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.ext.L 2064db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.ext.N 2164db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.ext.RoomTypeNames 2264db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.ext.S 2364db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.ext.SupportDbTypeNames 2464db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.ext.T 2564db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.solver.CodeGenScope 2664db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.vo.Database 2764db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.vo.Entity 28cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Arakiimport android.support.annotation.VisibleForTesting 29d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.squareup.javapoet.MethodSpec 30d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.squareup.javapoet.ParameterSpec 31d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.squareup.javapoet.TypeSpec 32a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyarimport javax.lang.model.element.Modifier.PROTECTED 33d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport javax.lang.model.element.Modifier.PUBLIC 34d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar 35d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar/** 36d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar * Create an open helper using SupportSQLiteOpenHelperFactory 37d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar */ 38d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarclass SQLiteOpenHelperWriter(val database : Database) { 39d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar fun write(outVar : String, configuration : ParameterSpec, scope: CodeGenScope) { 40d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar scope.builder().apply { 41d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar val sqliteConfigVar = scope.getTmpVar("_sqliteConfig") 42d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar val callbackVar = scope.getTmpVar("_openCallback") 43a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar addStatement("final $T $L = new $T($N, $L, $S)", 44d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar SupportDbTypeNames.SQLITE_OPEN_HELPER_CALLBACK, 453a433f7ddbffa6131883cc3b23fc80edf54add19Yigit Boyar callbackVar, RoomTypeNames.OPEN_HELPER, configuration, 463a433f7ddbffa6131883cc3b23fc80edf54add19Yigit Boyar createOpenCallback(scope), database.identityHash) 47d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar // build configuration 48d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addStatement( 49d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar """ 50d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar final $T $L = $T.builder($N.context) 51d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar .name($N.name) 52d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar .callback($L) 53d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar .build() 54d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar """.trimIndent(), 55d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar SupportDbTypeNames.SQLITE_OPEN_HELPER_CONFIG, sqliteConfigVar, 56d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar SupportDbTypeNames.SQLITE_OPEN_HELPER_CONFIG, 572b4a201ee53be9d5e3995e62c76f83c07a1ddfddYigit Boyar configuration, configuration, callbackVar) 58d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addStatement("final $T $N = $N.sqliteOpenHelperFactory.create($L)", 59d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar SupportDbTypeNames.SQLITE_OPEN_HELPER, outVar, 60d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar configuration, sqliteConfigVar) 61d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar } 62d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar } 63d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar 643a433f7ddbffa6131883cc3b23fc80edf54add19Yigit Boyar private fun createOpenCallback(scope: CodeGenScope) : TypeSpec { 652b4a201ee53be9d5e3995e62c76f83c07a1ddfddYigit Boyar return TypeSpec.anonymousClassBuilder(L, database.version).apply { 66a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar superclass(RoomTypeNames.OPEN_HELPER_DELEGATE) 67a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar addMethod(createCreateAllTables()) 68a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar addMethod(createDropAllTables()) 69cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki addMethod(createOnCreate(scope.fork())) 70cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki addMethod(createOnOpen(scope.fork())) 713a433f7ddbffa6131883cc3b23fc80edf54add19Yigit Boyar addMethod(createValidateMigration(scope.fork())) 72a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar }.build() 73a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar } 74a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar 753a433f7ddbffa6131883cc3b23fc80edf54add19Yigit Boyar private fun createValidateMigration(scope: CodeGenScope): MethodSpec { 76a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar return MethodSpec.methodBuilder("validateMigration").apply { 77a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar addModifiers(PROTECTED) 783a433f7ddbffa6131883cc3b23fc80edf54add19Yigit Boyar val dbParam = ParameterSpec.builder(SupportDbTypeNames.DB, "_db").build() 793a433f7ddbffa6131883cc3b23fc80edf54add19Yigit Boyar addParameter(dbParam) 803a433f7ddbffa6131883cc3b23fc80edf54add19Yigit Boyar database.entities.forEach { entity -> 813a433f7ddbffa6131883cc3b23fc80edf54add19Yigit Boyar val methodScope = scope.fork() 823a433f7ddbffa6131883cc3b23fc80edf54add19Yigit Boyar TableInfoValidationWriter(entity).write(dbParam, methodScope) 833a433f7ddbffa6131883cc3b23fc80edf54add19Yigit Boyar addCode(methodScope.builder().build()) 843a433f7ddbffa6131883cc3b23fc80edf54add19Yigit Boyar } 8534e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar }.build() 8634e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar } 8734e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar 88cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki private fun createOnCreate(scope: CodeGenScope): MethodSpec { 89cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki return MethodSpec.methodBuilder("onCreate").apply { 90cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki addModifiers(PROTECTED) 91cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki addParameter(SupportDbTypeNames.DB, "_db") 92cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki invokeCallbacks(scope, "onCreate") 93cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki }.build() 94cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki } 95cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki 96cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki private fun createOnOpen(scope: CodeGenScope): MethodSpec { 9734e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar return MethodSpec.methodBuilder("onOpen").apply { 9834e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar addModifiers(PUBLIC) 9934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar addParameter(SupportDbTypeNames.DB, "_db") 100fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar addStatement("mDatabase = _db") 1010045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar if (database.enableForeignKeys) { 1020045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar addStatement("_db.execSQL($S)", "PRAGMA foreign_keys = ON") 1030045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar } 10434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar addStatement("internalInitInvalidationTracker(_db)") 105cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki invokeCallbacks(scope, "onOpen") 106d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar }.build() 107d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar } 108d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar 109a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar private fun createCreateAllTables() : MethodSpec { 110a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar return MethodSpec.methodBuilder("createAllTables").apply { 111d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addModifiers(PUBLIC) 112d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addParameter(SupportDbTypeNames.DB, "_db") 113a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar database.bundle.buildCreateQueries().forEach { 114a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar addStatement("_db.execSQL($S)", it) 115a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar } 116d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar }.build() 117d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar } 118d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar 119a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar private fun createDropAllTables() : MethodSpec { 120a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar return MethodSpec.methodBuilder("dropAllTables").apply { 121d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addModifiers(PUBLIC) 122d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar addParameter(SupportDbTypeNames.DB, "_db") 123c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar database.entities.forEach { 124c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar addStatement("_db.execSQL($S)", createDropTableQuery(it)) 125c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar } 126d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar }.build() 127d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar } 128d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar 129cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki private fun MethodSpec.Builder.invokeCallbacks(scope: CodeGenScope, methodName: String) { 130cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki val iVar = scope.getTmpVar("_i") 131cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki val sizeVar = scope.getTmpVar("_size") 132cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki beginControlFlow("if (mCallbacks != null)").apply { 133cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki beginControlFlow("for (int $N = 0, $N = mCallbacks.size(); $N < $N; $N++)", 134cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki iVar, sizeVar, iVar, sizeVar, iVar).apply { 135cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki addStatement("mCallbacks.get($N).$N(_db)", iVar, methodName) 136cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki } 137cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki endControlFlow() 138cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki } 139cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki endControlFlow() 140cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki } 141cf4d34906517d8ced296a96e50339c926a7dfdcdYuichi Araki 142c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar @VisibleForTesting 143c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar fun createQuery(entity : Entity) : String { 14488865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar return entity.createTableQuery 145c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar } 146c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar 147c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar @VisibleForTesting 148c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar fun createDropTableQuery(entity: Entity) : String { 149c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar return "DROP TABLE IF EXISTS `${entity.tableName}`" 150c678b3a4eebc102a1a3b5923c5e07478c0eecae3Yigit Boyar } 151d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar} 152