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