18bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar/*
28bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar * Copyright (C) 2016 The Android Open Source Project
38bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar *
48bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
58bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar * you may not use this file except in compliance with the License.
68bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar * You may obtain a copy of the License at
78bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar *
88bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
98bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar *
108bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar * Unless required by applicable law or agreed to in writing, software
118bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
128bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar * See the License for the specific language governing permissions and
148bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar * limitations under the License.
158bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar */
168bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar
17bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viverettepackage androidx.room.vo
188bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar
19bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.RoomMasterTable
20bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.migration.bundle.DatabaseBundle
21bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.migration.bundle.SchemaBundle
22d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport com.squareup.javapoet.ClassName
23fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyarimport org.apache.commons.codec.digest.DigestUtils
24a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyarimport java.io.File
25aa82fce1d73394bdc7f4c2510cf94a3572032b24Yigit Boyarimport javax.lang.model.element.TypeElement
26d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyarimport javax.lang.model.type.TypeMirror
278bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar
288bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar/**
298bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar * Holds information about a class annotated with Database.
308bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar */
31a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyardata class Database(val element: TypeElement,
32a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar                    val type: TypeMirror,
33a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar                    val entities: List<Entity>,
34a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar                    val daoMethods: List<DaoMethod>,
35a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar                    val version: Int,
360045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                    val exportSchema: Boolean,
376f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas                    val enableForeignKeys: Boolean) {
38a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar    val typeName: ClassName by lazy { ClassName.get(element) }
39d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar
40dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar    private val implClassName by lazy {
41dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar        "${typeName.simpleNames().joinToString("_")}_Impl"
42d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar    }
43d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar
44a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar    val implTypeName: ClassName by lazy {
45d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar        ClassName.get(typeName.packageName(), implClassName)
46d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar    }
47fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar
48a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar    val bundle by lazy {
49a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar        DatabaseBundle(version, identityHash, entities.map(Entity::toBundle),
50a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar                listOf(RoomMasterTable.CREATE_QUERY,
51a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar                        RoomMasterTable.createInsertQuery(identityHash)))
52a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar    }
53a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar
54fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar    /**
55fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar     * Create a has that identifies this database definition so that at runtime we can check to
56fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar     * ensure developer didn't forget to update the version.
57fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar     */
58a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar    val identityHash: String by lazy {
59e5ed537fe6f14f0bbb43ddef605ed22f09714142Yigit Boyar        val idKey = SchemaIdentityKey()
60e5ed537fe6f14f0bbb43ddef605ed22f09714142Yigit Boyar        idKey.appendSorted(entities)
61e5ed537fe6f14f0bbb43ddef605ed22f09714142Yigit Boyar        idKey.hash()
62e5ed537fe6f14f0bbb43ddef605ed22f09714142Yigit Boyar    }
63e5ed537fe6f14f0bbb43ddef605ed22f09714142Yigit Boyar
64e5ed537fe6f14f0bbb43ddef605ed22f09714142Yigit Boyar    val legacyIdentityHash: String by lazy {
65fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar        val entityDescriptions = entities
66fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar                .sortedBy { it.tableName }
67fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar                .map { it.createTableQuery }
68fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar        val indexDescriptions = entities
69fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar                .flatMap { entity ->
70fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar                    entity.indices.map { index ->
71fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar                        index.createQuery(entity.tableName)
72fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar                    }
73fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar                }
74fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar        val input = (entityDescriptions + indexDescriptions).joinToString("¯\\_(ツ)_/¯")
75fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar        DigestUtils.md5Hex(input)
76fa3905934508aa143d899cb9b62b3b074748c9e9Yigit Boyar    }
77a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar
78a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar    fun exportSchema(file: File) {
79a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar        val schemaBundle = SchemaBundle(SchemaBundle.LATEST_FORMAT, bundle)
80e5ed537fe6f14f0bbb43ddef605ed22f09714142Yigit Boyar        if (file.exists()) {
81e5ed537fe6f14f0bbb43ddef605ed22f09714142Yigit Boyar            val existing = file.inputStream().use {
82e5ed537fe6f14f0bbb43ddef605ed22f09714142Yigit Boyar                SchemaBundle.deserialize(it)
83e5ed537fe6f14f0bbb43ddef605ed22f09714142Yigit Boyar            }
84e5ed537fe6f14f0bbb43ddef605ed22f09714142Yigit Boyar            if (existing.isSchemaEqual(schemaBundle)) {
85e5ed537fe6f14f0bbb43ddef605ed22f09714142Yigit Boyar                return
86e5ed537fe6f14f0bbb43ddef605ed22f09714142Yigit Boyar            }
87e5ed537fe6f14f0bbb43ddef605ed22f09714142Yigit Boyar        }
88a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar        SchemaBundle.serialize(schemaBundle, file)
89a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar    }
90d72e20e472815b7d0918e0d309cee48a71c7988bYigit Boyar}
91