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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
@ -26,6 +27,7 @@ public class SelectionCriteria {
|
||||
private final String targetPropertyName;
|
||||
private final TypeMirror qualifyingResultType;
|
||||
private final SourceRHS sourceRHS;
|
||||
private boolean ignoreQualifiers = false;
|
||||
private Type type;
|
||||
private final boolean allowDirect;
|
||||
private final boolean allowConversion;
|
||||
@ -87,12 +89,16 @@ public class SelectionCriteria {
|
||||
return type == Type.PRESENCE_CHECK;
|
||||
}
|
||||
|
||||
public void setIgnoreQualifiers(boolean ignoreQualifiers) {
|
||||
this.ignoreQualifiers = ignoreQualifiers;
|
||||
}
|
||||
|
||||
public List<TypeMirror> getQualifiers() {
|
||||
return qualifiers;
|
||||
return ignoreQualifiers ? Collections.emptyList() : qualifiers;
|
||||
}
|
||||
|
||||
public List<String> getQualifiedByNames() {
|
||||
return qualifiedByNames;
|
||||
return ignoreQualifiers ? Collections.emptyList() : qualifiedByNames;
|
||||
}
|
||||
|
||||
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:
|
||||
* <ul>
|
||||
@ -743,6 +748,17 @@ public class MappingResolverImpl implements MappingResolver {
|
||||
if ( mmAttempt.hasResult ) {
|
||||
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 =
|
||||
new MethodMethod<>( att, att.methods, att.builtIns, att::toMethodRef, att::toBuildInRef )
|
||||
.getBestMatch( sourceType, targetType );
|
||||
@ -772,6 +788,18 @@ public class MappingResolverImpl implements MappingResolver {
|
||||
}
|
||||
|
||||
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<>();
|
||||
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
|
||||
// a nested method call can be called. so C = methodY( methodX (A) )
|
||||
attempt.selectionCriteria.setPreferUpdateMapping( false );
|
||||
attempt.selectionCriteria.setIgnoreQualifiers(
|
||||
matchType == BestMatchType.IGNORE_QUALIFIERS_BEFORE_Y_CANDIDATES );
|
||||
|
||||
for ( T2 yCandidate : yMethods ) {
|
||||
Type ySourceType = yCandidate.getMappingSourceType();
|
||||
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 );
|
||||
if ( !xMatches.isEmpty() ) {
|
||||
xMatches.stream().forEach( x -> xCandidates.put( x, new ArrayList<>() ) );
|
||||
final Type typeInTheMiddle = ySourceType;
|
||||
xMatches.stream().forEach( x -> typesInTheMiddle.put( x, typeInTheMiddle ) );
|
||||
for ( SelectedMethod<T1> x : xMatches ) {
|
||||
xCandidates.put( x, new ArrayList<>() );
|
||||
typesInTheMiddle.put( x, ySourceType );
|
||||
}
|
||||
yCandidates.add( yCandidate );
|
||||
}
|
||||
}
|
||||
attempt.selectionCriteria.setPreferUpdateMapping( true );
|
||||
attempt.selectionCriteria.setIgnoreQualifiers(
|
||||
matchType == BestMatchType.IGNORE_QUALIFIERS_AFTER_Y_CANDIDATES );
|
||||
|
||||
// collect all results
|
||||
List<T2> yCandidatesList = new ArrayList<>( yCandidates );
|
||||
@ -816,6 +850,7 @@ public class MappingResolverImpl implements MappingResolver {
|
||||
}
|
||||
}
|
||||
|
||||
attempt.selectionCriteria.setIgnoreQualifiers( false );
|
||||
// no results left
|
||||
if ( xCandidates.isEmpty() ) {
|
||||
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