@@ -1420,31 +1420,15 @@ else if ( mapping.getJavaExpression() != null ) {
14201420 // When we implicitly map we first do property name based mapping
14211421 // i.e. look for matching properties in the source types
14221422 // and then do parameter name based mapping
1423- for ( Parameter sourceParameter : method .getSourceParameters () ) {
1424- SourceReference matchingSourceRef = getSourceRefByTargetName (
1425- sourceParameter ,
1426- targetPropertyName
1427- );
1428- if ( matchingSourceRef != null ) {
1429- if ( sourceRef != null ) {
1430- errorOccured = true ;
1431- // This can only happen when the target property matches multiple properties
1432- // within the different source parameters
1433- ctx .getMessager ()
1434- .printMessage (
1435- method .getExecutable (),
1436- mappingRef .getMapping ().getMirror (),
1437- Message .BEANMAPPING_SEVERAL_POSSIBLE_SOURCES ,
1438- targetPropertyName
1439- );
1440- break ;
1441- }
1442- // We can't break here since it is possible that the same property exists in multiple
1443- // source parameters
1444- sourceRef = matchingSourceRef ;
1445- }
1423+ SourceReferenceResult matchingSourceRefResult = findSourceReferenceForTargetProperty (
1424+ method .getSourceParameters (),
1425+ targetPropertyName ,
1426+ mappingRef .getMapping ().getMirror ()
1427+ );
1428+ sourceRef = matchingSourceRefResult .getSourceReference ();
1429+ if (matchingSourceRefResult .isErrorOccurred () ) {
1430+ errorOccured = true ;
14461431 }
1447-
14481432 }
14491433
14501434 if ( sourceRef == null ) {
@@ -1568,34 +1552,103 @@ private void applyTargetThisMapping() {
15681552 }
15691553
15701554 /**
1571- * Iterates over all target properties and all source parameters.
1555+ * Iterates over all target properties and all source parameters to find property name matches .
15721556 * <p>
1573- * When a property name match occurs, the remainder will be checked for duplicates. Matches will be removed from
1574- * the set of remaining target properties.
1557+ * For each target property, the method attempts to find a matching source property
1558+ * using {@link #findSourceReferenceForTargetProperty}.
1559+ * <p>
1560+ * When a match is found, it's added to the list of source references for further processing.
1561+ * Primary parameters take precedence when multiple source parameters have properties with the same name.
15751562 */
15761563 private void applyPropertyNameBasedMapping () {
15771564 List <SourceReference > sourceReferences = new ArrayList <>();
1565+
15781566 for ( String targetPropertyName : unprocessedTargetProperties .keySet () ) {
1579- for ( Parameter sourceParameter : method .getSourceParameters () ) {
1580- SourceReference sourceRef = getSourceRefByTargetName ( sourceParameter , targetPropertyName );
1567+ SourceReferenceResult matchingSourceRefResult = findSourceReferenceForTargetProperty (
1568+ method .getSourceParameters (),
1569+ targetPropertyName ,
1570+ null
1571+ );
1572+ if ( matchingSourceRefResult .getSourceReference () != null ) {
1573+ sourceReferences .add ( matchingSourceRefResult .getSourceReference () );
1574+ }
1575+ }
1576+ applyPropertyNameBasedMapping ( sourceReferences );
1577+ }
1578+
1579+ /**
1580+ * Finds a source reference for a target property name, handling potential conflicts.
1581+ * <p>
1582+ * This method iterates through source parameters to find properties matching the target property name,
1583+ * applying the following rules:
1584+ * <ul>
1585+ * <li>If only one matching source reference is found, it's returned</li>
1586+ * <li>If multiple matching source references are found, their primary status is checked:
1587+ * <ul>
1588+ * <li>If all matching references have the same primary status (all primary or all non-primary),
1589+ * a conflict error is reported and an error result is returned</li>
1590+ * <li>If they have different primary status, the reference from the primary parameter is preferred</li>
1591+ * </ul>
1592+ * </li>
1593+ * </ul>
1594+ *
1595+ * @param sourceParameters the source parameters to search through
1596+ * @param targetPropertyName the target property name to match
1597+ * @param positionHint annotation mirror used for error reporting position, can be null
1598+ * @return a SourceReferenceResult containing the selected source reference and error status
1599+ */
1600+ private SourceReferenceResult findSourceReferenceForTargetProperty (List <Parameter > sourceParameters ,
1601+ String targetPropertyName ,
1602+ AnnotationMirror positionHint ) {
1603+ List <Parameter > sortedSourceParameters =
1604+ sourceParameters
1605+ .stream ()
1606+ .sorted ( Comparator .comparing ( Parameter ::isPrimary ).reversed () )
1607+ .collect ( Collectors .toList () );
1608+
1609+ SourceReference sourceRef = null ;
1610+ boolean errorOccurred = false ;
1611+ for ( Parameter sourceParameter : sortedSourceParameters ) {
1612+ SourceReference matchingSourceRef = getSourceRefByTargetName ( sourceParameter , targetPropertyName );
1613+ if ( matchingSourceRef != null ) {
15811614 if ( sourceRef != null ) {
1582- sourceReferences .add ( sourceRef );
1615+ if ( sourceRef .getParameter ().isPrimary () == matchingSourceRef .getParameter ().isPrimary () ) {
1616+ // Conflict detected - both parameters have the same primary status
1617+ // Either:
1618+ // 1. Both parameters are marked with @MappingSource(primary = true)
1619+ // 2. Neither parameter has primary status
1620+ errorOccurred = true ;
1621+ ctx .getMessager ()
1622+ .printMessage (
1623+ method .getExecutable (),
1624+ positionHint ,
1625+ Message .BEANMAPPING_SEVERAL_POSSIBLE_SOURCES ,
1626+ targetPropertyName
1627+ );
1628+ }
1629+ break ;
15831630 }
1631+ // We can't break here since it is possible that the same property exists in multiple
1632+ // source parameters
1633+ sourceRef = matchingSourceRef ;
15841634 }
15851635 }
1586- applyPropertyNameBasedMapping ( sourceReferences );
1636+ return new SourceReferenceResult ( sourceRef , errorOccurred );
15871637 }
15881638
15891639 /**
1590- * Iterates over all target properties and all source parameters.
1640+ * Processes a list of source references to create property mappings.
1641+ * <p>
1642+ * Each source reference is used to create a property mapping for its target property.
1643+ * The referenced target property is removed from the set of unprocessed properties.
15911644 * <p>
1592- * When a property name match occurs, the remainder will be checked for duplicates. Matches will be removed from
1593- * the set of remaining target properties.
1645+ * Note: This method assumes that conflicts between multiple source references for the same target property
1646+ * have already been resolved by {@link #findSourceReferenceForTargetProperty}.
1647+ *
1648+ * @param sourceReferences the list of source references to process
15941649 */
15951650 private void applyPropertyNameBasedMapping (List <SourceReference > sourceReferences ) {
1596-
15971651 for ( SourceReference sourceRef : sourceReferences ) {
1598-
15991652 String targetPropertyName = sourceRef .getDeepestPropertyName ();
16001653 Accessor targetPropertyWriteAccessor = unprocessedTargetProperties .remove ( targetPropertyName );
16011654 unprocessedConstructorProperties .remove ( targetPropertyName );
@@ -1703,7 +1756,7 @@ private SourceReference getSourceRefByTargetName(Parameter sourceParameter, Stri
17031756 }
17041757
17051758 boolean allowedMapToBean =
1706- method .getSourceParameters ().size () == 1 || ( sourceParameter .isImplicitMapping () );
1759+ method .getSourceParameters ().size () == 1 || sourceParameter .isImplicitMapping ();
17071760 ReadAccessor sourceReadAccessor = sourceParameter .getType ()
17081761 .getReadAccessor ( targetPropertyName , allowedMapToBean );
17091762 if ( sourceReadAccessor != null ) {
@@ -1930,6 +1983,24 @@ private void reportErrorForUnusedSourceParameters() {
19301983 }
19311984 }
19321985
1986+ private static class SourceReferenceResult {
1987+ private final SourceReference sourceReference ;
1988+ private final boolean errorOccurred ;
1989+
1990+ private SourceReferenceResult (SourceReference sourceReference , boolean errorOccurred ) {
1991+ this .sourceReference = sourceReference ;
1992+ this .errorOccurred = errorOccurred ;
1993+ }
1994+
1995+ SourceReference getSourceReference () {
1996+ return sourceReference ;
1997+ }
1998+
1999+ boolean isErrorOccurred () {
2000+ return errorOccurred ;
2001+ }
2002+ }
2003+
19332004 private static class ConstructorAccessor {
19342005 private final boolean hasError ;
19352006 private final List <ParameterBinding > parameterBindings ;
0 commit comments