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 androidx.room.processor
18
19import androidx.room.ColumnInfo
20import androidx.room.Embedded
21import androidx.room.Ignore
22import androidx.room.Relation
23import androidx.room.ext.KotlinMetadataProcessor
24import androidx.room.ext.extendsBoundOrSelf
25import androidx.room.ext.getAllFieldsIncludingPrivateSupers
26import androidx.room.ext.getAnnotationValue
27import androidx.room.ext.getAsString
28import androidx.room.ext.getAsStringList
29import androidx.room.ext.hasAnnotation
30import androidx.room.ext.hasAnyOf
31import androidx.room.ext.isAssignableWithoutVariance
32import androidx.room.ext.isCollection
33import androidx.room.ext.toClassType
34import androidx.room.ext.typeName
35import androidx.room.processor.ProcessorErrors.CANNOT_FIND_GETTER_FOR_FIELD
36import androidx.room.processor.ProcessorErrors.CANNOT_FIND_SETTER_FOR_FIELD
37import androidx.room.processor.ProcessorErrors.CANNOT_FIND_TYPE
38import androidx.room.processor.ProcessorErrors.POJO_FIELD_HAS_DUPLICATE_COLUMN_NAME
39import androidx.room.processor.cache.Cache
40import androidx.room.vo.CallType
41import androidx.room.vo.Constructor
42import androidx.room.vo.EmbeddedField
43import androidx.room.vo.Entity
44import androidx.room.vo.Field
45import androidx.room.vo.FieldGetter
46import androidx.room.vo.FieldSetter
47import androidx.room.vo.Pojo
48import androidx.room.vo.PojoMethod
49import androidx.room.vo.Warning
50import com.google.auto.common.AnnotationMirrors
51import com.google.auto.common.MoreElements
52import com.google.auto.common.MoreTypes
53import me.eugeniomarletti.kotlin.metadata.KotlinClassMetadata
54import me.eugeniomarletti.kotlin.metadata.kotlinMetadata
55import javax.annotation.processing.ProcessingEnvironment
56import javax.lang.model.element.ExecutableElement
57import javax.lang.model.element.Modifier.ABSTRACT
58import javax.lang.model.element.Modifier.PRIVATE
59import javax.lang.model.element.Modifier.PROTECTED
60import javax.lang.model.element.Modifier.PUBLIC
61import javax.lang.model.element.Modifier.STATIC
62import javax.lang.model.element.Modifier.TRANSIENT
63import javax.lang.model.element.Name
64import javax.lang.model.element.TypeElement
65import javax.lang.model.element.VariableElement
66import javax.lang.model.type.DeclaredType
67import javax.lang.model.type.TypeKind
68import javax.lang.model.type.TypeMirror
69import javax.lang.model.util.ElementFilter
70
71/**
72 * Processes any class as if it is a Pojo.
73 */
74class PojoProcessor(
75        baseContext: Context,
76        val element: TypeElement,
77        val bindingScope: FieldProcessor.BindingScope,
78        val parent: EmbeddedField?,
79        val referenceStack: LinkedHashSet<Name> = LinkedHashSet())
80    : KotlinMetadataProcessor {
81    val context = baseContext.fork(element)
82
83    // for KotlinMetadataUtils
84    override val processingEnv: ProcessingEnvironment
85        get() = context.processingEnv
86
87    // opportunistic kotlin metadata
88    private val kotlinMetadata by lazy {
89        try {
90            element.kotlinMetadata
91        } catch (throwable: Throwable) {
92            context.logger.d(element, "failed to read get kotlin metadata from %s", element)
93        } as? KotlinClassMetadata
94    }
95
96    companion object {
97        val PROCESSED_ANNOTATIONS = listOf(ColumnInfo::class, Embedded::class,
98                Relation::class)
99    }
100
101    fun process(): Pojo {
102        return context.cache.pojos.get(Cache.PojoKey(element, bindingScope, parent), {
103            referenceStack.add(element.qualifiedName)
104            try {
105                doProcess()
106            } finally {
107                referenceStack.remove(element.qualifiedName)
108            }
109        })
110    }
111
112    private fun doProcess(): Pojo {
113        val declaredType = MoreTypes.asDeclared(element.asType())
114        // TODO handle conflicts with super: b/35568142
115        val allFields = element.getAllFieldsIncludingPrivateSupers(context.processingEnv)
116                .filter {
117                    !it.hasAnnotation(Ignore::class)
118                            && !it.hasAnyOf(STATIC)
119                            && (!it.hasAnyOf(TRANSIENT)
120                            || it.hasAnnotation(ColumnInfo::class)
121                            || it.hasAnnotation(Embedded::class)
122                            || it.hasAnnotation(Relation::class))
123                }
124                .groupBy { field ->
125                    context.checker.check(
126                            PROCESSED_ANNOTATIONS.count { field.hasAnnotation(it) } < 2, field,
127                            ProcessorErrors.CANNOT_USE_MORE_THAN_ONE_POJO_FIELD_ANNOTATION
128                    )
129                    if (field.hasAnnotation(Embedded::class)) {
130                        Embedded::class
131                    } else if (field.hasAnnotation(Relation::class)) {
132                        Relation::class
133                    } else {
134                        null
135                    }
136                }
137
138        val myFields = allFields[null]
139                ?.map {
140                    FieldProcessor(
141                            baseContext = context,
142                            containing = declaredType,
143                            element = it,
144                            bindingScope = bindingScope,
145                            fieldParent = parent).process()
146                } ?: emptyList()
147
148        val embeddedFields =
149                allFields[Embedded::class]
150                        ?.mapNotNull {
151                            processEmbeddedField(declaredType, it)
152                        }
153                        ?: emptyList()
154
155        val subFields = embeddedFields.flatMap { it.pojo.fields }
156        val fields = myFields + subFields
157
158        val myRelationsList = allFields[Relation::class]
159                ?.mapNotNull {
160                    processRelationField(fields, declaredType, it)
161                }
162                ?: emptyList()
163
164        val subRelations = embeddedFields.flatMap { it.pojo.relations }
165        val relations = myRelationsList + subRelations
166
167        fields.groupBy { it.columnName }
168                .filter { it.value.size > 1 }
169                .forEach {
170                    context.logger.e(element, ProcessorErrors.pojoDuplicateFieldNames(
171                            it.key, it.value.map(Field::getPath)
172                    ))
173                    it.value.forEach {
174                        context.logger.e(it.element, POJO_FIELD_HAS_DUPLICATE_COLUMN_NAME)
175                    }
176                }
177
178        val methods = MoreElements.getLocalAndInheritedMethods(element,
179                context.processingEnv.elementUtils)
180                .filter {
181                    !it.hasAnyOf(PRIVATE, ABSTRACT, STATIC)
182                            && !it.hasAnnotation(Ignore::class)
183                }
184                .map { MoreElements.asExecutable(it) }
185                .map {
186                    PojoMethodProcessor(
187                            context = context,
188                            element = it,
189                            owner = declaredType
190                    ).process()
191                }
192
193        val getterCandidates = methods.filter {
194            it.element.parameters.size == 0 && it.resolvedType.returnType.kind != TypeKind.VOID
195        }
196
197        val setterCandidates = methods.filter {
198            it.element.parameters.size == 1 && it.resolvedType.returnType.kind == TypeKind.VOID
199        }
200
201        // don't try to find a constructor for binding to statement.
202        val constructor = if (bindingScope == FieldProcessor.BindingScope.BIND_TO_STMT) {
203            // we don't need to construct this POJO.
204            null
205        } else {
206            chooseConstructor(myFields, embeddedFields, relations)
207        }
208
209        assignGetters(myFields, getterCandidates)
210        assignSetters(myFields, setterCandidates, constructor)
211
212        embeddedFields.forEach {
213            assignGetter(it.field, getterCandidates)
214            assignSetter(it.field, setterCandidates, constructor)
215        }
216
217        myRelationsList.forEach {
218            assignGetter(it.field, getterCandidates)
219            assignSetter(it.field, setterCandidates, constructor)
220        }
221
222        return Pojo(element = element,
223                type = declaredType,
224                fields = fields,
225                embeddedFields = embeddedFields,
226                relations = relations,
227                constructor = constructor)
228    }
229
230    /**
231     * Retrieves the parameter names of a method. If the method is inherited from a dependency
232     * module, the parameter name is not available (not in java spec). For kotlin, since parameter
233     * names are part of the API, we can read them via the kotlin metadata annotation.
234     * <p>
235     * Since we are using an unofficial library to read the metadata, all access to that code
236     * is safe guarded to avoid unexpected failures. In other words, it is a best effort but
237     * better than not supporting these until JB provides a proper API.
238     */
239    private fun getParamNames(method: ExecutableElement): List<String> {
240        val paramNames = method.parameters.map { it.simpleName.toString() }
241        if (paramNames.isEmpty()) {
242            return emptyList()
243        }
244        return kotlinMetadata?.getParameterNames(method) ?: paramNames
245    }
246
247    private fun chooseConstructor(
248            myFields: List<Field>,
249            embedded: List<EmbeddedField>,
250            relations: List<androidx.room.vo.Relation>): Constructor? {
251        val constructors = ElementFilter.constructorsIn(element.enclosedElements)
252                .filterNot { it.hasAnnotation(Ignore::class) || it.hasAnyOf(PRIVATE) }
253        val fieldMap = myFields.associateBy { it.name }
254        val embeddedMap = embedded.associateBy { it.field.name }
255        val typeUtils = context.processingEnv.typeUtils
256        // list of param names -> matched params pairs for each failed constructor
257        val failedConstructors = arrayListOf<FailedConstructor>()
258        // if developer puts a relation into a constructor, it is usually an error but if there
259        // is another constructor that is good, we can ignore the error. b/72884434
260        val relationsInConstructor = arrayListOf<VariableElement>()
261        val goodConstructors = constructors.map { constructor ->
262            val parameterNames = getParamNames(constructor)
263            val params = constructor.parameters.mapIndexed param@ { index, param ->
264                val paramName = parameterNames[index]
265                val paramType = param.asType()
266
267                val matches = fun(field: Field?): Boolean {
268                    return if (field == null) {
269                        false
270                    } else if (!field.nameWithVariations.contains(paramName)) {
271                        false
272                    } else {
273                        // see: b/69164099
274                        typeUtils.isAssignableWithoutVariance(paramType, field.type)
275                    }
276                }
277
278                val exactFieldMatch = fieldMap[paramName]
279
280                if (matches(exactFieldMatch)) {
281                    return@param Constructor.FieldParam(exactFieldMatch!!)
282                }
283                val exactEmbeddedMatch = embeddedMap[paramName]
284                if (matches(exactEmbeddedMatch?.field)) {
285                    return@param Constructor.EmbeddedParam(exactEmbeddedMatch!!)
286                }
287
288                val matchingFields = myFields.filter {
289                    matches(it)
290                }
291                val embeddedMatches = embedded.filter {
292                    matches(it.field)
293                }
294                if (matchingFields.isEmpty() && embeddedMatches.isEmpty()) {
295                    // if it didn't match a proper field, a common mistake is to have a relation
296                    // so check to see if it is a relation
297                    val matchedRelation = relations.any {
298                        it.field.nameWithVariations.contains(paramName)
299                    }
300                    if (matchedRelation) {
301                        relationsInConstructor.add(param)
302                    }
303                    null
304                } else if (matchingFields.size + embeddedMatches.size == 1) {
305                    if (matchingFields.isNotEmpty()) {
306                        Constructor.FieldParam(matchingFields.first())
307                    } else {
308                        Constructor.EmbeddedParam(embeddedMatches.first())
309                    }
310                } else {
311                    context.logger.e(param, ProcessorErrors.ambigiousConstructor(
312                            pojo = element.qualifiedName.toString(),
313                            paramName = paramName,
314                            matchingFields = matchingFields.map { it.getPath() }
315                                    + embedded.map { it.field.getPath() }
316                    ))
317                    null
318                }
319            }
320            if (params.any { it == null }) {
321                failedConstructors.add(FailedConstructor(constructor, parameterNames, params))
322                null
323            } else {
324                @Suppress("UNCHECKED_CAST")
325                Constructor(constructor, params as List<Constructor.Param>)
326            }
327        }.filterNotNull()
328        when {
329            goodConstructors.isEmpty() -> {
330                relationsInConstructor.forEach {
331                    context.logger.e(it,
332                            ProcessorErrors.RELATION_CANNOT_BE_CONSTRUCTOR_PARAMETER)
333                }
334                if (failedConstructors.isNotEmpty()) {
335                    val failureMsg = failedConstructors.joinToString("\n") { entry ->
336                        entry.log()
337                    }
338                    context.logger.e(element, ProcessorErrors.MISSING_POJO_CONSTRUCTOR +
339                            "\nTried the following constructors but they failed to match:" +
340                            "\n$failureMsg")
341                }
342                context.logger.e(element, ProcessorErrors.MISSING_POJO_CONSTRUCTOR)
343                return null
344            }
345            goodConstructors.size > 1 -> {
346                // if there is a no-arg constructor, pick it. Even though it is weird, easily happens
347                // with kotlin data classes.
348                val noArg = goodConstructors.firstOrNull { it.params.isEmpty() }
349                if (noArg != null) {
350                    context.logger.w(Warning.DEFAULT_CONSTRUCTOR, element,
351                            ProcessorErrors.TOO_MANY_POJO_CONSTRUCTORS_CHOOSING_NO_ARG)
352                    return noArg
353                }
354                goodConstructors.forEach {
355                    context.logger.e(it.element, ProcessorErrors.TOO_MANY_POJO_CONSTRUCTORS)
356                }
357                return null
358            }
359            else -> return goodConstructors.first()
360        }
361    }
362
363    private fun processEmbeddedField(
364            declaredType: DeclaredType?, variableElement: VariableElement): EmbeddedField? {
365        val asMemberType = MoreTypes.asMemberOf(
366            context.processingEnv.typeUtils, declaredType, variableElement)
367        val asTypeElement = MoreTypes.asTypeElement(asMemberType)
368
369        if (detectReferenceRecursion(asTypeElement)) {
370            return null
371        }
372
373        val fieldPrefix = variableElement
374                .getAnnotationValue(Embedded::class.java, "prefix")
375                ?.toString()
376                ?: ""
377        val inheritedPrefix = parent?.prefix ?: ""
378        val embeddedField = Field(
379                variableElement,
380                variableElement.simpleName.toString(),
381                type = asMemberType,
382                affinity = null,
383                parent = parent)
384        val subParent = EmbeddedField(
385                field = embeddedField,
386                prefix = inheritedPrefix + fieldPrefix,
387                parent = parent)
388        subParent.pojo = PojoProcessor(
389                baseContext = context.fork(variableElement),
390                element = asTypeElement,
391                bindingScope = bindingScope,
392                parent = subParent,
393                referenceStack = referenceStack).process()
394        return subParent
395    }
396
397    private fun processRelationField(
398            myFields: List<Field>, container: DeclaredType?,
399            relationElement: VariableElement
400    ): androidx.room.vo.Relation? {
401        val asTypeElement = MoreTypes.asTypeElement(
402                MoreElements.asVariable(relationElement).asType())
403
404        if (detectReferenceRecursion(asTypeElement)) {
405            return null
406        }
407
408        val annotation = MoreElements.getAnnotationMirror(relationElement, Relation::class.java)
409                .orNull()!!
410        val parentColumnInput = AnnotationMirrors.getAnnotationValue(annotation, "parentColumn")
411                .getAsString("") ?: ""
412
413        val parentField = myFields.firstOrNull {
414            it.columnName == parentColumnInput
415        }
416        if (parentField == null) {
417            context.logger.e(relationElement,
418                    ProcessorErrors.relationCannotFindParentEntityField(
419                            entityName = element.qualifiedName.toString(),
420                            columnName = parentColumnInput,
421                            availableColumns = myFields.map { it.columnName }))
422            return null
423        }
424        // parse it as an entity.
425        val asMember = MoreTypes
426                .asMemberOf(context.processingEnv.typeUtils, container, relationElement)
427        if (asMember.kind == TypeKind.ERROR) {
428            context.logger.e(ProcessorErrors.CANNOT_FIND_TYPE, element)
429            return null
430        }
431        val declared = MoreTypes.asDeclared(asMember)
432        if (!declared.isCollection()) {
433            context.logger.e(relationElement, ProcessorErrors.RELATION_NOT_COLLECTION)
434            return null
435        }
436        val typeArg = declared.typeArguments.first().extendsBoundOrSelf()
437        if (typeArg.kind == TypeKind.ERROR) {
438            context.logger.e(MoreTypes.asTypeElement(typeArg), CANNOT_FIND_TYPE)
439            return null
440        }
441        val typeArgElement = MoreTypes.asTypeElement(typeArg)
442        val entityClassInput = AnnotationMirrors
443                .getAnnotationValue(annotation, "entity").toClassType()
444
445        // do we need to decide on the entity?
446        val inferEntity = (entityClassInput == null
447                || MoreTypes.isTypeOf(Any::class.java, entityClassInput))
448
449        val entity = if (inferEntity) {
450            EntityProcessor(context, typeArgElement, referenceStack).process()
451        } else {
452            EntityProcessor(context, MoreTypes.asTypeElement(entityClassInput),
453                    referenceStack).process()
454        }
455
456        // now find the field in the entity.
457        val entityColumnInput = AnnotationMirrors.getAnnotationValue(annotation, "entityColumn")
458                .getAsString() ?: ""
459        val entityField = entity.fields.firstOrNull {
460            it.columnName == entityColumnInput
461        }
462
463        if (entityField == null) {
464            context.logger.e(relationElement,
465                    ProcessorErrors.relationCannotFindEntityField(
466                            entityName = entity.typeName.toString(),
467                            columnName = entityColumnInput,
468                            availableColumns = entity.fields.map { it.columnName }))
469            return null
470        }
471
472        val field = Field(
473                element = relationElement,
474                name = relationElement.simpleName.toString(),
475                type = context.processingEnv.typeUtils.asMemberOf(container, relationElement),
476                affinity = null,
477                parent = parent)
478
479        val projectionInput = AnnotationMirrors.getAnnotationValue(annotation, "projection")
480                .getAsStringList()
481        val projection = if (projectionInput.isEmpty()) {
482            // we need to infer the projection from inputs.
483            createRelationshipProjection(inferEntity, typeArg, entity, entityField, typeArgElement)
484        } else {
485            // make sure projection makes sense
486            validateRelationshipProjection(projectionInput, entity, relationElement)
487            projectionInput
488        }
489        // if types don't match, row adapter prints a warning
490        return androidx.room.vo.Relation(
491                entity = entity,
492                pojoType = typeArg,
493                field = field,
494                parentField = parentField,
495                entityField = entityField,
496                projection = projection
497        )
498    }
499
500    private fun validateRelationshipProjection(
501            projectionInput: List<String>,
502            entity: Entity,
503            relationElement: VariableElement) {
504        val missingColumns = projectionInput.filterNot { columnName ->
505            entity.fields.any { columnName == it.columnName }
506        }
507        if (missingColumns.isNotEmpty()) {
508            context.logger.e(relationElement,
509                    ProcessorErrors.relationBadProject(entity.typeName.toString(),
510                            missingColumns, entity.fields.map { it.columnName }))
511        }
512    }
513
514    /**
515     * Create the projection column list based on the relationship args.
516     *
517     *  if entity field in the annotation is not specified, it is the method return type
518     *  if it is specified in the annotation:
519     *       still check the method return type, if the same, use it
520     *       if not, check to see if we can find a column Adapter, if so use the childField
521     *       last resort, try to parse it as a pojo to infer it.
522     */
523    private fun createRelationshipProjection(
524            inferEntity: Boolean,
525            typeArg: TypeMirror,
526            entity: Entity,
527            entityField: Field,
528            typeArgElement: TypeElement): List<String> {
529        return if (inferEntity || typeArg.typeName() == entity.typeName) {
530            entity.fields.map { it.columnName }
531        } else {
532            val columnAdapter = context.typeAdapterStore.findCursorValueReader(typeArg, null)
533            if (columnAdapter != null) {
534                // nice, there is a column adapter for this, assume single column response
535                listOf(entityField.name)
536            } else {
537                // last resort, it needs to be a pojo
538                val pojo = PojoProcessor(
539                        baseContext = context,
540                        element = typeArgElement,
541                        bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR,
542                        parent = parent,
543                        referenceStack = referenceStack).process()
544                pojo.fields.map { it.columnName }
545            }
546        }
547    }
548
549    private fun detectReferenceRecursion(typeElement: TypeElement): Boolean {
550        if (referenceStack.contains(typeElement.qualifiedName)) {
551            context.logger.e(
552                    typeElement,
553                    ProcessorErrors
554                            .RECURSIVE_REFERENCE_DETECTED
555                            .format(computeReferenceRecursionString(typeElement)))
556            return true
557        }
558        return false
559    }
560
561    private fun computeReferenceRecursionString(typeElement: TypeElement): String {
562        val recursiveTailTypeName = typeElement.qualifiedName
563
564        val referenceRecursionList = mutableListOf<Name>()
565        with(referenceRecursionList) {
566            add(recursiveTailTypeName)
567            addAll(referenceStack.toList().takeLastWhile { it != recursiveTailTypeName })
568            add(recursiveTailTypeName)
569        }
570
571        return referenceRecursionList.joinToString(" -> ")
572    }
573
574    private fun assignGetters(fields: List<Field>, getterCandidates: List<PojoMethod>) {
575        fields.forEach { field ->
576            assignGetter(field, getterCandidates)
577        }
578    }
579
580    private fun assignGetter(field: Field, getterCandidates: List<PojoMethod>) {
581        val success = chooseAssignment(field = field,
582                candidates = getterCandidates,
583                nameVariations = field.getterNameWithVariations,
584                getType = { method ->
585                    method.resolvedType.returnType
586                },
587                assignFromField = {
588                    field.getter = FieldGetter(
589                            name = field.name,
590                            type = field.type,
591                            callType = CallType.FIELD)
592                },
593                assignFromMethod = { match ->
594                    field.getter = FieldGetter(
595                            name = match.name,
596                            type = match.resolvedType.returnType,
597                            callType = CallType.METHOD)
598                },
599                reportAmbiguity = { matching ->
600                    context.logger.e(field.element,
601                            ProcessorErrors.tooManyMatchingGetters(field, matching))
602                })
603        context.checker.check(success, field.element, CANNOT_FIND_GETTER_FOR_FIELD)
604    }
605
606    private fun assignSetters(
607            fields: List<Field>,
608            setterCandidates: List<PojoMethod>,
609            constructor: Constructor?) {
610        fields.forEach { field ->
611            assignSetter(field, setterCandidates, constructor)
612        }
613    }
614
615    private fun assignSetter(
616            field: Field,
617            setterCandidates: List<PojoMethod>,
618            constructor: Constructor?) {
619        if (constructor != null && constructor.hasField(field)) {
620            field.setter = FieldSetter(field.name, field.type, CallType.CONSTRUCTOR)
621            return
622        }
623        val success = chooseAssignment(field = field,
624                candidates = setterCandidates,
625                nameVariations = field.setterNameWithVariations,
626                getType = { method ->
627                    method.resolvedType.parameterTypes.first()
628                },
629                assignFromField = {
630                    field.setter = FieldSetter(
631                            name = field.name,
632                            type = field.type,
633                            callType = CallType.FIELD)
634                },
635                assignFromMethod = { match ->
636                    val paramType = match.resolvedType.parameterTypes.first()
637                    field.setter = FieldSetter(
638                            name = match.name,
639                            type = paramType,
640                            callType = CallType.METHOD)
641                },
642                reportAmbiguity = { matching ->
643                    context.logger.e(field.element,
644                            ProcessorErrors.tooManyMatchingSetter(field, matching))
645                })
646        context.checker.check(success, field.element, CANNOT_FIND_SETTER_FOR_FIELD)
647    }
648
649    /**
650     * Finds a setter/getter from available list of methods.
651     * It returns true if assignment is successful, false otherwise.
652     * At worst case, it sets to the field as if it is accessible so that the rest of the
653     * compilation can continue.
654     */
655    private fun chooseAssignment(
656            field: Field,
657            candidates: List<PojoMethod>,
658            nameVariations: List<String>,
659            getType: (PojoMethod) -> TypeMirror,
660            assignFromField: () -> Unit,
661            assignFromMethod: (PojoMethod) -> Unit,
662            reportAmbiguity: (List<String>) -> Unit
663    ): Boolean {
664        if (field.element.hasAnyOf(PUBLIC)) {
665            assignFromField()
666            return true
667        }
668        val types = context.processingEnv.typeUtils
669
670        val matching = candidates
671                .filter {
672                    // b/69164099
673                    types.isAssignableWithoutVariance(getType(it), field.type)
674                            && (field.nameWithVariations.contains(it.name)
675                            || nameVariations.contains(it.name))
676                }
677                .groupBy {
678                    if (it.element.hasAnyOf(PUBLIC)) PUBLIC else PROTECTED
679                }
680        if (matching.isEmpty()) {
681            // we always assign to avoid NPEs in the rest of the compilation.
682            assignFromField()
683            // if field is not private, assume it works (if we are on the same package).
684            // if not, compiler will tell, we didn't have any better alternative anyways.
685            return !field.element.hasAnyOf(PRIVATE)
686        }
687        val match = verifyAndChooseOneFrom(matching[PUBLIC], reportAmbiguity)
688                ?: verifyAndChooseOneFrom(matching[PROTECTED], reportAmbiguity)
689        if (match == null) {
690            assignFromField()
691            return false
692        } else {
693            assignFromMethod(match)
694            return true
695        }
696    }
697
698    private fun verifyAndChooseOneFrom(
699            candidates: List<PojoMethod>?,
700            reportAmbiguity: (List<String>) -> Unit
701    ): PojoMethod? {
702        if (candidates == null) {
703            return null
704        }
705        if (candidates.size > 1) {
706            reportAmbiguity(candidates.map { it.name })
707        }
708        return candidates.first()
709    }
710
711    private data class FailedConstructor(
712            val method: ExecutableElement,
713            val params: List<String>,
714            val matches: List<Constructor.Param?>
715    ) {
716        fun log(): String {
717            val logPerParam = params.withIndex().joinToString(", ") {
718                "param:${it.value} -> matched field:" + (matches[it.index]?.log() ?: "unmatched")
719            }
720            return "$method -> [$logPerParam]"
721        }
722    }
723}
724