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