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
17package androidx.room.vo
18
19import androidx.room.RoomMasterTable
20import androidx.room.migration.bundle.DatabaseBundle
21import androidx.room.migration.bundle.SchemaBundle
22import com.squareup.javapoet.ClassName
23import org.apache.commons.codec.digest.DigestUtils
24import java.io.File
25import javax.lang.model.element.TypeElement
26import javax.lang.model.type.TypeMirror
27
28/**
29 * Holds information about a class annotated with Database.
30 */
31data class Database(val element: TypeElement,
32                    val type: TypeMirror,
33                    val entities: List<Entity>,
34                    val daoMethods: List<DaoMethod>,
35                    val version: Int,
36                    val exportSchema: Boolean,
37                    val enableForeignKeys: Boolean) {
38    val typeName: ClassName by lazy { ClassName.get(element) }
39
40    private val implClassName by lazy {
41        "${typeName.simpleNames().joinToString("_")}_Impl"
42    }
43
44    val implTypeName: ClassName by lazy {
45        ClassName.get(typeName.packageName(), implClassName)
46    }
47
48    val bundle by lazy {
49        DatabaseBundle(version, identityHash, entities.map(Entity::toBundle),
50                listOf(RoomMasterTable.CREATE_QUERY,
51                        RoomMasterTable.createInsertQuery(identityHash)))
52    }
53
54    /**
55     * Create a has that identifies this database definition so that at runtime we can check to
56     * ensure developer didn't forget to update the version.
57     */
58    val identityHash: String by lazy {
59        val idKey = SchemaIdentityKey()
60        idKey.appendSorted(entities)
61        idKey.hash()
62    }
63
64    val legacyIdentityHash: String by lazy {
65        val entityDescriptions = entities
66                .sortedBy { it.tableName }
67                .map { it.createTableQuery }
68        val indexDescriptions = entities
69                .flatMap { entity ->
70                    entity.indices.map { index ->
71                        index.createQuery(entity.tableName)
72                    }
73                }
74        val input = (entityDescriptions + indexDescriptions).joinToString("¯\\_(ツ)_/¯")
75        DigestUtils.md5Hex(input)
76    }
77
78    fun exportSchema(file: File) {
79        val schemaBundle = SchemaBundle(SchemaBundle.LATEST_FORMAT, bundle)
80        if (file.exists()) {
81            val existing = file.inputStream().use {
82                SchemaBundle.deserialize(it)
83            }
84            if (existing.isSchemaEqual(schemaBundle)) {
85                return
86            }
87        }
88        SchemaBundle.serialize(schemaBundle, file)
89    }
90}
91