mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#2538 Allow using 2 step mappings with only one of the 2 methods being qualified
This commit is contained in:
parent
0a69492983
commit
ad00adfa86
@ -6,6 +6,7 @@
|
|||||||
package org.mapstruct.ap.internal.model.source.selector;
|
package org.mapstruct.ap.internal.model.source.selector;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.lang.model.type.TypeMirror;
|
import javax.lang.model.type.TypeMirror;
|
||||||
@ -26,6 +27,7 @@ public class SelectionCriteria {
|
|||||||
private final String targetPropertyName;
|
private final String targetPropertyName;
|
||||||
private final TypeMirror qualifyingResultType;
|
private final TypeMirror qualifyingResultType;
|
||||||
private final SourceRHS sourceRHS;
|
private final SourceRHS sourceRHS;
|
||||||
|
private boolean ignoreQualifiers = false;
|
||||||
private Type type;
|
private Type type;
|
||||||
private final boolean allowDirect;
|
private final boolean allowDirect;
|
||||||
private final boolean allowConversion;
|
private final boolean allowConversion;
|
||||||
@ -87,12 +89,16 @@ public class SelectionCriteria {
|
|||||||
return type == Type.PRESENCE_CHECK;
|
return type == Type.PRESENCE_CHECK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setIgnoreQualifiers(boolean ignoreQualifiers) {
|
||||||
|
this.ignoreQualifiers = ignoreQualifiers;
|
||||||
|
}
|
||||||
|
|
||||||
public List<TypeMirror> getQualifiers() {
|
public List<TypeMirror> getQualifiers() {
|
||||||
return qualifiers;
|
return ignoreQualifiers ? Collections.emptyList() : qualifiers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getQualifiedByNames() {
|
public List<String> getQualifiedByNames() {
|
||||||
return qualifiedByNames;
|
return ignoreQualifiers ? Collections.emptyList() : qualifiedByNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTargetPropertyName() {
|
public String getTargetPropertyName() {
|
||||||
|
@ -712,6 +712,11 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum BestMatchType {
|
||||||
|
IGNORE_QUALIFIERS_BEFORE_Y_CANDIDATES,
|
||||||
|
IGNORE_QUALIFIERS_AFTER_Y_CANDIDATES,
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Suppose mapping required from A to C and:
|
* Suppose mapping required from A to C and:
|
||||||
* <ul>
|
* <ul>
|
||||||
@ -743,6 +748,17 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
if ( mmAttempt.hasResult ) {
|
if ( mmAttempt.hasResult ) {
|
||||||
return mmAttempt.result;
|
return mmAttempt.result;
|
||||||
}
|
}
|
||||||
|
if ( att.hasQualfiers() ) {
|
||||||
|
mmAttempt = mmAttempt.getBestMatchIgnoringQualifiersBeforeY( sourceType, targetType );
|
||||||
|
if ( mmAttempt.hasResult ) {
|
||||||
|
return mmAttempt.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
mmAttempt = mmAttempt.getBestMatchIgnoringQualifiersAfterY( sourceType, targetType );
|
||||||
|
if ( mmAttempt.hasResult ) {
|
||||||
|
return mmAttempt.result;
|
||||||
|
}
|
||||||
|
}
|
||||||
MethodMethod<Method, BuiltInMethod> mbAttempt =
|
MethodMethod<Method, BuiltInMethod> mbAttempt =
|
||||||
new MethodMethod<>( att, att.methods, att.builtIns, att::toMethodRef, att::toBuildInRef )
|
new MethodMethod<>( att, att.methods, att.builtIns, att::toMethodRef, att::toBuildInRef )
|
||||||
.getBestMatch( sourceType, targetType );
|
.getBestMatch( sourceType, targetType );
|
||||||
@ -772,6 +788,18 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private MethodMethod<T1, T2> getBestMatch(Type sourceType, Type targetType) {
|
private MethodMethod<T1, T2> getBestMatch(Type sourceType, Type targetType) {
|
||||||
|
return getBestMatch( sourceType, targetType, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodMethod<T1, T2> getBestMatchIgnoringQualifiersBeforeY(Type sourceType, Type targetType) {
|
||||||
|
return getBestMatch( sourceType, targetType, BestMatchType.IGNORE_QUALIFIERS_BEFORE_Y_CANDIDATES );
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodMethod<T1, T2> getBestMatchIgnoringQualifiersAfterY(Type sourceType, Type targetType) {
|
||||||
|
return getBestMatch( sourceType, targetType, BestMatchType.IGNORE_QUALIFIERS_AFTER_Y_CANDIDATES );
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodMethod<T1, T2> getBestMatch(Type sourceType, Type targetType, BestMatchType matchType) {
|
||||||
|
|
||||||
Set<T2> yCandidates = new HashSet<>();
|
Set<T2> yCandidates = new HashSet<>();
|
||||||
Map<SelectedMethod<T1>, List<SelectedMethod<T2>>> xCandidates = new LinkedHashMap<>();
|
Map<SelectedMethod<T1>, List<SelectedMethod<T2>>> xCandidates = new LinkedHashMap<>();
|
||||||
@ -784,6 +812,9 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
// sourceMethod or builtIn that fits the signature B to C. Only then there is a match. If we have a match
|
// sourceMethod or builtIn that fits the signature B to C. Only then there is a match. If we have a match
|
||||||
// a nested method call can be called. so C = methodY( methodX (A) )
|
// a nested method call can be called. so C = methodY( methodX (A) )
|
||||||
attempt.selectionCriteria.setPreferUpdateMapping( false );
|
attempt.selectionCriteria.setPreferUpdateMapping( false );
|
||||||
|
attempt.selectionCriteria.setIgnoreQualifiers(
|
||||||
|
matchType == BestMatchType.IGNORE_QUALIFIERS_BEFORE_Y_CANDIDATES );
|
||||||
|
|
||||||
for ( T2 yCandidate : yMethods ) {
|
for ( T2 yCandidate : yMethods ) {
|
||||||
Type ySourceType = yCandidate.getMappingSourceType();
|
Type ySourceType = yCandidate.getMappingSourceType();
|
||||||
ySourceType = ySourceType.resolveParameterToType( targetType, yCandidate.getResultType() ).getMatch();
|
ySourceType = ySourceType.resolveParameterToType( targetType, yCandidate.getResultType() ).getMatch();
|
||||||
@ -796,13 +827,16 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
}
|
}
|
||||||
List<SelectedMethod<T1>> xMatches = attempt.getBestMatch( xMethods, sourceType, ySourceType );
|
List<SelectedMethod<T1>> xMatches = attempt.getBestMatch( xMethods, sourceType, ySourceType );
|
||||||
if ( !xMatches.isEmpty() ) {
|
if ( !xMatches.isEmpty() ) {
|
||||||
xMatches.stream().forEach( x -> xCandidates.put( x, new ArrayList<>() ) );
|
for ( SelectedMethod<T1> x : xMatches ) {
|
||||||
final Type typeInTheMiddle = ySourceType;
|
xCandidates.put( x, new ArrayList<>() );
|
||||||
xMatches.stream().forEach( x -> typesInTheMiddle.put( x, typeInTheMiddle ) );
|
typesInTheMiddle.put( x, ySourceType );
|
||||||
|
}
|
||||||
yCandidates.add( yCandidate );
|
yCandidates.add( yCandidate );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
attempt.selectionCriteria.setPreferUpdateMapping( true );
|
attempt.selectionCriteria.setPreferUpdateMapping( true );
|
||||||
|
attempt.selectionCriteria.setIgnoreQualifiers(
|
||||||
|
matchType == BestMatchType.IGNORE_QUALIFIERS_AFTER_Y_CANDIDATES );
|
||||||
|
|
||||||
// collect all results
|
// collect all results
|
||||||
List<T2> yCandidatesList = new ArrayList<>( yCandidates );
|
List<T2> yCandidatesList = new ArrayList<>( yCandidates );
|
||||||
@ -816,6 +850,7 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attempt.selectionCriteria.setIgnoreQualifiers( false );
|
||||||
// no results left
|
// no results left
|
||||||
if ( xCandidates.isEmpty() ) {
|
if ( xCandidates.isEmpty() ) {
|
||||||
return this;
|
return this;
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.bugs._2538;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class Group {
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
public Group(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.bugs._2538;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class GroupDto {
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
public GroupDto(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.bugs._2538;
|
||||||
|
|
||||||
|
import org.mapstruct.ap.testutil.ProcessorTest;
|
||||||
|
import org.mapstruct.ap.testutil.WithClasses;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@WithClasses({
|
||||||
|
Group.class,
|
||||||
|
GroupDto.class,
|
||||||
|
TeamMapper.class,
|
||||||
|
TeamRole.class,
|
||||||
|
TeamRoleDto.class,
|
||||||
|
})
|
||||||
|
class Issue2538Test {
|
||||||
|
|
||||||
|
@ProcessorTest
|
||||||
|
void shouldCorrectlyUseQualifiedMethodIn2StepMapping() {
|
||||||
|
TeamRole role = TeamMapper.INSTANCE.mapUsingFirstLookup( new TeamRoleDto( "test" ) );
|
||||||
|
|
||||||
|
assertThat( role ).isNotNull();
|
||||||
|
assertThat( role.getGroup() ).isNotNull();
|
||||||
|
assertThat( role.getGroup().getId() ).isEqualTo( "lookup-test" );
|
||||||
|
|
||||||
|
role = TeamMapper.INSTANCE.mapUsingSecondLookup( new TeamRoleDto( "test" ) );
|
||||||
|
|
||||||
|
assertThat( role ).isNotNull();
|
||||||
|
assertThat( role.getGroup() ).isNotNull();
|
||||||
|
assertThat( role.getGroup().getId() ).isEqualTo( "second-test" );
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.bugs._2538;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.Named;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface TeamMapper {
|
||||||
|
|
||||||
|
TeamMapper INSTANCE = Mappers.getMapper( TeamMapper.class );
|
||||||
|
|
||||||
|
// This method is testing methodX(methodY(...)) where methodY is qualified
|
||||||
|
@Mapping(target = "group", source = "groupId", qualifiedByName = "firstLookup")
|
||||||
|
TeamRole mapUsingFirstLookup(TeamRoleDto in);
|
||||||
|
|
||||||
|
// This method is testing methodX(methodY(...)) where methodX is qualified
|
||||||
|
@Mapping(target = "group", source = "groupId", qualifiedByName = "secondLookup")
|
||||||
|
TeamRole mapUsingSecondLookup(TeamRoleDto in);
|
||||||
|
|
||||||
|
Group map(GroupDto in);
|
||||||
|
|
||||||
|
@Named("firstLookup")
|
||||||
|
default GroupDto lookupGroup(String groupId) {
|
||||||
|
return groupId != null ? new GroupDto( "lookup-" + groupId ) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
default GroupDto normalLookup(String groupId) {
|
||||||
|
return groupId != null ? new GroupDto( groupId ) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Named("secondLookup")
|
||||||
|
default Group mapSecondLookup(GroupDto in) {
|
||||||
|
return in != null ? new Group( "second-" + in.getId() ) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.bugs._2538;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class TeamRole {
|
||||||
|
|
||||||
|
private final Group group;
|
||||||
|
|
||||||
|
public TeamRole(Group group) {
|
||||||
|
this.group = group;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Group getGroup() {
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.bugs._2538;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class TeamRoleDto {
|
||||||
|
|
||||||
|
private final String groupId;
|
||||||
|
|
||||||
|
public TeamRoleDto(String groupId) {
|
||||||
|
this.groupId = groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGroupId() {
|
||||||
|
return groupId;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user