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