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