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 17ba069d50913c3fb250bb60ec310439db36895337Alan Viverettepackage androidx.room.processor 181600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar 19ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.ext.getAsBoolean 20ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.ext.getAsInt 21ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.ext.getAsString 22ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.ext.getAsStringList 23ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.ext.toType 24ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.parser.SQLTypeAffinity 25ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.parser.SqlParser 26ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.processor.ProcessorErrors.INDEX_COLUMNS_CANNOT_BE_EMPTY 27ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.processor.ProcessorErrors.RELATION_IN_ENTITY 28ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.processor.cache.Cache 29ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.vo.EmbeddedField 30ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.vo.Entity 31ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.vo.Field 32ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.vo.ForeignKey 33ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.vo.ForeignKeyAction 34ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.vo.Index 35ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.vo.Pojo 36ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.vo.PrimaryKey 37ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.room.vo.Warning 380fc66ddc60bdc71d5466bb1db1a218e5a3d9c1fcYigit Boyarimport com.google.auto.common.AnnotationMirrors 39875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyarimport com.google.auto.common.AnnotationMirrors.getAnnotationValue 401600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyarimport com.google.auto.common.MoreElements 41dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyarimport com.google.auto.common.MoreTypes 42dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyarimport javax.lang.model.element.AnnotationMirror 43dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyarimport javax.lang.model.element.AnnotationValue 441a74519922de68e007027d56aae9370ee21f31f9shepshapardimport javax.lang.model.element.Name 451600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyarimport javax.lang.model.element.TypeElement 46dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyarimport javax.lang.model.type.TypeKind 47dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyarimport javax.lang.model.type.TypeMirror 48dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyarimport javax.lang.model.util.SimpleAnnotationValueVisitor6 491600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar 501a74519922de68e007027d56aae9370ee21f31f9shepshapardclass EntityProcessor(baseContext: Context, 511a74519922de68e007027d56aae9370ee21f31f9shepshapard val element: TypeElement, 52757abd3002dff7725cde3cebdbf9bfeed691d2f9Yigit Boyar private val referenceStack: LinkedHashSet<Name> = LinkedHashSet()) { 53aa82fce1d73394bdc7f4c2510cf94a3572032b24Yigit Boyar val context = baseContext.fork(element) 541600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar 55aa82fce1d73394bdc7f4c2510cf94a3572032b24Yigit Boyar fun process(): Entity { 565124503104860e68981cc3e3092b95932586f66fYigit Boyar return context.cache.entities.get(Cache.EntityKey(element), { 575124503104860e68981cc3e3092b95932586f66fYigit Boyar doProcess() 585124503104860e68981cc3e3092b95932586f66fYigit Boyar }) 595124503104860e68981cc3e3092b95932586f66fYigit Boyar } 606f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas private fun doProcess(): Entity { 61ba069d50913c3fb250bb60ec310439db36895337Alan Viverette context.checker.hasAnnotation(element, androidx.room.Entity::class, 628bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar ProcessorErrors.ENTITY_MUST_BE_ANNOTATED_WITH_ENTITY) 6396cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar val pojo = PojoProcessor( 6496cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar baseContext = context, 6596cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar element = element, 6696cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar bindingScope = FieldProcessor.BindingScope.TWO_WAY, 671a74519922de68e007027d56aae9370ee21f31f9shepshapard parent = null, 681a74519922de68e007027d56aae9370ee21f31f9shepshapard referenceStack = referenceStack).process() 69092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar context.checker.check(pojo.relations.isEmpty(), element, RELATION_IN_ENTITY) 700fc66ddc60bdc71d5466bb1db1a218e5a3d9c1fcYigit Boyar val annotation = MoreElements.getAnnotationMirror(element, 71ba069d50913c3fb250bb60ec310439db36895337Alan Viverette androidx.room.Entity::class.java).orNull() 72dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar val tableName: String 732fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar val entityIndices: List<IndexInput> 74875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val foreignKeyInputs: List<ForeignKeyInput> 75dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar val inheritSuperIndices: Boolean 76faf0a687b4b6022196c4a9223d2c901b1e9cebe7Yigit Boyar if (annotation != null) { 77875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar tableName = extractTableName(element, annotation) 78dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar entityIndices = extractIndices(annotation, tableName) 79dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar inheritSuperIndices = AnnotationMirrors 80dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar .getAnnotationValue(annotation, "inheritSuperIndices").getAsBoolean(false) 81875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar foreignKeyInputs = extractForeignKeys(annotation) 820fc66ddc60bdc71d5466bb1db1a218e5a3d9c1fcYigit Boyar } else { 830fc66ddc60bdc71d5466bb1db1a218e5a3d9c1fcYigit Boyar tableName = element.simpleName.toString() 84875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar foreignKeyInputs = emptyList() 85dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar entityIndices = emptyList() 86dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar inheritSuperIndices = false 870fc66ddc60bdc71d5466bb1db1a218e5a3d9c1fcYigit Boyar } 88faf0a687b4b6022196c4a9223d2c901b1e9cebe7Yigit Boyar context.checker.notBlank(tableName, element, 89faf0a687b4b6022196c4a9223d2c901b1e9cebe7Yigit Boyar ProcessorErrors.ENTITY_TABLE_NAME_CANNOT_BE_EMPTY) 90dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar 91dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar val fieldIndices = pojo.fields 92c21394d1dcb24518061aabde879baff891a426e3Sergey Vasilinets .filter { it.indexed }.mapNotNull { 93dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar if (it.parent != null) { 94dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar it.indexed = false 954d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar context.logger.w(Warning.INDEX_FROM_EMBEDDED_FIELD_IS_DROPPED, it.element, 964d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar ProcessorErrors.droppedEmbeddedFieldIndex( 97dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar it.getPath(), element.qualifiedName.toString())) 98dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar null 99dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } else if (it.element.enclosingElement != element && !inheritSuperIndices) { 100dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar it.indexed = false 101dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar context.logger.w(Warning.INDEX_FROM_PARENT_FIELD_IS_DROPPED, 102dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar ProcessorErrors.droppedSuperClassFieldIndex( 103dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar it.columnName, element.toString(), 104dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar it.element.enclosingElement.toString() 105dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar )) 106dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar null 107dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } else { 1082fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar IndexInput( 1096728af1603c6082a13533b830a9ef0bbc51ba827Yigit Boyar name = createIndexName(listOf(it.columnName), tableName), 110dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar unique = false, 1116728af1603c6082a13533b830a9ef0bbc51ba827Yigit Boyar columnNames = listOf(it.columnName) 112dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar ) 113dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 114c21394d1dcb24518061aabde879baff891a426e3Sergey Vasilinets } 115dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar val superIndices = loadSuperIndices(element.superclass, tableName, inheritSuperIndices) 1162fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar val indexInputs = entityIndices + fieldIndices + superIndices 1172fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar val indices = validateAndCreateIndices(indexInputs, pojo) 118dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar 1194c22afcd70f11a45e350b5729e67dc9f27deb319Florina Muntenescu val primaryKey = findAndValidatePrimaryKey(pojo.fields, pojo.embeddedFields) 1202c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar val affinity = primaryKey.fields.firstOrNull()?.affinity ?: SQLTypeAffinity.TEXT 1212c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar context.checker.check( 1222c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar !primaryKey.autoGenerateId || affinity == SQLTypeAffinity.INTEGER, 1232c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar primaryKey.fields.firstOrNull()?.element ?: element, 1242c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar ProcessorErrors.AUTO_INCREMENTED_PRIMARY_KEY_IS_NOT_INT 1252c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar ) 126875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar 127875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val entityForeignKeys = validateAndCreateForeignKeyReferences(foreignKeyInputs, pojo) 128875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar checkIndicesForForeignKeys(entityForeignKeys, primaryKey, indices) 129875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar 130f3b01d87d24552e0d716aa6b002fcd54e2522adfYigit Boyar context.checker.check(SqlParser.isValidIdentifier(tableName), element, 131f3b01d87d24552e0d716aa6b002fcd54e2522adfYigit Boyar ProcessorErrors.INVALID_TABLE_NAME) 132f3b01d87d24552e0d716aa6b002fcd54e2522adfYigit Boyar pojo.fields.forEach { 133f3b01d87d24552e0d716aa6b002fcd54e2522adfYigit Boyar context.checker.check(SqlParser.isValidIdentifier(it.columnName), it.element, 134f3b01d87d24552e0d716aa6b002fcd54e2522adfYigit Boyar ProcessorErrors.INVALID_COLUMN_NAME) 135f3b01d87d24552e0d716aa6b002fcd54e2522adfYigit Boyar } 136f3b01d87d24552e0d716aa6b002fcd54e2522adfYigit Boyar 13796cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar val entity = Entity(element = element, 13896cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar tableName = tableName, 13996cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar type = pojo.type, 14096cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar fields = pojo.fields, 1414d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar embeddedFields = pojo.embeddedFields, 1422c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar indices = indices, 143875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar primaryKey = primaryKey, 144f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar foreignKeys = entityForeignKeys, 145f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar constructor = pojo.constructor) 1462c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar 147e6325fbeaa2e6759496ea2ca9a4d3d958df690d7Yigit Boyar return entity 1481600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar } 149dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar 150875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar private fun checkIndicesForForeignKeys(entityForeignKeys: List<ForeignKey>, 151875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar primaryKey: PrimaryKey, 152875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar indices: List<Index>) { 1536f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas fun covers(columnNames: List<String>, fields: List<Field>): Boolean = 154875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar fields.size >= columnNames.size && columnNames.withIndex().all { 155875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar fields[it.index].columnName == it.value 156875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 157875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar 158875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar entityForeignKeys.forEach { fKey -> 159875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val columnNames = fKey.childFields.map { it.columnName } 160875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val exists = covers(columnNames, primaryKey.fields) || indices.any { index -> 161875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar covers(columnNames, index.fields) 162875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 163875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar if (!exists) { 164875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar if (columnNames.size == 1) { 165875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar context.logger.w(Warning.MISSING_INDEX_ON_FOREIGN_KEY_CHILD, element, 166875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar ProcessorErrors.foreignKeyMissingIndexInChildColumn(columnNames[0])) 167875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } else { 168875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar context.logger.w(Warning.MISSING_INDEX_ON_FOREIGN_KEY_CHILD, element, 169875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar ProcessorErrors.foreignKeyMissingIndexInChildColumns(columnNames)) 170875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 171875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 172875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 173875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 174875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar 175875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar /** 176875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar * Does a validation on foreign keys except the parent table's columns. 177875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar */ 178875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar private fun validateAndCreateForeignKeyReferences(foreignKeyInputs: List<ForeignKeyInput>, 179875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar pojo: Pojo): List<ForeignKey> { 180875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return foreignKeyInputs.map { 181875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar if (it.onUpdate == null) { 182875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar context.logger.e(element, ProcessorErrors.INVALID_FOREIGN_KEY_ACTION) 183875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return@map null 184875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 185875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar if (it.onDelete == null) { 186875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar context.logger.e(element, ProcessorErrors.INVALID_FOREIGN_KEY_ACTION) 187875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return@map null 188875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 189875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar if (it.childColumns.isEmpty()) { 190875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar context.logger.e(element, ProcessorErrors.FOREIGN_KEY_EMPTY_CHILD_COLUMN_LIST) 191875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return@map null 192875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 193875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar if (it.parentColumns.isEmpty()) { 194875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar context.logger.e(element, ProcessorErrors.FOREIGN_KEY_EMPTY_PARENT_COLUMN_LIST) 195875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return@map null 196875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 197875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar if (it.childColumns.size != it.parentColumns.size) { 198875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar context.logger.e(element, ProcessorErrors.foreignKeyColumnNumberMismatch( 199875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar it.childColumns, it.parentColumns 200875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar )) 201875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return@map null 202875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 203875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val parentElement = try { 204875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar MoreTypes.asElement(it.parent) as TypeElement 205875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } catch (noClass: IllegalArgumentException) { 206875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar context.logger.e(element, ProcessorErrors.FOREIGN_KEY_CANNOT_FIND_PARENT) 207875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return@map null 208875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 209875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val parentAnnotation = MoreElements.getAnnotationMirror(parentElement, 210ba069d50913c3fb250bb60ec310439db36895337Alan Viverette androidx.room.Entity::class.java).orNull() 211875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar if (parentAnnotation == null) { 212875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar context.logger.e(element, 213875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar ProcessorErrors.foreignKeyNotAnEntity(parentElement.toString())) 214875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return@map null 215875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 216875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val tableName = extractTableName(parentElement, parentAnnotation) 217c21394d1dcb24518061aabde879baff891a426e3Sergey Vasilinets val fields = it.childColumns.mapNotNull { columnName -> 218875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val field = pojo.fields.find { it.columnName == columnName } 219875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar if (field == null) { 220875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar context.logger.e(pojo.element, 221875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar ProcessorErrors.foreignKeyChildColumnDoesNotExist(columnName, 222875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar pojo.fields.map { it.columnName })) 223875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 224875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar field 225c21394d1dcb24518061aabde879baff891a426e3Sergey Vasilinets } 226875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar if (fields.size != it.childColumns.size) { 227875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return@map null 228875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 229875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar ForeignKey( 230875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar parentTable = tableName, 231875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar childFields = fields, 232875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar parentColumns = it.parentColumns, 233875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar onDelete = it.onDelete, 234875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar onUpdate = it.onUpdate, 235875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar deferred = it.deferred 236875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar ) 237875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar }.filterNotNull() 238875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 239875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar 2406f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas private fun findAndValidatePrimaryKey( 2416f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas fields: List<Field>, embeddedFields: List<EmbeddedField>): PrimaryKey { 2422c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar val candidates = collectPrimaryKeysFromEntityAnnotations(element, fields) + 2432c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar collectPrimaryKeysFromPrimaryKeyAnnotations(fields) + 2444d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar collectPrimaryKeysFromEmbeddedFields(embeddedFields) 2452c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar 2462c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar context.checker.check(candidates.isNotEmpty(), element, ProcessorErrors.MISSING_PRIMARY_KEY) 247540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu 248540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu // 1. If a key is not autogenerated, but is Primary key or is part of Primary key we 249a1be0035ff9cb46339f02db942c990e44eab7075Florina Muntenescu // force the @NonNull annotation. If the key is a single Primary Key, Integer or Long, we 250a1be0035ff9cb46339f02db942c990e44eab7075Florina Muntenescu // don't force the @NonNull annotation since SQLite will automatically generate IDs. 251540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu // 2. If a key is autogenerate, we generate NOT NULL in table spec, but we don't require 252540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu // @NonNull annotation on the field itself. 253540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu candidates.filter { candidate -> !candidate.autoGenerateId } 254540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu .map { candidate -> 255540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu candidate.fields.map { field -> 256a1be0035ff9cb46339f02db942c990e44eab7075Florina Muntenescu if (candidate.fields.size > 1 || 257a1be0035ff9cb46339f02db942c990e44eab7075Florina Muntenescu (candidate.fields.size == 1 258a1be0035ff9cb46339f02db942c990e44eab7075Florina Muntenescu && field.affinity != SQLTypeAffinity.INTEGER)) { 259a1be0035ff9cb46339f02db942c990e44eab7075Florina Muntenescu context.checker.check(field.nonNull, field.element, 260a1be0035ff9cb46339f02db942c990e44eab7075Florina Muntenescu ProcessorErrors.primaryKeyNull(field.getPath())) 261a1be0035ff9cb46339f02db942c990e44eab7075Florina Muntenescu // Validate parents for nullability 262a1be0035ff9cb46339f02db942c990e44eab7075Florina Muntenescu var parent = field.parent 263a1be0035ff9cb46339f02db942c990e44eab7075Florina Muntenescu while (parent != null) { 264a1be0035ff9cb46339f02db942c990e44eab7075Florina Muntenescu val parentField = parent.field 265a1be0035ff9cb46339f02db942c990e44eab7075Florina Muntenescu context.checker.check(parentField.nonNull, 266a1be0035ff9cb46339f02db942c990e44eab7075Florina Muntenescu parentField.element, 267a1be0035ff9cb46339f02db942c990e44eab7075Florina Muntenescu ProcessorErrors.primaryKeyNull(parentField.getPath())) 268a1be0035ff9cb46339f02db942c990e44eab7075Florina Muntenescu parent = parentField.parent 269a1be0035ff9cb46339f02db942c990e44eab7075Florina Muntenescu } 2704c22afcd70f11a45e350b5729e67dc9f27deb319Florina Muntenescu } 271540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu } 272540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu } 273540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu 2742c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar if (candidates.size == 1) { 2752c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // easy :) 2762c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar return candidates.first() 2772c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 2782c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar 2792c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar return choosePrimaryKey(candidates, element) 2802c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 2812c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar 2822c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar /** 2832c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar * Check fields for @PrimaryKey. 2842c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar */ 2852c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar private fun collectPrimaryKeysFromPrimaryKeyAnnotations(fields: List<Field>): List<PrimaryKey> { 286c21394d1dcb24518061aabde879baff891a426e3Sergey Vasilinets return fields.mapNotNull { field -> 2872c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar MoreElements.getAnnotationMirror(field.element, 288ba069d50913c3fb250bb60ec310439db36895337Alan Viverette androidx.room.PrimaryKey::class.java).orNull()?.let { 2892c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar if (field.parent != null) { 2902c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // the field in the entity that contains this error. 2914d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar val grandParentField = field.parent.mRootParent.field.element 2922c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // bound for entity. 2932c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar context.fork(grandParentField).logger.w( 2944d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar Warning.PRIMARY_KEY_FROM_EMBEDDED_IS_DROPPED, 2952c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar grandParentField, 2964d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar ProcessorErrors.embeddedPrimaryKeyIsDropped( 2972c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar element.qualifiedName.toString(), field.name)) 2982c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar null 2992c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } else { 3002c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar PrimaryKey(declaredIn = field.element.enclosingElement, 3012c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar fields = listOf(field), 3022c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar autoGenerateId = AnnotationMirrors 3032c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar .getAnnotationValue(it, "autoGenerate") 3042c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar .getAsBoolean(false)) 3052c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 3062c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 307c21394d1dcb24518061aabde879baff891a426e3Sergey Vasilinets } 3082c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 3092c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar 3102c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar /** 3112c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar * Check classes for @Entity(primaryKeys = ?). 3122c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar */ 3136f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas private fun collectPrimaryKeysFromEntityAnnotations( 3146f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas typeElement: TypeElement, availableFields: List<Field>): List<PrimaryKey> { 3152c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar val myPkeys = MoreElements.getAnnotationMirror(typeElement, 316ba069d50913c3fb250bb60ec310439db36895337Alan Viverette androidx.room.Entity::class.java).orNull()?.let { 3172c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar val primaryKeyColumns = AnnotationMirrors.getAnnotationValue(it, "primaryKeys") 3182c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar .getAsStringList() 3192c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar if (primaryKeyColumns.isEmpty()) { 320c21394d1dcb24518061aabde879baff891a426e3Sergey Vasilinets emptyList() 3212c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } else { 322c21394d1dcb24518061aabde879baff891a426e3Sergey Vasilinets val fields = primaryKeyColumns.mapNotNull { pKeyColumnName -> 3232c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar val field = availableFields.firstOrNull { it.columnName == pKeyColumnName } 3242c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar context.checker.check(field != null, typeElement, 3252c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar ProcessorErrors.primaryKeyColumnDoesNotExist(pKeyColumnName, 3262c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar availableFields.map { it.columnName })) 3272c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar field 328c21394d1dcb24518061aabde879baff891a426e3Sergey Vasilinets } 3292c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar listOf(PrimaryKey(declaredIn = typeElement, 3302c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar fields = fields, 3312c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar autoGenerateId = false)) 3322c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 333c21394d1dcb24518061aabde879baff891a426e3Sergey Vasilinets } ?: emptyList() 3342c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // checks supers. 3352c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar val mySuper = typeElement.superclass 3362c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar val superPKeys = if (mySuper != null && mySuper.kind != TypeKind.NONE) { 3372c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // my super cannot see my fields so remove them. 3382c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar val remainingFields = availableFields.filterNot { 3392c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar it.element.enclosingElement == typeElement 3402c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 3412c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar collectPrimaryKeysFromEntityAnnotations( 3422c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar MoreTypes.asTypeElement(mySuper), remainingFields) 3432c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } else { 3442c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar emptyList() 3452c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 3462c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar return superPKeys + myPkeys 3472c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 3482c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar 3496f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas private fun collectPrimaryKeysFromEmbeddedFields( 3506f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas embeddedFields: List<EmbeddedField>): List<PrimaryKey> { 351c21394d1dcb24518061aabde879baff891a426e3Sergey Vasilinets return embeddedFields.mapNotNull { embeddedField -> 3524d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar MoreElements.getAnnotationMirror(embeddedField.field.element, 353ba069d50913c3fb250bb60ec310439db36895337Alan Viverette androidx.room.PrimaryKey::class.java).orNull()?.let { 3542c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar val autoGenerate = AnnotationMirrors 3552c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar .getAnnotationValue(it, "autoGenerate").getAsBoolean(false) 3564d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar context.checker.check(!autoGenerate || embeddedField.pojo.fields.size == 1, 3574d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar embeddedField.field.element, 3584d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar ProcessorErrors.AUTO_INCREMENT_EMBEDDED_HAS_MULTIPLE_FIELDS) 3594d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar PrimaryKey(declaredIn = embeddedField.field.element.enclosingElement, 3604d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar fields = embeddedField.pojo.fields, 3612c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar autoGenerateId = autoGenerate) 3622c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 363c21394d1dcb24518061aabde879baff891a426e3Sergey Vasilinets } 3642c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 3652c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar 3662c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // start from my element and check if anywhere in the list we can find the only well defined 3672c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // pkey, if so, use it. 3686f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas private fun choosePrimaryKey( 3696f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas candidates: List<PrimaryKey>, typeElement: TypeElement): PrimaryKey { 3702c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // If 1 of these primary keys is declared in this class, then it is the winner. Just print 3712c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // a note for the others. 3722c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // If 0 is declared, check the parent. 3732c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // If more than 1 primary key is declared in this class, it is an error. 3742c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar val myPKeys = candidates.filter { candidate -> 3752c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar candidate.declaredIn == typeElement 3762c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 3772c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar return if (myPKeys.size == 1) { 3782c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // just note, this is not worth an error or warning 3792c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar (candidates - myPKeys).forEach { 3802c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar context.logger.d(element, 3812c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar "${it.toHumanReadableString()} is" + 3822c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar " overridden by ${myPKeys.first().toHumanReadableString()}") 3832c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 3842c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar myPKeys.first() 3852c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } else if (myPKeys.isEmpty()) { 3862c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar // i have not declared anything, delegate to super 3872c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar val mySuper = typeElement.superclass 3882c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar if (mySuper != null && mySuper.kind != TypeKind.NONE) { 3892c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar return choosePrimaryKey(candidates, MoreTypes.asTypeElement(mySuper)) 3902c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 3912c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar PrimaryKey.MISSING 3922c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } else { 3932c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar context.logger.e(element, ProcessorErrors.multiplePrimaryKeyAnnotations( 3942c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar myPKeys.map(PrimaryKey::toHumanReadableString))) 3952c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar PrimaryKey.MISSING 3962c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 3972c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } 3982c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar 3996f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas private fun validateAndCreateIndices( 4006f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas inputs: List<IndexInput>, pojo: Pojo): List<Index> { 401dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar // check for columns 402c21394d1dcb24518061aabde879baff891a426e3Sergey Vasilinets val indices = inputs.mapNotNull { input -> 4036728af1603c6082a13533b830a9ef0bbc51ba827Yigit Boyar context.checker.check(input.columnNames.isNotEmpty(), element, 404dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar INDEX_COLUMNS_CANNOT_BE_EMPTY) 405c21394d1dcb24518061aabde879baff891a426e3Sergey Vasilinets val fields = input.columnNames.mapNotNull { columnName -> 4062fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar val field = pojo.fields.firstOrNull { 4076728af1603c6082a13533b830a9ef0bbc51ba827Yigit Boyar it.columnName == columnName 408dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 4092fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar context.checker.check(field != null, element, 4102fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar ProcessorErrors.indexColumnDoesNotExist( 4116728af1603c6082a13533b830a9ef0bbc51ba827Yigit Boyar columnName, pojo.fields.map { it.columnName } 4122fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar )) 4132fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar field 414c21394d1dcb24518061aabde879baff891a426e3Sergey Vasilinets } 4152fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar if (fields.isEmpty()) { 4162fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar null 4172fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar } else { 4182fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar Index(name = input.name, unique = input.unique, fields = fields) 419dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 420c21394d1dcb24518061aabde879baff891a426e3Sergey Vasilinets } 421dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar 422dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar // check for duplicate indices 423dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar indices 424dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar .groupBy { it.name } 425dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar .filter { it.value.size > 1 } 426dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar .forEach { 427dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar context.logger.e(element, ProcessorErrors.duplicateIndexInEntity(it.key)) 428dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 429dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar 4304d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar // see if any embedded field is an entity with indices, if so, report a warning 4314d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar pojo.embeddedFields.forEach { embedded -> 4324d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar val embeddedElement = embedded.pojo.element 4334d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar val subEntityAnnotation = MoreElements.getAnnotationMirror(embeddedElement, 434ba069d50913c3fb250bb60ec310439db36895337Alan Viverette androidx.room.Entity::class.java).orNull() 435dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar subEntityAnnotation?.let { 436dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar val subIndices = extractIndices(subEntityAnnotation, "") 437dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar if (subIndices.isNotEmpty()) { 4384d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar context.logger.w(Warning.INDEX_FROM_EMBEDDED_ENTITY_IS_DROPPED, 4394d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar embedded.field.element, ProcessorErrors.droppedEmbeddedIndex( 4404d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar entityName = embedded.pojo.typeName.toString(), 4414d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar fieldPath = embedded.field.getPath(), 442dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar grandParent = element.qualifiedName.toString())) 443dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 444dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 445dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 4462fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar return indices 447dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 448dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar 449dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar // check if parent is an Entity, if so, report its annotation indices 4506f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas private fun loadSuperIndices( 4516f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas typeMirror: TypeMirror?, tableName: String, inherit: Boolean): List<IndexInput> { 452dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar if (typeMirror == null || typeMirror.kind == TypeKind.NONE) { 453dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar return emptyList() 454dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 455dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar val parentElement = MoreTypes.asTypeElement(typeMirror) 456dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar val myIndices = MoreElements.getAnnotationMirror(parentElement, 457ba069d50913c3fb250bb60ec310439db36895337Alan Viverette androidx.room.Entity::class.java).orNull()?.let { annotation -> 458dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar val indices = extractIndices(annotation, tableName = "super") 4592c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar if (indices.isEmpty()) { 4602c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar emptyList() 4612c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar } else if (inherit) { 462dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar // rename them 463dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar indices.map { 4642fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar IndexInput( 4656728af1603c6082a13533b830a9ef0bbc51ba827Yigit Boyar name = createIndexName(it.columnNames, tableName), 466dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar unique = it.unique, 4676728af1603c6082a13533b830a9ef0bbc51ba827Yigit Boyar columnNames = it.columnNames) 468dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 469dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } else { 470dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar context.logger.w(Warning.INDEX_FROM_PARENT_IS_DROPPED, 471dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar parentElement, 472dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar ProcessorErrors.droppedSuperClassIndex( 473dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar childEntity = element.qualifiedName.toString(), 474dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar superEntity = parentElement.qualifiedName.toString())) 475dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar emptyList() 476dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 477dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } ?: emptyList() 478dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar return myIndices + loadSuperIndices(parentElement.superclass, tableName, inherit) 479dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 480dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar 481dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar companion object { 48271d73407ef17998685b29cc5c04cefb6d74e99c3Yigit Boyar fun extractTableName(element: TypeElement, annotation: AnnotationMirror): String { 483875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val annotationValue = AnnotationMirrors 484875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar .getAnnotationValue(annotation, "tableName").value.toString() 485875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return if (annotationValue == "") { 486875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar element.simpleName.toString() 487875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } else { 488875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar annotationValue 489875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 490875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 491875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar 4926f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas private fun extractIndices( 4936f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas annotation: AnnotationMirror, tableName: String): List<IndexInput> { 494dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar val arrayOfIndexAnnotations = AnnotationMirrors.getAnnotationValue(annotation, 495dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar "indices") 496dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar return INDEX_LIST_VISITOR.visit(arrayOfIndexAnnotations, tableName) 497dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 498dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar 499dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar private val INDEX_LIST_VISITOR = object 5002fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar : SimpleAnnotationValueVisitor6<List<IndexInput>, String>() { 5016f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas override fun visitArray( 5026f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas values: MutableList<out AnnotationValue>?, 5036f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas tableName: String 5046f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas ): List<IndexInput> { 505c21394d1dcb24518061aabde879baff891a426e3Sergey Vasilinets return values?.mapNotNull { 506dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar INDEX_VISITOR.visit(it, tableName) 507c21394d1dcb24518061aabde879baff891a426e3Sergey Vasilinets } ?: emptyList() 508dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 509dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 510dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar 5112fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar private val INDEX_VISITOR = object : SimpleAnnotationValueVisitor6<IndexInput?, String>() { 5122fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar override fun visitAnnotation(a: AnnotationMirror?, tableName: String): IndexInput? { 513875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val fieldInput = getAnnotationValue(a, "value").getAsStringList() 514875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val unique = getAnnotationValue(a, "unique").getAsBoolean(false) 515875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val nameValue = getAnnotationValue(a, "name") 516dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar .getAsString("") 517dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar val name = if (nameValue == null || nameValue == "") { 5182fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar createIndexName(fieldInput, tableName) 519dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } else { 520dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar nameValue 521dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 5222fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar return IndexInput(name, unique, fieldInput) 523dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 524dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 525dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar 526dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar private fun createIndexName(columnNames: List<String>, tableName: String): String { 5272db0875dfb15f3d909e7721bd97e3544d0fe9ae1Yigit Boyar return Index.DEFAULT_PREFIX + tableName + "_" + columnNames.joinToString("_") 528dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 529875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar 530875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar private fun extractForeignKeys(annotation: AnnotationMirror): List<ForeignKeyInput> { 531875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val arrayOfForeignKeyAnnotations = getAnnotationValue(annotation, "foreignKeys") 532875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return FOREIGN_KEY_LIST_VISITOR.visit(arrayOfForeignKeyAnnotations) 533875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 534875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar 535875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar private val FOREIGN_KEY_LIST_VISITOR = object 536875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar : SimpleAnnotationValueVisitor6<List<ForeignKeyInput>, Void?>() { 5376f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas override fun visitArray( 5386f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas values: MutableList<out AnnotationValue>?, 5396f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas void: Void? 5406f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas ): List<ForeignKeyInput> { 541c21394d1dcb24518061aabde879baff891a426e3Sergey Vasilinets return values?.mapNotNull { 542875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar FOREIGN_KEY_VISITOR.visit(it) 543c21394d1dcb24518061aabde879baff891a426e3Sergey Vasilinets } ?: emptyList() 544875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 545875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 546875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar 547875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar private val FOREIGN_KEY_VISITOR = object : SimpleAnnotationValueVisitor6<ForeignKeyInput?, 548875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar Void?>() { 549875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar override fun visitAnnotation(a: AnnotationMirror?, void: Void?): ForeignKeyInput? { 550875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val entityClass = try { 551875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar getAnnotationValue(a, "entity").toType() 552875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } catch (notPresent: TypeNotPresentException) { 553875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return null 554875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 555875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val parentColumns = getAnnotationValue(a, "parentColumns").getAsStringList() 556875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val childColumns = getAnnotationValue(a, "childColumns").getAsStringList() 557875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val onDeleteInput = getAnnotationValue(a, "onDelete").getAsInt() 558875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val onUpdateInput = getAnnotationValue(a, "onUpdate").getAsInt() 559875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val deferred = getAnnotationValue(a, "deferred").getAsBoolean(true) 560875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val onDelete = ForeignKeyAction.fromAnnotationValue(onDeleteInput) 561875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar val onUpdate = ForeignKeyAction.fromAnnotationValue(onUpdateInput) 562875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar return ForeignKeyInput( 563875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar parent = entityClass, 564875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar parentColumns = parentColumns, 565875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar childColumns = childColumns, 566875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar onDelete = onDelete, 567875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar onUpdate = onUpdate, 568875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar deferred = deferred) 569875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 570875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar } 571dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar } 5722fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar 5732fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar /** 5742fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar * processed Index annotation output 5752fa2ccde296e35f2850b227065c27f27e74bce10Yigit Boyar */ 576875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar data class IndexInput(val name: String, val unique: Boolean, val columnNames: List<String>) 577875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar 578875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar /** 579875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar * ForeignKey, before it is processed in the context of a database. 580875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar */ 5816f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas data class ForeignKeyInput( 5826f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas val parent: TypeMirror, 5836f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas val parentColumns: List<String>, 5846f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas val childColumns: List<String>, 5856f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas val onDelete: ForeignKeyAction?, 5866f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas val onUpdate: ForeignKeyAction?, 5876f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas val deferred: Boolean) 5881600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar} 589