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