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