11600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar/* 21600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar * Copyright (C) 2016 The Android Open Source Project 31600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar * 41600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar * Licensed under the Apache License, Version 2.0 (the "License"); 51600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar * you may not use this file except in compliance with the License. 61600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar * You may obtain a copy of the License at 71600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar * 81600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar * http://www.apache.org/licenses/LICENSE-2.0 91600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar * 101600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar * Unless required by applicable law or agreed to in writing, software 111600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar * distributed under the License is distributed on an "AS IS" BASIS, 121600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar * See the License for the specific language governing permissions and 141600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar * limitations under the License. 151600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar */ 161600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar 1764db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarpackage android.arch.persistence.room.processor 181600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar 1964db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.ext.getAsBoolean 2064db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.ext.getAsInt 2164db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.ext.getAsString 2264db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.ext.getAsStringList 2364db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.ext.toType 2464db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.parser.SQLTypeAffinity 2564db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.processor.ProcessorErrors.INDEX_COLUMNS_CANNOT_BE_EMPTY 2664db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.processor.ProcessorErrors.RELATION_IN_ENTITY 2764db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.processor.cache.Cache 284d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyarimport android.arch.persistence.room.vo.EmbeddedField 2964db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.vo.Entity 3064db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.vo.Field 3164db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.vo.ForeignKey 3264db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.vo.ForeignKeyAction 3364db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.vo.Index 3464db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.vo.Pojo 3564db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.vo.PrimaryKey 3664db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.vo.Warning 370fc66ddc60bdc71d5466bb1db1a218e5a3d9c1fcYigit Boyarimport com.google.auto.common.AnnotationMirrors 38875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyarimport com.google.auto.common.AnnotationMirrors.getAnnotationValue 391600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyarimport com.google.auto.common.MoreElements 40dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyarimport com.google.auto.common.MoreTypes 41dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyarimport javax.lang.model.element.AnnotationMirror 42dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyarimport javax.lang.model.element.AnnotationValue 431600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyarimport javax.lang.model.element.TypeElement 44dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyarimport javax.lang.model.type.TypeKind 45dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyarimport javax.lang.model.type.TypeMirror 46dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyarimport javax.lang.model.util.SimpleAnnotationValueVisitor6 471600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar 48aa82fce1d73394bdc7f4c2510cf94a3572032b24Yigit Boyarclass EntityProcessor(baseContext: Context, val element: TypeElement) { 49aa82fce1d73394bdc7f4c2510cf94a3572032b24Yigit Boyar val context = baseContext.fork(element) 501600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar 51aa82fce1d73394bdc7f4c2510cf94a3572032b24Yigit Boyar fun process(): Entity { 525124503104860e68981cc3e3092b95932586f66fYigit Boyar return context.cache.entities.get(Cache.EntityKey(element), { 535124503104860e68981cc3e3092b95932586f66fYigit Boyar doProcess() 545124503104860e68981cc3e3092b95932586f66fYigit Boyar }) 555124503104860e68981cc3e3092b95932586f66fYigit Boyar } 565124503104860e68981cc3e3092b95932586f66fYigit Boyar private fun doProcess() : Entity { 5764db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyar context.checker.hasAnnotation(element, android.arch.persistence.room.Entity::class, 588bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar ProcessorErrors.ENTITY_MUST_BE_ANNOTATED_WITH_ENTITY) 5996cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar val pojo = PojoProcessor( 6096cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar baseContext = context, 6196cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar element = element, 6296cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar bindingScope = FieldProcessor.BindingScope.TWO_WAY, 6396cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar parent = null).process() 64092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar context.checker.check(pojo.relations.isEmpty(), element, RELATION_IN_ENTITY) 650fc66ddc60bdc71d5466bb1db1a218e5a3d9c1fcYigit Boyar val annotation = MoreElements.getAnnotationMirror(element, 6664db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyar android.arch.persistence.room.Entity::class.java).orNull() 67dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar val tableName: String 682fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar val entityIndices: List<IndexInput> 69875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val foreignKeyInputs: List<ForeignKeyInput> 70dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar val inheritSuperIndices: Boolean 71faf0a687b4b6022196c4a9223d2c901b1e9cebe7Yigit Boyar if (annotation != null) { 72875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar tableName = extractTableName(element, annotation) 73dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar entityIndices = extractIndices(annotation, tableName) 74dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar inheritSuperIndices = AnnotationMirrors 75dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar .getAnnotationValue(annotation, "inheritSuperIndices").getAsBoolean(false) 76875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar foreignKeyInputs = extractForeignKeys(annotation) 770fc66ddc60bdc71d5466bb1db1a218e5a3d9c1fcYigit Boyar } else { 780fc66ddc60bdc71d5466bb1db1a218e5a3d9c1fcYigit Boyar tableName = element.simpleName.toString() 79875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar foreignKeyInputs = emptyList() 80dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar entityIndices = emptyList() 81dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar inheritSuperIndices = false 820fc66ddc60bdc71d5466bb1db1a218e5a3d9c1fcYigit Boyar } 83faf0a687b4b6022196c4a9223d2c901b1e9cebe7Yigit Boyar context.checker.notBlank(tableName, element, 84faf0a687b4b6022196c4a9223d2c901b1e9cebe7Yigit Boyar ProcessorErrors.ENTITY_TABLE_NAME_CANNOT_BE_EMPTY) 85dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar 86dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar val fieldIndices = pojo.fields 87dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar .filter { it.indexed } 88dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar .map { 89dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar if (it.parent != null) { 90dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar it.indexed = false 914d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar context.logger.w(Warning.INDEX_FROM_EMBEDDED_FIELD_IS_DROPPED, it.element, 924d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar ProcessorErrors.droppedEmbeddedFieldIndex( 93dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar it.getPath(), element.qualifiedName.toString())) 94dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar null 95dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } else if (it.element.enclosingElement != element && !inheritSuperIndices) { 96dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar it.indexed = false 97dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar context.logger.w(Warning.INDEX_FROM_PARENT_FIELD_IS_DROPPED, 98dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar ProcessorErrors.droppedSuperClassFieldIndex( 99dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar it.columnName, element.toString(), 100dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar it.element.enclosingElement.toString() 101dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar )) 102dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar null 103dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } else { 1042fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar IndexInput( 1056728af1603c6082a13533b830a9ef0bbc51ba827Yigit Boyar name = createIndexName(listOf(it.columnName), tableName), 106dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar unique = false, 1076728af1603c6082a13533b830a9ef0bbc51ba827Yigit Boyar columnNames = listOf(it.columnName) 108dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar ) 109dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 110dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar }.filterNotNull() 111dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar val superIndices = loadSuperIndices(element.superclass, tableName, inheritSuperIndices) 1122fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar val indexInputs = entityIndices + fieldIndices + superIndices 1132fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar val indices = validateAndCreateIndices(indexInputs, pojo) 114dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar 1154d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar val primaryKey = findPrimaryKey(pojo.fields, pojo.embeddedFields) 1162c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar val affinity = primaryKey.fields.firstOrNull()?.affinity ?: SQLTypeAffinity.TEXT 1172c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar context.checker.check( 1182c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar !primaryKey.autoGenerateId || affinity == SQLTypeAffinity.INTEGER, 1192c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar primaryKey.fields.firstOrNull()?.element ?: element, 1202c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar ProcessorErrors.AUTO_INCREMENTED_PRIMARY_KEY_IS_NOT_INT 1212c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar ) 122875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar 123875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val entityForeignKeys = validateAndCreateForeignKeyReferences(foreignKeyInputs, pojo) 124875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar checkIndicesForForeignKeys(entityForeignKeys, primaryKey, indices) 125875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar 12696cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar val entity = Entity(element = element, 12796cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar tableName = tableName, 12896cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar type = pojo.type, 12996cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar fields = pojo.fields, 1304d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar embeddedFields = pojo.embeddedFields, 1312c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar indices = indices, 132875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar primaryKey = primaryKey, 133f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar foreignKeys = entityForeignKeys, 134f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar constructor = pojo.constructor) 1352c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar 136e6325fbeaa2e6759496ea2ca9a4d3d958df690d7Yigit Boyar return entity 1371600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar } 138dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar 139875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar private fun checkIndicesForForeignKeys(entityForeignKeys: List<ForeignKey>, 140875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar primaryKey: PrimaryKey, 141875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar indices: List<Index>) { 142875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar fun covers(columnNames: List<String>, fields : List<Field>) : Boolean = 143875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar fields.size >= columnNames.size && columnNames.withIndex().all { 144875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar fields[it.index].columnName == it.value 145875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 146875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar 147875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar entityForeignKeys.forEach { fKey -> 148875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val columnNames = fKey.childFields.map { it.columnName } 149875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val exists = covers(columnNames, primaryKey.fields) || indices.any { index -> 150875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar covers(columnNames, index.fields) 151875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 152875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar if (!exists) { 153875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar if (columnNames.size == 1) { 154875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar context.logger.w(Warning.MISSING_INDEX_ON_FOREIGN_KEY_CHILD, element, 155875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar ProcessorErrors.foreignKeyMissingIndexInChildColumn(columnNames[0])) 156875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } else { 157875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar context.logger.w(Warning.MISSING_INDEX_ON_FOREIGN_KEY_CHILD, element, 158875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar ProcessorErrors.foreignKeyMissingIndexInChildColumns(columnNames)) 159875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 160875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 161875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 162875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 163875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar 164875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar /** 165875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar * Does a validation on foreign keys except the parent table's columns. 166875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar */ 167875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar private fun validateAndCreateForeignKeyReferences(foreignKeyInputs: List<ForeignKeyInput>, 168875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar pojo: Pojo): List<ForeignKey> { 169875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return foreignKeyInputs.map { 170875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar if (it.onUpdate == null) { 171875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar context.logger.e(element, ProcessorErrors.INVALID_FOREIGN_KEY_ACTION) 172875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return@map null 173875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 174875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar if (it.onDelete == null) { 175875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar context.logger.e(element, ProcessorErrors.INVALID_FOREIGN_KEY_ACTION) 176875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return@map null 177875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 178875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar if (it.childColumns.isEmpty()) { 179875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar context.logger.e(element, ProcessorErrors.FOREIGN_KEY_EMPTY_CHILD_COLUMN_LIST) 180875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return@map null 181875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 182875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar if (it.parentColumns.isEmpty()) { 183875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar context.logger.e(element, ProcessorErrors.FOREIGN_KEY_EMPTY_PARENT_COLUMN_LIST) 184875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return@map null 185875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 186875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar if (it.childColumns.size != it.parentColumns.size) { 187875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar context.logger.e(element, ProcessorErrors.foreignKeyColumnNumberMismatch( 188875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar it.childColumns, it.parentColumns 189875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar )) 190875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return@map null 191875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 192875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val parentElement = try { 193875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar MoreTypes.asElement(it.parent) as TypeElement 194875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } catch (noClass: IllegalArgumentException) { 195875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar context.logger.e(element, ProcessorErrors.FOREIGN_KEY_CANNOT_FIND_PARENT) 196875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return@map null 197875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 198875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val parentAnnotation = MoreElements.getAnnotationMirror(parentElement, 19964db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyar android.arch.persistence.room.Entity::class.java).orNull() 200875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar if (parentAnnotation == null) { 201875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar context.logger.e(element, 202875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar ProcessorErrors.foreignKeyNotAnEntity(parentElement.toString())) 203875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return@map null 204875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 205875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val tableName = extractTableName(parentElement, parentAnnotation) 206875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val fields = it.childColumns.map { columnName -> 207875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val field = pojo.fields.find { it.columnName == columnName } 208875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar if (field == null) { 209875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar context.logger.e(pojo.element, 210875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar ProcessorErrors.foreignKeyChildColumnDoesNotExist(columnName, 211875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar pojo.fields.map { it.columnName })) 212875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 213875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar field 214875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar }.filterNotNull() 215875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar if (fields.size != it.childColumns.size) { 216875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return@map null 217875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 218875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar ForeignKey( 219875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar parentTable = tableName, 220875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar childFields = fields, 221875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar parentColumns = it.parentColumns, 222875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar onDelete = it.onDelete, 223875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar onUpdate = it.onUpdate, 224875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar deferred = it.deferred 225875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar ) 226875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar }.filterNotNull() 227875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 228875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar 2294d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar private fun findPrimaryKey(fields: List<Field>, embeddedFields: List<EmbeddedField>) 2302c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar : PrimaryKey { 2312c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar val candidates = collectPrimaryKeysFromEntityAnnotations(element, fields) + 2322c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar collectPrimaryKeysFromPrimaryKeyAnnotations(fields) + 2334d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar collectPrimaryKeysFromEmbeddedFields(embeddedFields) 2342c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar 2352c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar context.checker.check(candidates.isNotEmpty(), element, ProcessorErrors.MISSING_PRIMARY_KEY) 236540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu 237540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu // 1. If a key is not autogenerated, but is Primary key or is part of Primary key we 238540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu // force the @NonNull annotation 239540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu // 2. If a key is autogenerate, we generate NOT NULL in table spec, but we don't require 240540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu // @NonNull annotation on the field itself. 241540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu candidates.filter { candidate -> !candidate.autoGenerateId } 242540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu .map { candidate -> 243540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu candidate.fields.map { field -> 244540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu context.checker.check(field.nonNull, field.element, 245540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu ProcessorErrors.PRIMARY_KEY_NULL) 246540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu } 247540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu } 248540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu 2492c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar if (candidates.size == 1) { 2502c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // easy :) 2512c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar return candidates.first() 2522c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 2532c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar 2542c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar return choosePrimaryKey(candidates, element) 2552c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 2562c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar 2572c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar /** 2582c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar * Check fields for @PrimaryKey. 2592c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar */ 2602c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar private fun collectPrimaryKeysFromPrimaryKeyAnnotations(fields: List<Field>): List<PrimaryKey> { 2612c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar return fields.map { field -> 2622c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar MoreElements.getAnnotationMirror(field.element, 26364db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyar android.arch.persistence.room.PrimaryKey::class.java).orNull()?.let { 2642c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar if (field.parent != null) { 2652c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // the field in the entity that contains this error. 2664d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar val grandParentField = field.parent.mRootParent.field.element 2672c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // bound for entity. 2682c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar context.fork(grandParentField).logger.w( 2694d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar Warning.PRIMARY_KEY_FROM_EMBEDDED_IS_DROPPED, 2702c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar grandParentField, 2714d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar ProcessorErrors.embeddedPrimaryKeyIsDropped( 2722c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar element.qualifiedName.toString(), field.name)) 2732c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar null 2742c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } else { 2752c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar PrimaryKey(declaredIn = field.element.enclosingElement, 2762c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar fields = listOf(field), 2772c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar autoGenerateId = AnnotationMirrors 2782c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar .getAnnotationValue(it, "autoGenerate") 2792c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar .getAsBoolean(false)) 2802c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 2812c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 2822c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar }.filterNotNull() 2832c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 2842c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar 2852c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar /** 2862c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar * Check classes for @Entity(primaryKeys = ?). 2872c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar */ 2882c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar private fun collectPrimaryKeysFromEntityAnnotations(typeElement: TypeElement, 2892c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar availableFields: List<Field>) 2902c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar : List<PrimaryKey> { 2912c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar val myPkeys = MoreElements.getAnnotationMirror(typeElement, 29264db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyar android.arch.persistence.room.Entity::class.java).orNull()?.let { 2932c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar val primaryKeyColumns = AnnotationMirrors.getAnnotationValue(it, "primaryKeys") 2942c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar .getAsStringList() 2952c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar if (primaryKeyColumns.isEmpty()) { 2962c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar emptyList<PrimaryKey>() 2972c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } else { 2982c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar val fields = primaryKeyColumns.map { pKeyColumnName -> 2992c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar val field = availableFields.firstOrNull { it.columnName == pKeyColumnName } 3002c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar context.checker.check(field != null, typeElement, 3012c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar ProcessorErrors.primaryKeyColumnDoesNotExist(pKeyColumnName, 3022c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar availableFields.map { it.columnName })) 3032c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar field 3042c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar }.filterNotNull() 3052c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar listOf(PrimaryKey(declaredIn = typeElement, 3062c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar fields = fields, 3072c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar autoGenerateId = false)) 3082c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 3092c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } ?: emptyList<PrimaryKey>() 3102c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // checks supers. 3112c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar val mySuper = typeElement.superclass 3122c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar val superPKeys = if (mySuper != null && mySuper.kind != TypeKind.NONE) { 3132c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // my super cannot see my fields so remove them. 3142c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar val remainingFields = availableFields.filterNot { 3152c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar it.element.enclosingElement == typeElement 3162c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 3172c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar collectPrimaryKeysFromEntityAnnotations( 3182c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar MoreTypes.asTypeElement(mySuper), remainingFields) 3192c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } else { 3202c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar emptyList() 3212c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 3222c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar return superPKeys + myPkeys 3232c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 3242c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar 3254d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar private fun collectPrimaryKeysFromEmbeddedFields(embeddedFields: List<EmbeddedField>) 3262c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar : List<PrimaryKey> { 3274d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar return embeddedFields.map { embeddedField -> 3284d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar MoreElements.getAnnotationMirror(embeddedField.field.element, 32964db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyar android.arch.persistence.room.PrimaryKey::class.java).orNull()?.let { 3302c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar val autoGenerate = AnnotationMirrors 3312c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar .getAnnotationValue(it, "autoGenerate").getAsBoolean(false) 3324d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar context.checker.check(!autoGenerate || embeddedField.pojo.fields.size == 1, 3334d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar embeddedField.field.element, 3344d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar ProcessorErrors.AUTO_INCREMENT_EMBEDDED_HAS_MULTIPLE_FIELDS) 3354d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar PrimaryKey(declaredIn = embeddedField.field.element.enclosingElement, 3364d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar fields = embeddedField.pojo.fields, 3372c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar autoGenerateId = autoGenerate) 3382c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 3392c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar }.filterNotNull() 3402c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 3412c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar 3422c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // start from my element and check if anywhere in the list we can find the only well defined 3432c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // pkey, if so, use it. 3442c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar private fun choosePrimaryKey(candidates: List<PrimaryKey>, typeElement: TypeElement) 3452c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar : PrimaryKey { 3462c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // If 1 of these primary keys is declared in this class, then it is the winner. Just print 3472c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // a note for the others. 3482c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // If 0 is declared, check the parent. 3492c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // If more than 1 primary key is declared in this class, it is an error. 3502c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar val myPKeys = candidates.filter { candidate -> 3512c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar candidate.declaredIn == typeElement 3522c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 3532c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar return if (myPKeys.size == 1) { 3542c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // just note, this is not worth an error or warning 3552c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar (candidates - myPKeys).forEach { 3562c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar context.logger.d(element, 3572c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar "${it.toHumanReadableString()} is" + 3582c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar " overridden by ${myPKeys.first().toHumanReadableString()}") 3592c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 3602c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar myPKeys.first() 3612c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } else if (myPKeys.isEmpty()) { 3622c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // i have not declared anything, delegate to super 3632c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar val mySuper = typeElement.superclass 3642c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar if (mySuper != null && mySuper.kind != TypeKind.NONE) { 3652c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar return choosePrimaryKey(candidates, MoreTypes.asTypeElement(mySuper)) 3662c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 3672c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar PrimaryKey.MISSING 3682c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } else { 3692c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar context.logger.e(element, ProcessorErrors.multiplePrimaryKeyAnnotations( 3702c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar myPKeys.map(PrimaryKey::toHumanReadableString))) 3712c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar PrimaryKey.MISSING 3722c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 3732c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 3742c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar 375875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar private fun validateAndCreateIndices(inputs: List<IndexInput>, pojo: Pojo): List<Index> { 376dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar // check for columns 3772fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar val indices = inputs.map { input -> 3786728af1603c6082a13533b830a9ef0bbc51ba827Yigit Boyar context.checker.check(input.columnNames.isNotEmpty(), element, 379dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar INDEX_COLUMNS_CANNOT_BE_EMPTY) 3806728af1603c6082a13533b830a9ef0bbc51ba827Yigit Boyar val fields = input.columnNames.map { columnName -> 3812fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar val field = pojo.fields.firstOrNull { 3826728af1603c6082a13533b830a9ef0bbc51ba827Yigit Boyar it.columnName == columnName 383dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 3842fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar context.checker.check(field != null, element, 3852fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar ProcessorErrors.indexColumnDoesNotExist( 3866728af1603c6082a13533b830a9ef0bbc51ba827Yigit Boyar columnName, pojo.fields.map { it.columnName } 3872fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar )) 3882fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar field 3892fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar }.filterNotNull() 3902fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar if (fields.isEmpty()) { 3912fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar null 3922fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar } else { 3932fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar Index(name = input.name, unique = input.unique, fields = fields) 394dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 3952fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar }.filterNotNull() 396dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar 397dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar // check for duplicate indices 398dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar indices 399dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar .groupBy { it.name } 400dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar .filter { it.value.size > 1 } 401dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar .forEach { 402dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar context.logger.e(element, ProcessorErrors.duplicateIndexInEntity(it.key)) 403dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 404dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar 4054d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar // see if any embedded field is an entity with indices, if so, report a warning 4064d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar pojo.embeddedFields.forEach { embedded -> 4074d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar val embeddedElement = embedded.pojo.element 4084d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar val subEntityAnnotation = MoreElements.getAnnotationMirror(embeddedElement, 40964db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyar android.arch.persistence.room.Entity::class.java).orNull() 410dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar subEntityAnnotation?.let { 411dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar val subIndices = extractIndices(subEntityAnnotation, "") 412dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar if (subIndices.isNotEmpty()) { 4134d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar context.logger.w(Warning.INDEX_FROM_EMBEDDED_ENTITY_IS_DROPPED, 4144d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar embedded.field.element, ProcessorErrors.droppedEmbeddedIndex( 4154d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar entityName = embedded.pojo.typeName.toString(), 4164d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar fieldPath = embedded.field.getPath(), 417dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar grandParent = element.qualifiedName.toString())) 418dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 419dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 420dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 4212fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar return indices 422dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 423dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar 424dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar // check if parent is an Entity, if so, report its annotation indices 425dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar private fun loadSuperIndices(typeMirror: TypeMirror?, tableName: String, inherit: Boolean) 4262fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar : List<IndexInput> { 427dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar if (typeMirror == null || typeMirror.kind == TypeKind.NONE) { 428dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar return emptyList() 429dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 430dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar val parentElement = MoreTypes.asTypeElement(typeMirror) 431dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar val myIndices = MoreElements.getAnnotationMirror(parentElement, 43264db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyar android.arch.persistence.room.Entity::class.java).orNull()?.let { annotation -> 433dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar val indices = extractIndices(annotation, tableName = "super") 4342c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar if (indices.isEmpty()) { 4352c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar emptyList() 4362c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } else if (inherit) { 437dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar // rename them 438dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar indices.map { 4392fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar IndexInput( 4406728af1603c6082a13533b830a9ef0bbc51ba827Yigit Boyar name = createIndexName(it.columnNames, tableName), 441dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar unique = it.unique, 4426728af1603c6082a13533b830a9ef0bbc51ba827Yigit Boyar columnNames = it.columnNames) 443dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 444dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } else { 445dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar context.logger.w(Warning.INDEX_FROM_PARENT_IS_DROPPED, 446dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar parentElement, 447dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar ProcessorErrors.droppedSuperClassIndex( 448dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar childEntity = element.qualifiedName.toString(), 449dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar superEntity = parentElement.qualifiedName.toString())) 450dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar emptyList() 451dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 452dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } ?: emptyList() 453dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar return myIndices + loadSuperIndices(parentElement.superclass, tableName, inherit) 454dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 455dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar 456dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar companion object { 457875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar private fun extractTableName(element: TypeElement, annotation: AnnotationMirror) 458875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar : String { 459875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val annotationValue = AnnotationMirrors 460875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar .getAnnotationValue(annotation, "tableName").value.toString() 461875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return if (annotationValue == "") { 462875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar element.simpleName.toString() 463875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } else { 464875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar annotationValue 465875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 466875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 467875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar 468dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar private fun extractIndices(annotation: AnnotationMirror, tableName: String) 4692fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar : List<IndexInput> { 470dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar val arrayOfIndexAnnotations = AnnotationMirrors.getAnnotationValue(annotation, 471dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar "indices") 472dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar return INDEX_LIST_VISITOR.visit(arrayOfIndexAnnotations, tableName) 473dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 474dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar 475dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar private val INDEX_LIST_VISITOR = object 4762fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar : SimpleAnnotationValueVisitor6<List<IndexInput>, String>() { 477dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar override fun visitArray(values: MutableList<out AnnotationValue>?, tableName: String) 4782fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar : List<IndexInput> { 479dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar return values?.map { 480dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar INDEX_VISITOR.visit(it, tableName) 4812fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar }?.filterNotNull() ?: emptyList<IndexInput>() 482dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 483dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 484dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar 4852fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar private val INDEX_VISITOR = object : SimpleAnnotationValueVisitor6<IndexInput?, String>() { 4862fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar override fun visitAnnotation(a: AnnotationMirror?, tableName: String): IndexInput? { 487875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val fieldInput = getAnnotationValue(a, "value").getAsStringList() 488875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val unique = getAnnotationValue(a, "unique").getAsBoolean(false) 489875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val nameValue = getAnnotationValue(a, "name") 490dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar .getAsString("") 491dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar val name = if (nameValue == null || nameValue == "") { 4922fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar createIndexName(fieldInput, tableName) 493dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } else { 494dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar nameValue 495dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 4962fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar return IndexInput(name, unique, fieldInput) 497dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 498dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 499dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar 500dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar private fun createIndexName(columnNames: List<String>, tableName: String): String { 5016728af1603c6082a13533b830a9ef0bbc51ba827Yigit Boyar return "index_" + tableName + "_" + columnNames.joinToString("_") 502dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 503875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar 504875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar private fun extractForeignKeys(annotation: AnnotationMirror): List<ForeignKeyInput> { 505875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val arrayOfForeignKeyAnnotations = getAnnotationValue(annotation, "foreignKeys") 506875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return FOREIGN_KEY_LIST_VISITOR.visit(arrayOfForeignKeyAnnotations) 507875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 508875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar 509875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar private val FOREIGN_KEY_LIST_VISITOR = object 510875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar : SimpleAnnotationValueVisitor6<List<ForeignKeyInput>, Void?>() { 511875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar override fun visitArray(values: MutableList<out AnnotationValue>?, void: Void?) 512875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar : List<ForeignKeyInput> { 513875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return values?.map { 514875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar FOREIGN_KEY_VISITOR.visit(it) 515875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar }?.filterNotNull() ?: emptyList<ForeignKeyInput>() 516875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 517875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 518875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar 519875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar private val FOREIGN_KEY_VISITOR = object : SimpleAnnotationValueVisitor6<ForeignKeyInput?, 520875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar Void?>() { 521875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar override fun visitAnnotation(a: AnnotationMirror?, void: Void?): ForeignKeyInput? { 522875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val entityClass = try { 523875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar getAnnotationValue(a, "entity").toType() 524875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } catch (notPresent: TypeNotPresentException) { 525875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return null 526875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 527875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val parentColumns = getAnnotationValue(a, "parentColumns").getAsStringList() 528875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val childColumns = getAnnotationValue(a, "childColumns").getAsStringList() 529875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val onDeleteInput = getAnnotationValue(a, "onDelete").getAsInt() 530875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val onUpdateInput = getAnnotationValue(a, "onUpdate").getAsInt() 531875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val deferred = getAnnotationValue(a, "deferred").getAsBoolean(true) 532875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val onDelete = ForeignKeyAction.fromAnnotationValue(onDeleteInput) 533875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val onUpdate = ForeignKeyAction.fromAnnotationValue(onUpdateInput) 534875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return ForeignKeyInput( 535875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar parent = entityClass, 536875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar parentColumns = parentColumns, 537875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar childColumns = childColumns, 538875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar onDelete = onDelete, 539875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar onUpdate = onUpdate, 540875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar deferred = deferred) 541875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 542875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 543dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 5442fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar 5452fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar /** 5462fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar * processed Index annotation output 5472fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar */ 548875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar data class IndexInput(val name: String, val unique: Boolean, val columnNames: List<String>) 549875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar 550875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar /** 551875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar * ForeignKey, before it is processed in the context of a database. 552875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar */ 553875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar data class ForeignKeyInput(val parent : TypeMirror, val parentColumns : List<String>, 554875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val childColumns : List<String>, val onDelete : ForeignKeyAction?, 555875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val onUpdate : ForeignKeyAction?, val deferred : Boolean) 5561600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar} 557