Home | History | Annotate | Download | only in writer
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package androidx.room.writer
     18 
     19 import androidx.annotation.VisibleForTesting
     20 import androidx.room.ext.L
     21 import androidx.room.ext.N
     22 import androidx.room.ext.RoomTypeNames
     23 import androidx.room.ext.S
     24 import androidx.room.ext.SupportDbTypeNames
     25 import androidx.room.ext.T
     26 import androidx.room.solver.CodeGenScope
     27 import androidx.room.vo.Database
     28 import androidx.room.vo.Entity
     29 import com.squareup.javapoet.MethodSpec
     30 import com.squareup.javapoet.ParameterSpec
     31 import com.squareup.javapoet.TypeSpec
     32 import javax.lang.model.element.Modifier.PROTECTED
     33 import javax.lang.model.element.Modifier.PUBLIC
     34 
     35 /**
     36  * Create an open helper using SupportSQLiteOpenHelperFactory
     37  */
     38 class SQLiteOpenHelperWriter(val database: Database) {
     39     fun write(outVar: String, configuration: ParameterSpec, scope: CodeGenScope) {
     40         scope.builder().apply {
     41             val sqliteConfigVar = scope.getTmpVar("_sqliteConfig")
     42             val callbackVar = scope.getTmpVar("_openCallback")
     43             addStatement("final $T $L = new $T($N, $L, $S, $S)",
     44                     SupportDbTypeNames.SQLITE_OPEN_HELPER_CALLBACK,
     45                     callbackVar, RoomTypeNames.OPEN_HELPER, configuration,
     46                     createOpenCallback(scope), database.identityHash, database.legacyIdentityHash)
     47             // build configuration
     48             addStatement(
     49                     """
     50                     final $T $L = $T.builder($N.context)
     51                     .name($N.name)
     52                     .callback($L)
     53                     .build()
     54                     """.trimIndent(),
     55                     SupportDbTypeNames.SQLITE_OPEN_HELPER_CONFIG, sqliteConfigVar,
     56                     SupportDbTypeNames.SQLITE_OPEN_HELPER_CONFIG,
     57                     configuration, configuration, callbackVar)
     58             addStatement("final $T $N = $N.sqliteOpenHelperFactory.create($L)",
     59                     SupportDbTypeNames.SQLITE_OPEN_HELPER, outVar,
     60                     configuration, sqliteConfigVar)
     61         }
     62     }
     63 
     64     private fun createOpenCallback(scope: CodeGenScope): TypeSpec {
     65         return TypeSpec.anonymousClassBuilder(L, database.version).apply {
     66             superclass(RoomTypeNames.OPEN_HELPER_DELEGATE)
     67             addMethod(createCreateAllTables())
     68             addMethod(createDropAllTables())
     69             addMethod(createOnCreate(scope.fork()))
     70             addMethod(createOnOpen(scope.fork()))
     71             addMethod(createValidateMigration(scope.fork()))
     72         }.build()
     73     }
     74 
     75     private fun createValidateMigration(scope: CodeGenScope): MethodSpec {
     76         return MethodSpec.methodBuilder("validateMigration").apply {
     77             addModifiers(PROTECTED)
     78             addAnnotation(Override::class.java)
     79             val dbParam = ParameterSpec.builder(SupportDbTypeNames.DB, "_db").build()
     80             addParameter(dbParam)
     81             database.entities.forEach { entity ->
     82                 val methodScope = scope.fork()
     83                 TableInfoValidationWriter(entity).write(dbParam, methodScope)
     84                 addCode(methodScope.builder().build())
     85             }
     86         }.build()
     87     }
     88 
     89     private fun createOnCreate(scope: CodeGenScope): MethodSpec {
     90         return MethodSpec.methodBuilder("onCreate").apply {
     91             addModifiers(PROTECTED)
     92             addAnnotation(Override::class.java)
     93             addParameter(SupportDbTypeNames.DB, "_db")
     94             invokeCallbacks(scope, "onCreate")
     95         }.build()
     96     }
     97 
     98     private fun createOnOpen(scope: CodeGenScope): MethodSpec {
     99         return MethodSpec.methodBuilder("onOpen").apply {
    100             addModifiers(PUBLIC)
    101             addAnnotation(Override::class.java)
    102             addParameter(SupportDbTypeNames.DB, "_db")
    103             addStatement("mDatabase = _db")
    104             if (database.enableForeignKeys) {
    105                 addStatement("_db.execSQL($S)", "PRAGMA foreign_keys = ON")
    106             }
    107             addStatement("internalInitInvalidationTracker(_db)")
    108             invokeCallbacks(scope, "onOpen")
    109         }.build()
    110     }
    111 
    112     private fun createCreateAllTables(): MethodSpec {
    113         return MethodSpec.methodBuilder("createAllTables").apply {
    114             addModifiers(PUBLIC)
    115             addAnnotation(Override::class.java)
    116             addParameter(SupportDbTypeNames.DB, "_db")
    117             database.bundle.buildCreateQueries().forEach {
    118                 addStatement("_db.execSQL($S)", it)
    119             }
    120         }.build()
    121     }
    122 
    123     private fun createDropAllTables(): MethodSpec {
    124         return MethodSpec.methodBuilder("dropAllTables").apply {
    125             addModifiers(PUBLIC)
    126             addAnnotation(Override::class.java)
    127             addParameter(SupportDbTypeNames.DB, "_db")
    128             database.entities.forEach {
    129                 addStatement("_db.execSQL($S)", createDropTableQuery(it))
    130             }
    131         }.build()
    132     }
    133 
    134     private fun MethodSpec.Builder.invokeCallbacks(scope: CodeGenScope, methodName: String) {
    135         val iVar = scope.getTmpVar("_i")
    136         val sizeVar = scope.getTmpVar("_size")
    137         beginControlFlow("if (mCallbacks != null)").apply {
    138             beginControlFlow("for (int $N = 0, $N = mCallbacks.size(); $N < $N; $N++)",
    139                     iVar, sizeVar, iVar, sizeVar, iVar).apply {
    140                 addStatement("mCallbacks.get($N).$N(_db)", iVar, methodName)
    141             }
    142             endControlFlow()
    143         }
    144         endControlFlow()
    145     }
    146 
    147     @VisibleForTesting
    148     fun createQuery(entity: Entity): String {
    149         return entity.createTableQuery
    150     }
    151 
    152     @VisibleForTesting
    153     fun createDropTableQuery(entity: Entity): String {
    154         return "DROP TABLE IF EXISTS `${entity.tableName}`"
    155     }
    156 }
    157