diff --git a/copyright.txt b/copyright.txt index c66607498..8b6317dc7 100644 --- a/copyright.txt +++ b/copyright.txt @@ -7,6 +7,7 @@ Christian Bandowski - https://github.com/chris922 Christian Schuster - https://github.com/chschu Christophe Labouisse - https://github.com/ggtools Ciaran Liedeman - https://github.com/cliedeman +Cindy Wang - https://github.com/birdfriend Cornelius Dirmeier - https://github.com/cornzy David Feinblum - https://github.com/dvfeinblum Darren Rambaud - https://github.com/xyzst diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 7c632f1f8..09627b5c4 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -563,6 +563,20 @@ public class PropertyMapping extends ModelElement { if ( propertyEntry.getPresenceChecker() != null ) { sourcePresenceChecker = sourceParam.getName() + "." + propertyEntry.getPresenceChecker().getSimpleName() + "()"; + + String variableName = sourceParam.getName() + "." + + propertyEntry.getReadAccessor().getSimpleName() + "()"; + for (int i = 1; i < sourceReference.getPropertyEntries().size(); i++) { + PropertyEntry entry = sourceReference.getPropertyEntries().get( i ); + if (entry.getPresenceChecker() != null && entry.getReadAccessor() != null) { + sourcePresenceChecker += " && " + variableName + " != null && " + + variableName + "." + entry.getPresenceChecker().getSimpleName() + "()"; + variableName = variableName + "." + entry.getReadAccessor().getSimpleName() + "()"; + } + else { + break; + } + } } } return sourcePresenceChecker; diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/PresenceCheckNestedObjectsTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/PresenceCheckNestedObjectsTest.java new file mode 100644 index 000000000..1f3981083 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/PresenceCheckNestedObjectsTest.java @@ -0,0 +1,50 @@ +/* + * 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.source.presencecheck.spi; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertFalse; + +/** + * Test for correct handling of source presence checks for nested objects. + * + * @author Cindy Wang + */ +@WithClasses({ + SoccerTeamMapperNestedObjects.class, + SoccerTeamSource.class, + GoalKeeper.class, + SoccerTeamTargetWithPresenceCheck.class, + Referee.class +}) +@RunWith(AnnotationProcessorTestRunner.class) +public class PresenceCheckNestedObjectsTest { + + @Test + public void testNestedWithSourcesAbsentOnNestingLevel() { + + SoccerTeamSource soccerTeamSource = new SoccerTeamSource(); + GoalKeeper goalKeeper = new GoalKeeper(); + goalKeeper.setHasName( false ); + goalKeeper.setName( "shouldNotBeUsed" ); + soccerTeamSource.setGoalKeeper( goalKeeper ); + soccerTeamSource.setHasRefereeName( false ); + soccerTeamSource.setRefereeName( "shouldNotBeUsed" ); + + SoccerTeamTargetWithPresenceCheck target = SoccerTeamMapperNestedObjects.INSTANCE.mapNested( soccerTeamSource ); + + assertThat( target.getGoalKeeperName() ).isNull(); + assertFalse( target.hasGoalKeeperName() ); + assertThat( target.getReferee() ).isNotNull(); + assertThat( target.getReferee().getName() ).isNull(); + + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/Referee.java b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/Referee.java new file mode 100644 index 000000000..d10848689 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/Referee.java @@ -0,0 +1,18 @@ +/* + * 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.source.presencecheck.spi; + +public class Referee { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SoccerTeamMapperNestedObjects.java b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SoccerTeamMapperNestedObjects.java new file mode 100644 index 000000000..f804d957d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SoccerTeamMapperNestedObjects.java @@ -0,0 +1,27 @@ +/* + * 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.source.presencecheck.spi; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.factory.Mappers; + +/** + * @author Cindy Wang + */ +@Mapper +public interface SoccerTeamMapperNestedObjects { + SoccerTeamMapperNestedObjects INSTANCE = Mappers.getMapper( SoccerTeamMapperNestedObjects.class ); + + @Mappings({ + @Mapping(target = "players", ignore = true), + @Mapping(target = "goalKeeperName", source = "goalKeeper.name"), + @Mapping(target = "referee.name", source = "refereeName") + }) + SoccerTeamTargetWithPresenceCheck mapNested( SoccerTeamSource in ); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SoccerTeamSource.java b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SoccerTeamSource.java index d2462acf0..35ecdb8fe 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SoccerTeamSource.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SoccerTeamSource.java @@ -18,6 +18,9 @@ public class SoccerTeamSource { private GoalKeeper goalKeeper; private boolean hasGoalKeeper = true; + private String refereeName; + private boolean hasRefereeName; + public boolean hasPlayers() { return hasPlayers; } @@ -50,4 +53,19 @@ public class SoccerTeamSource { this.hasGoalKeeper = hasGoalKeeper; } + public String getRefereeName() { + return refereeName; + } + + public void setRefereeName(String refereeName) { + this.refereeName = refereeName; + } + + public boolean hasRefereeName() { + return hasRefereeName; + } + + public void setHasRefereeName(boolean hasRefereeName) { + this.hasRefereeName = hasRefereeName; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SoccerTeamTargetWithPresenceCheck.java b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SoccerTeamTargetWithPresenceCheck.java new file mode 100644 index 000000000..1e005904e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SoccerTeamTargetWithPresenceCheck.java @@ -0,0 +1,50 @@ +/* + * 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.source.presencecheck.spi; + +import java.util.List; + +/** + * @author Cindy Wang + */ +public class SoccerTeamTargetWithPresenceCheck { + + private List players; + private String goalKeeperName; + private Referee referee; + + private boolean hasPlayers = false; + private boolean hasGoalKeeperName = false; + + public List getPlayers() { + return players; + } + + public String getGoalKeeperName() { + return goalKeeperName; + } + + public void setGoalKeeperName(String goalKeeperName) { + this.goalKeeperName = goalKeeperName; + hasGoalKeeperName = true; + } + + public boolean hasPlayers() { + return hasPlayers; + } + + public boolean hasGoalKeeperName() { + return hasGoalKeeperName; + } + + public Referee getReferee() { + return referee; + } + + public void setReferee(Referee referee) { + this.referee = referee; + } +}