Home | History | Annotate | Download | only in processor
      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 
     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