1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.arch.persistence.room.processor
18
19import android.arch.persistence.room.Relation
20import android.arch.persistence.room.ColumnInfo
21import android.arch.persistence.room.Embedded
22import android.arch.persistence.room.Ignore
23import android.arch.persistence.room.ext.getAllFieldsIncludingPrivateSupers
24import android.arch.persistence.room.ext.getAnnotationValue
25import android.arch.persistence.room.ext.getAsString
26import android.arch.persistence.room.ext.getAsStringList
27import android.arch.persistence.room.ext.hasAnnotation
28import android.arch.persistence.room.ext.hasAnyOf
29import android.arch.persistence.room.ext.isCollection
30import android.arch.persistence.room.ext.toClassType
31import android.arch.persistence.room.processor.ProcessorErrors.CANNOT_FIND_GETTER_FOR_FIELD
32import android.arch.persistence.room.processor.ProcessorErrors.CANNOT_FIND_SETTER_FOR_FIELD
33import android.arch.persistence.room.processor.ProcessorErrors.CANNOT_FIND_TYPE
34import android.arch.persistence.room.processor.ProcessorErrors.POJO_FIELD_HAS_DUPLICATE_COLUMN_NAME
35import android.arch.persistence.room.processor.cache.Cache
36import android.arch.persistence.room.vo.CallType
37import android.arch.persistence.room.vo.Constructor
38import android.arch.persistence.room.vo.Field
39import android.arch.persistence.room.vo.FieldGetter
40import android.arch.persistence.room.vo.EmbeddedField
41import android.arch.persistence.room.vo.Entity
42import android.arch.persistence.room.vo.FieldSetter
43import android.arch.persistence.room.vo.Pojo
44import com.google.auto.common.AnnotationMirrors
45import com.google.auto.common.MoreElements
46import com.google.auto.common.MoreTypes
47import javax.lang.model.element.Element
48import javax.lang.model.element.ExecutableElement
49import javax.lang.model.element.Modifier
50import javax.lang.model.element.Modifier.ABSTRACT
51import javax.lang.model.element.Modifier.PRIVATE
52import javax.lang.model.element.Modifier.PROTECTED
53import javax.lang.model.element.Modifier.PUBLIC
54import javax.lang.model.element.Modifier.STATIC
55import javax.lang.model.element.Modifier.TRANSIENT
56import javax.lang.model.element.TypeElement
57import javax.lang.model.element.VariableElement
58import javax.lang.model.type.DeclaredType
59import javax.lang.model.type.TypeKind
60import javax.lang.model.type.TypeMirror
61import javax.lang.model.util.ElementFilter
62
63/**
64 * Processes any class as if it is a Pojo.
65 */
66class PojoProcessor(baseContext: Context, val element: TypeElement,
67                    val bindingScope: FieldProcessor.BindingScope,
68                    val parent: EmbeddedField?) {
69    val context = baseContext.fork(element)
70    companion object {
71        val PROCESSED_ANNOTATIONS = listOf(ColumnInfo::class, Embedded::class,
72                    Relation::class)
73    }
74    fun process() : Pojo {
75        return context.cache.pojos.get(Cache.PojoKey(element, bindingScope, parent), {
76            doProcess()
77        })
78    }
79
80    private fun doProcess(): Pojo {
81        // TODO handle recursion: b/35980205
82        val declaredType = MoreTypes.asDeclared(element.asType())
83        // TODO handle conflicts with super: b/35568142
84        val allFields = element.getAllFieldsIncludingPrivateSupers(context.processingEnv)
85                .filter {
86                    !it.hasAnnotation(Ignore::class)
87                            && !it.hasAnyOf(STATIC)
88                            && (!it.hasAnyOf(TRANSIENT)
89                                    || it.hasAnnotation(ColumnInfo::class)
90                                    || it.hasAnnotation(Embedded::class)
91                                    || it.hasAnnotation(Relation::class))
92                }
93                .groupBy { field ->
94                    context.checker.check(
95                            PROCESSED_ANNOTATIONS.count { field.hasAnnotation(it) } < 2, field,
96                            ProcessorErrors.CANNOT_USE_MORE_THAN_ONE_POJO_FIELD_ANNOTATION
97                    )
98                    if (field.hasAnnotation(Embedded::class)) {
99                        Embedded::class
100                    } else if (field.hasAnnotation(Relation::class)) {
101                        Relation::class
102                    } else {
103                        null
104                    }
105                }
106        val myFields = allFields[null]
107                ?.map {
108                    FieldProcessor(
109                            baseContext = context,
110                            containing = declaredType,
111                            element = it,
112                            bindingScope = bindingScope,
113                            fieldParent = parent).process()
114                } ?: emptyList()
115
116        val embeddedFields = allFields[Embedded::class]
117                ?.map {
118                    processEmbeddedField(declaredType, it)
119                } ?: emptyList()
120        val subFields = embeddedFields.flatMap { it.pojo.fields }
121
122        val fields = myFields + subFields
123
124        val myRelationsList = allFields[Relation::class]
125                ?.map {
126                    processRelationField(fields, declaredType, it)
127                }
128                ?.filterNotNull() ?: emptyList()
129
130        val subRelations = embeddedFields.flatMap { it.pojo.relations }
131
132        val relations = myRelationsList + subRelations
133
134        fields.groupBy { it.columnName }
135                .filter { it.value.size > 1 }
136                .forEach {
137                    context.logger.e(element, ProcessorErrors.pojoDuplicateFieldNames(
138                            it.key, it.value.map(Field::getPath)
139                    ))
140                    it.value.forEach {
141                        context.logger.e(it.element, POJO_FIELD_HAS_DUPLICATE_COLUMN_NAME)
142                    }
143                }
144        val methods = MoreElements.getLocalAndInheritedMethods(element,
145                context.processingEnv.elementUtils)
146                .filter {
147                    !it.hasAnyOf(PRIVATE, ABSTRACT, STATIC)
148                            && !it.hasAnnotation(Ignore::class)
149                }
150                .map { MoreElements.asExecutable(it) }
151
152        val getterCandidates = methods.filter {
153            it.parameters.size == 0 && it.returnType.kind != TypeKind.VOID
154        }
155
156        val setterCandidates = methods.filter {
157            it.parameters.size == 1 && it.returnType.kind == TypeKind.VOID
158        }
159        // don't try to find a constructor for binding to statement.
160        val constructor = if (bindingScope == FieldProcessor.BindingScope.BIND_TO_STMT) {
161            // we don't need to construct this POJO.
162            null
163        } else {
164            chooseConstructor(myFields, embeddedFields)
165        }
166
167        assignGetters(myFields, getterCandidates)
168        assignSetters(myFields, setterCandidates, constructor)
169
170        embeddedFields.forEach {
171            assignGetter(it.field, getterCandidates)
172            assignSetter(it.field, setterCandidates, constructor)
173        }
174
175        myRelationsList.forEach {
176            assignGetter(it.field, getterCandidates)
177            assignSetter(it.field, setterCandidates, constructor)
178        }
179
180        val pojo = Pojo(element = element,
181                type = declaredType,
182                fields = fields,
183                embeddedFields = embeddedFields,
184                relations = relations,
185                constructor = constructor)
186        return pojo
187    }
188
189    private fun chooseConstructor(myFields: List<Field>, embedded: List<EmbeddedField>)
190            : Constructor? {
191        val constructors = ElementFilter.constructorsIn(element.enclosedElements)
192                .filterNot { it.hasAnnotation(Ignore::class) || it.hasAnyOf(PRIVATE) }
193        val fieldMap = myFields.associateBy { it.name }
194        val embeddedMap = embedded.associateBy { it.field.name }
195        val typeUtils = context.processingEnv.typeUtils
196        val failedConstructors = mutableMapOf<ExecutableElement, List<Constructor.Param?>>()
197        val goodConstructors = constructors.map { constructor ->
198            val params = constructor.parameters.map param@ { param ->
199                val paramName = param.simpleName.toString()
200                val paramType = param.asType()
201
202                val matches = fun(field: Field?): Boolean {
203                    return if (field == null) {
204                        false
205                    } else if (!field.nameWithVariations.contains(paramName)) {
206                        false
207                    } else {
208                        typeUtils.isAssignable(paramType, field.type)
209                    }
210                }
211
212                val exactFieldMatch = fieldMap[paramName]
213
214                if (matches(exactFieldMatch)) {
215                    return@param Constructor.FieldParam(exactFieldMatch!!)
216                }
217                val exactEmbeddedMatch = embeddedMap[paramName]
218                if (matches(exactEmbeddedMatch?.field)) {
219                    return@param Constructor.EmbeddedParam(exactEmbeddedMatch!!)
220                }
221
222                val matchingFields = myFields.filter {
223                    matches(it)
224                }
225                val embeddedMatches = embedded.filter {
226                    matches(it.field)
227                }
228                if (matchingFields.isEmpty() && embeddedMatches.isEmpty()) {
229                    null
230                } else if (matchingFields.size + embeddedMatches.size == 1) {
231                    if (matchingFields.isNotEmpty()) {
232                        Constructor.FieldParam(matchingFields.first())
233                    } else {
234                        Constructor.EmbeddedParam(embeddedMatches.first())
235                    }
236                } else {
237                    context.logger.e(param, ProcessorErrors.ambigiousConstructor(
238                            pojo = element.qualifiedName.toString(),
239                            paramName = param.simpleName.toString(),
240                            matchingFields = matchingFields.map { it.getPath() }
241                                    + embedded.map { it.field.getPath() }
242                    ))
243                    null
244                }
245            }
246            if (params.any { it == null }) {
247                failedConstructors.put(constructor, params)
248                null
249            } else {
250                @Suppress("UNCHECKED_CAST")
251                Constructor(constructor, params as List<Constructor.Param>)
252            }
253        }.filterNotNull()
254        if (goodConstructors.isEmpty()) {
255            if (failedConstructors.isNotEmpty()) {
256                val failureMsg = failedConstructors.entries.joinToString("\n") { entry ->
257                    val paramsMatching = entry.key.parameters.withIndex().joinToString(", ") {
258                        "${it.value.simpleName} : ${entry.value[it.index]?.log()}"
259                    }
260                    "${entry.key} : [$paramsMatching]"
261                }
262                context.logger.e(element, ProcessorErrors.MISSING_POJO_CONSTRUCTOR +
263                        "\nTried the following constructors but they failed to match:\n$failureMsg")
264            }
265            context.logger.e(element, ProcessorErrors.MISSING_POJO_CONSTRUCTOR)
266            return null
267        }
268        if (goodConstructors.size > 1) {
269            goodConstructors.forEach {
270                context.logger.e(it.element, ProcessorErrors.TOO_MANY_POJO_CONSTRUCTORS)
271            }
272            return null
273        }
274        return goodConstructors.first()
275    }
276
277    private fun processEmbeddedField(declaredType: DeclaredType?, it: Element): EmbeddedField {
278        val fieldPrefix = it.getAnnotationValue(Embedded::class.java, "prefix")
279                ?.toString() ?: ""
280        val inheritedPrefix = parent?.prefix ?: ""
281        val embeddedField = Field(
282                it,
283                it.simpleName.toString(),
284                type = context.processingEnv.typeUtils.asMemberOf(declaredType, it),
285                affinity = null,
286                parent = parent)
287        val subParent = EmbeddedField(
288                field = embeddedField,
289                prefix = inheritedPrefix + fieldPrefix,
290                parent = parent)
291        val asVariable = MoreElements.asVariable(it)
292        subParent.pojo = PojoProcessor(baseContext = context.fork(it),
293                element = MoreTypes.asTypeElement(asVariable.asType()),
294                bindingScope = bindingScope,
295                parent = subParent).process()
296        return subParent
297    }
298
299    private fun processRelationField(myFields : List<Field>, container: DeclaredType?,
300                                     relationElement: VariableElement)
301            : android.arch.persistence.room.vo.Relation? {
302        val annotation = MoreElements.getAnnotationMirror(relationElement, Relation::class.java)
303                .orNull()!!
304        val parentColumnInput = AnnotationMirrors.getAnnotationValue(annotation, "parentColumn")
305                .getAsString("") ?: ""
306
307        val parentField = myFields.firstOrNull {
308            it.columnName == parentColumnInput
309        }
310        if (parentField == null) {
311            context.logger.e(relationElement,
312                    ProcessorErrors.relationCannotFindParentEntityField(
313                            entityName = element.qualifiedName.toString(),
314                            columnName = parentColumnInput,
315                            availableColumns = myFields.map { it.columnName }))
316            return null
317        }
318        // parse it as an entity.
319        val asMember = MoreTypes
320                .asMemberOf(context.processingEnv.typeUtils, container, relationElement)
321        if (asMember.kind == TypeKind.ERROR) {
322            context.logger.e(ProcessorErrors.CANNOT_FIND_TYPE, element)
323            return null
324        }
325        val declared = MoreTypes.asDeclared(asMember)
326        if (!declared.isCollection()) {
327            context.logger.e(relationElement, ProcessorErrors.RELATION_NOT_COLLECTION)
328            return null
329        }
330        val typeArg = declared.typeArguments.first()
331        if (typeArg.kind == TypeKind.ERROR) {
332            context.logger.e(MoreTypes.asTypeElement(typeArg), CANNOT_FIND_TYPE)
333            return null
334        }
335        val typeArgElement = MoreTypes.asTypeElement(typeArg)
336        val entityClassInput = AnnotationMirrors
337                .getAnnotationValue(annotation, "entity").toClassType()
338        val pojo : Pojo
339        val entity : Entity
340        if (entityClassInput == null
341                || MoreTypes.isTypeOf(Any::class.java, entityClassInput)) {
342            entity = EntityProcessor(context, typeArgElement).process()
343            pojo = entity
344        } else {
345            entity = EntityProcessor(context, MoreTypes.asTypeElement(entityClassInput)).process()
346            pojo = PojoProcessor(baseContext = context,
347                    element = typeArgElement,
348                    bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR,
349                    parent = parent).process()
350        }
351        // now find the field in the entity.
352        val entityColumnInput = AnnotationMirrors.getAnnotationValue(annotation, "entityColumn")
353                .getAsString() ?: ""
354        val entityField = entity.fields.firstOrNull {
355            it.columnName == entityColumnInput
356        }
357
358        if (entityField == null) {
359            context.logger.e(relationElement,
360                    ProcessorErrors.relationCannotFindEntityField(
361                            entityName = entity.typeName.toString(),
362                            columnName = entityColumnInput,
363                            availableColumns = entity.fields.map { it.columnName }))
364            return null
365        }
366
367        val field = Field(
368                element = relationElement,
369                name = relationElement.simpleName.toString(),
370                type = context.processingEnv.typeUtils.asMemberOf(container, relationElement),
371                affinity = null,
372                parent = parent)
373
374        val projection = AnnotationMirrors.getAnnotationValue(annotation, "projection")
375                .getAsStringList()
376        if(projection.isNotEmpty()) {
377            val missingColumns = projection.filterNot { columnName ->
378                entity.fields.any { columnName == it.columnName }
379            }
380            if (missingColumns.isNotEmpty()) {
381                context.logger.e(relationElement,
382                        ProcessorErrors.relationBadProject(entity.typeName.toString(),
383                                missingColumns, entity.fields.map { it.columnName }))
384            }
385        }
386
387        // if types don't match, row adapter prints a warning
388        return android.arch.persistence.room.vo.Relation(
389                entity = entity,
390                pojo = pojo,
391                field = field,
392                parentField = parentField,
393                entityField = entityField,
394                projection = projection
395        )
396    }
397
398    private fun assignGetters(fields: List<Field>, getterCandidates: List<ExecutableElement>) {
399        fields.forEach { field ->
400            assignGetter(field, getterCandidates)
401        }
402    }
403
404    private fun assignGetter(field: Field, getterCandidates: List<ExecutableElement>) {
405        val success = chooseAssignment(field = field,
406                candidates = getterCandidates,
407                nameVariations = field.getterNameWithVariations,
408                getType = { method ->
409                    method.returnType
410                },
411                assignFromField = {
412                    field.getter = FieldGetter(
413                            name = field.name,
414                            type = field.type,
415                            callType = CallType.FIELD)
416                },
417                assignFromMethod = { match ->
418                    field.getter = FieldGetter(
419                            name = match.simpleName.toString(),
420                            type = match.returnType,
421                            callType = CallType.METHOD)
422                },
423                reportAmbiguity = { matching ->
424                    context.logger.e(field.element,
425                            ProcessorErrors.tooManyMatchingGetters(field, matching))
426                })
427        context.checker.check(success, field.element, CANNOT_FIND_GETTER_FOR_FIELD)
428    }
429
430    private fun assignSetters(fields: List<Field>, setterCandidates: List<ExecutableElement>,
431                              constructor : Constructor?) {
432        fields.forEach { field ->
433            assignSetter(field, setterCandidates, constructor)
434        }
435    }
436
437    private fun assignSetter(field: Field, setterCandidates: List<ExecutableElement>,
438                             constructor: Constructor?) {
439        if (constructor != null && constructor.hasField(field)) {
440            field.setter = FieldSetter(field.name, field.type, CallType.CONSTRUCTOR)
441            return
442        }
443        val success = chooseAssignment(field = field,
444                candidates = setterCandidates,
445                nameVariations = field.setterNameWithVariations,
446                getType = { method ->
447                    method.parameters.first().asType()
448                },
449                assignFromField = {
450                    field.setter = FieldSetter(
451                            name = field.name,
452                            type = field.type,
453                            callType = CallType.FIELD)
454                },
455                assignFromMethod = { match ->
456                    val paramType = match.parameters.first().asType()
457                    field.setter = FieldSetter(
458                            name = match.simpleName.toString(),
459                            type = paramType,
460                            callType = CallType.METHOD)
461                },
462                reportAmbiguity = { matching ->
463                    context.logger.e(field.element,
464                            ProcessorErrors.tooManyMatchingSetter(field, matching))
465                })
466        context.checker.check(success, field.element, CANNOT_FIND_SETTER_FOR_FIELD)
467    }
468
469    /**
470     * Finds a setter/getter from available list of methods.
471     * It returns true if assignment is successful, false otherwise.
472     * At worst case, it sets to the field as if it is accessible so that the rest of the
473     * compilation can continue.
474     */
475    private fun chooseAssignment(field: Field, candidates: List<ExecutableElement>,
476                                 nameVariations: List<String>,
477                                 getType: (ExecutableElement) -> TypeMirror,
478                                 assignFromField: () -> Unit,
479                                 assignFromMethod: (ExecutableElement) -> Unit,
480                                 reportAmbiguity: (List<String>) -> Unit): Boolean {
481        if (field.element.hasAnyOf(PUBLIC)) {
482            assignFromField()
483            return true
484        }
485        val types = context.processingEnv.typeUtils
486
487        val matching = candidates
488                .filter {
489                    types.isAssignable(getType(it), field.element.asType())
490                            && (field.nameWithVariations.contains(it.simpleName.toString())
491                            || nameVariations.contains(it.simpleName.toString()))
492                }
493                .groupBy {
494                    if (it.hasAnyOf(PUBLIC)) PUBLIC else PROTECTED
495                }
496        if (matching.isEmpty()) {
497            // we always assign to avoid NPEs in the rest of the compilation.
498            assignFromField()
499            // if field is not private, assume it works (if we are on the same package).
500            // if not, compiler will tell, we didn't have any better alternative anyways.
501            return !field.element.hasAnyOf(PRIVATE)
502        }
503        val match = verifyAndChooseOneFrom(matching[PUBLIC], reportAmbiguity) ?:
504                verifyAndChooseOneFrom(matching[PROTECTED], reportAmbiguity)
505        if (match == null) {
506            assignFromField()
507            return false
508        } else {
509            assignFromMethod(match)
510            return true
511        }
512    }
513
514    private fun verifyAndChooseOneFrom(candidates: List<ExecutableElement>?,
515                                       reportAmbiguity: (List<String>) -> Unit)
516            : ExecutableElement? {
517        if (candidates == null) {
518            return null
519        }
520        if (candidates.size > 1) {
521            reportAmbiguity(candidates.map { it.simpleName.toString() })
522        }
523        return candidates.first()
524    }
525}
526