-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Closed
Description
Expected behavior
Feel free to change the title...
Given the following mapper having a mapping method with two parameters:
import java.util.List;
import org.mapstruct.Condition;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper
interface WrongConditionMapper {
@Mapping(target = "currentId", source = "source.uuid")
@Mapping(target = "targetIds", source = "sourceIds")
Target map(Source source, List<String> sourceIds);
@Condition
default boolean isNotEmpty(List<String> elements) {
return elements != null && !elements.isEmpty();
}
class Source {
public String uuid;
}
class Target {
public String currentId;
public List<String> targetIds;
}
}The isNotEmpty presence check method should only be applied when mapping sourceIds to targetIds but not source.uuid
to currentId.
Actual behavior
class WrongConditionMapperImpl implements WrongConditionMapper {
@Override
public Target map(Source source, List<String> sourceIds) {
if ( source == null && sourceIds == null ) {
return null;
}
Target target = new Target();
if ( source != null ) {
// this isNotEmpty check has nothing to do with source.uuid or target.currentId
if ( isNotEmpty( sourceIds ) ) {
target.currentId = source.uuid;
}
}
if ( isNotEmpty( sourceIds ) ) {
List<String> list = sourceIds;
target.targetIds = new ArrayList<String>( list );
}
return target;
}
}We didn't even find a workaround for this and implemented the method ourself.
I tried to find the culprit but I'm not experienced enough to fix this, but it is because it is picked up with the PresenceCheckMethodResolver here:
mapstruct/processor/src/main/java/org/mapstruct/ap/internal/model/PresenceCheckMethodResolver.java
Lines 125 to 128 in 0a2a0aa
return selectors.getMatchingMethods( getAllAvailableMethods( method, ctx.getSourceModel(), selectionContext.getSelectionCriteria() ), selectionContext );
Steps to reproduce the problem
Here is a test mapper and a corresponding unit test:
import java.util.List;
import org.mapstruct.Condition;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper
interface WrongConditionMapper {
@Mapping(target = "currentId", source = "source.uuid")
@Mapping(target = "targetIds", source = "sourceIds")
Target map(Source source, List<String> sourceIds);
@Condition
default boolean isNotEmpty(List<String> elements) {
return elements != null && !elements.isEmpty();
}
@Condition
default boolean stringCondition(String str) {
return str != null;
}
class Source {
public String uuid;
}
class Target {
public String currentId;
public List<String> targetIds;
}
}import java.util.ArrayList;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.ProcessorTest;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.factory.Mappers;
import static org.assertj.core.api.Assertions.assertThat;
@WithClasses(WrongConditionMapper.class)
@IssueKey("3601")
class Issue3601Test {
@ProcessorTest
void shouldMapCurrentId() {
Issue3601Mapper mapper = Mappers.getMapper( Issue3601Mapper.class );
Issue3601Mapper.Source source = new Issue3601Mapper.Source();
source.uuid = "some-uuid";
Issue3601Mapper.Target target = mapper.map( source, null );
assertThat( target ).isNotNull();
assertThat( target.currentId ).isEqualTo( "some-uuid" );
assertThat( target.targetIds ).isNull();
target = mapper.map( source, Collections.emptyList() );
assertThat( target ).isNotNull();
assertThat( target.currentId ).isEqualTo( "some-uuid" );
assertThat( target.targetIds ).isNull();
ArrayList<String> sourceIds = new ArrayList<>();
sourceIds.add( "other-uuid" );
target = mapper.map( source, sourceIds );
assertThat( target ).isNotNull();
assertThat( target.currentId ).isEqualTo( "some-uuid" );
assertThat( target.targetIds ).containsExactly( "other-uuid" );
}
}MapStruct Version
1.5.5, 1.6.0.Beta1