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