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