| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package androidx.room.vo |
| |
| import androidx.room.RoomMasterTable |
| import androidx.room.migration.bundle.DatabaseBundle |
| import androidx.room.migration.bundle.SchemaBundle |
| import androidx.room.processing.XType |
| import androidx.room.processing.XTypeElement |
| import com.squareup.javapoet.ClassName |
| import org.apache.commons.codec.digest.DigestUtils |
| import java.io.File |
| |
| /** |
| * Holds information about a class annotated with Database. |
| */ |
| data class Database( |
| val element: XTypeElement, |
| val type: XType, |
| val entities: List<Entity>, |
| val views: List<DatabaseView>, |
| val daoMethods: List<DaoMethod>, |
| val version: Int, |
| val exportSchema: Boolean, |
| val enableForeignKeys: Boolean |
| ) { |
| val typeName: ClassName by lazy { element.className } |
| |
| private val implClassName by lazy { |
| "${typeName.simpleNames().joinToString("_")}_Impl" |
| } |
| |
| val implTypeName: ClassName by lazy { |
| ClassName.get(typeName.packageName(), implClassName) |
| } |
| |
| val bundle by lazy { |
| DatabaseBundle(version, identityHash, entities.map(Entity::toBundle), |
| views.map(DatabaseView::toBundle), |
| listOf(RoomMasterTable.CREATE_QUERY, |
| RoomMasterTable.createInsertQuery(identityHash))) |
| } |
| |
| /** |
| * Create a has that identifies this database definition so that at runtime we can check to |
| * ensure developer didn't forget to update the version. |
| */ |
| val identityHash: String by lazy { |
| val idKey = SchemaIdentityKey() |
| idKey.appendSorted(entities) |
| idKey.appendSorted(views) |
| idKey.hash() |
| } |
| |
| val legacyIdentityHash: String by lazy { |
| val entityDescriptions = entities |
| .sortedBy { it.tableName } |
| .map { it.createTableQuery } |
| val indexDescriptions = entities |
| .flatMap { entity -> |
| entity.indices.map { index -> |
| // For legacy purposes we need to remove the later added 'IF NOT EXISTS' |
| // part of the create statement, otherwise old valid legacy hashes stop |
| // being accepted even though the schema has not changed. b/139306173 |
| if (index.unique) { |
| "CREATE UNIQUE INDEX" |
| } else { |
| // The extra space between 'CREATE' and 'INDEX' is on purpose, this |
| // is a typo we have to live with. |
| "CREATE INDEX" |
| } + index.createQuery(entity.tableName).substringAfter("IF NOT EXISTS") |
| } |
| } |
| val viewDescriptions = views |
| .sortedBy { it.viewName } |
| .map { it.viewName + it.query.original } |
| val input = (entityDescriptions + indexDescriptions + viewDescriptions) |
| .joinToString("¯\\_(ツ)_/¯") |
| DigestUtils.md5Hex(input) |
| } |
| |
| fun exportSchema(file: File) { |
| val schemaBundle = SchemaBundle(SchemaBundle.LATEST_FORMAT, bundle) |
| if (file.exists()) { |
| val existing = file.inputStream().use { |
| SchemaBundle.deserialize(it) |
| } |
| if (existing.isSchemaEqual(schemaBundle)) { |
| return |
| } |
| } |
| SchemaBundle.serialize(schemaBundle, file) |
| } |
| } |