1 /*
<lambda>null2  * 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 
17 package androidx.room.processor
18 
19 import androidx.room.ColumnInfo
20 import androidx.room.Embedded
21 import androidx.room.Ignore
22 import androidx.room.Relation
23 import androidx.room.ext.KotlinMetadataProcessor
24 import androidx.room.ext.extendsBoundOrSelf
25 import androidx.room.ext.getAllFieldsIncludingPrivateSupers
26 import androidx.room.ext.getAnnotationValue
27 import androidx.room.ext.getAsString
28 import androidx.room.ext.getAsStringList
29 import androidx.room.ext.hasAnnotation
30 import androidx.room.ext.hasAnyOf
31 import androidx.room.ext.isAssignableWithoutVariance
32 import androidx.room.ext.isCollection
33 import androidx.room.ext.toClassType
34 import androidx.room.ext.typeName
35 import androidx.room.processor.ProcessorErrors.CANNOT_FIND_GETTER_FOR_FIELD
36 import androidx.room.processor.ProcessorErrors.CANNOT_FIND_SETTER_FOR_FIELD
37 import androidx.room.processor.ProcessorErrors.CANNOT_FIND_TYPE
38 import androidx.room.processor.ProcessorErrors.POJO_FIELD_HAS_DUPLICATE_COLUMN_NAME
39 import androidx.room.processor.cache.Cache
40 import androidx.room.vo.CallType
41 import androidx.room.vo.Constructor
42 import androidx.room.vo.EmbeddedField
43 import androidx.room.vo.Entity
44 import androidx.room.vo.Field
45 import androidx.room.vo.FieldGetter
46 import androidx.room.vo.FieldSetter
47 import androidx.room.vo.Pojo
48 import androidx.room.vo.PojoMethod
49 import androidx.room.vo.Warning
50 import com.google.auto.common.AnnotationMirrors
51 import com.google.auto.common.MoreElements
52 import com.google.auto.common.MoreTypes
53 import me.eugeniomarletti.kotlin.metadata.KotlinClassMetadata
54 import me.eugeniomarletti.kotlin.metadata.kotlinMetadata
55 import javax.annotation.processing.ProcessingEnvironment
56 import javax.lang.model.element.ExecutableElement
57 import javax.lang.model.element.Modifier.ABSTRACT
58 import javax.lang.model.element.Modifier.PRIVATE
59 import javax.lang.model.element.Modifier.PROTECTED
60 import javax.lang.model.element.Modifier.PUBLIC
61 import javax.lang.model.element.Modifier.STATIC
62 import javax.lang.model.element.Modifier.TRANSIENT
63 import javax.lang.model.element.Name
64 import javax.lang.model.element.TypeElement
65 import javax.lang.model.element.VariableElement
66 import javax.lang.model.type.DeclaredType
67 import javax.lang.model.type.TypeKind
68 import javax.lang.model.type.TypeMirror
69 import javax.lang.model.util.ElementFilter
70 
71 /**
72  * Processes any class as if it is a Pojo.
73  */
74 class 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