diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 9d0acea91..361d04b39 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -117,6 +117,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod { private final Map> unprocessedDefinedTargets = new LinkedHashMap<>(); private MappingReferences mappingReferences; + private List targetThisReferences; private MethodReference factoryMethod; private boolean hasFactoryMethod; @@ -1068,6 +1069,14 @@ public class BeanMappingMethod extends NormalTypeMappingMethod { for ( MappingReference mapping : mappingReferences.getMappingReferences() ) { if ( mapping.isValid() ) { String target = mapping.getTargetReference().getShallowestPropertyName(); + if ( target == null ) { + // When the shallowest property name is null then it is for @Mapping(target = ".") + if ( this.targetThisReferences == null ) { + this.targetThisReferences = new ArrayList<>(); + } + this.targetThisReferences.add( mapping ); + continue; + } if ( !handledTargets.contains( target ) ) { if ( handleDefinedMapping( mapping, resultTypeToMap, handledTargets ) ) { errorOccurred = true; @@ -1435,8 +1444,11 @@ public class BeanMappingMethod extends NormalTypeMappingMethod { * duplicates will be handled by {@link #applyPropertyNameBasedMapping(List)} */ private void applyTargetThisMapping() { + if ( this.targetThisReferences == null ) { + return; + } Set handledTargetProperties = new HashSet<>(); - for ( MappingReference targetThis : mappingReferences.getTargetThisReferences() ) { + for ( MappingReference targetThis : this.targetThisReferences ) { // handle all prior unprocessed target properties, but let duplicates fall through List sourceRefs = targetThis diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java index 569c75e0e..ced945bb9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/beanmapping/MappingReferences.java @@ -5,10 +5,8 @@ */ package org.mapstruct.ap.internal.model.beanmapping; -import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashSet; -import java.util.List; import java.util.Objects; import java.util.Set; @@ -23,7 +21,6 @@ public class MappingReferences { private static final MappingReferences EMPTY = new MappingReferences( Collections.emptySet(), false ); private final Set mappingReferences; - private final List targetThisReferences; private final boolean restrictToDefinedMappings; private final boolean forForgedMethods; @@ -38,7 +35,6 @@ public class MappingReferences { TypeFactory typeFactory) { Set references = new LinkedHashSet<>(); - List targetThisReferences = new ArrayList<>( ); for ( MappingOptions mapping : sourceMethod.getOptions().getMappings() ) { @@ -61,30 +57,16 @@ public class MappingReferences { // add when inverse is also valid MappingReference mappingReference = new MappingReference( mapping, targetReference, sourceReference ); if ( isValidWhenInversed( mappingReference ) ) { - if ( ".".equals( mapping.getTargetName() ) ) { - targetThisReferences.add( mappingReference ); - } - else { - references.add( mappingReference ); - } + references.add( mappingReference ); } } - return new MappingReferences( references, targetThisReferences, false ); - } - - public MappingReferences(Set mappingReferences, List targetThisReferences, - boolean restrictToDefinedMappings) { - this.mappingReferences = mappingReferences; - this.restrictToDefinedMappings = restrictToDefinedMappings; - this.forForgedMethods = restrictToDefinedMappings; - this.targetThisReferences = targetThisReferences; + return new MappingReferences( references, false ); } public MappingReferences(Set mappingReferences, boolean restrictToDefinedMappings) { this.mappingReferences = mappingReferences; this.restrictToDefinedMappings = restrictToDefinedMappings; this.forForgedMethods = restrictToDefinedMappings; - this.targetThisReferences = Collections.emptyList(); } public MappingReferences(Set mappingReferences, boolean restrictToDefinedMappings, @@ -92,7 +74,6 @@ public class MappingReferences { this.mappingReferences = mappingReferences; this.restrictToDefinedMappings = restrictToDefinedMappings; this.forForgedMethods = forForgedMethods; - this.targetThisReferences = Collections.emptyList(); } public Set getMappingReferences() { @@ -136,10 +117,6 @@ public class MappingReferences { return false; } - public List getTargetThisReferences() { - return targetThisReferences; - } - @Override public boolean equals(Object o) { if ( this == o ) { @@ -160,9 +137,6 @@ public class MappingReferences { if ( !Objects.equals( mappingReferences, that.mappingReferences ) ) { return false; } - if ( Objects.equals( targetThisReferences, that.targetThisReferences ) ) { - return false; - } return true; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3126/Issue3126Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3126/Issue3126Mapper.java new file mode 100644 index 000000000..e680db931 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3126/Issue3126Mapper.java @@ -0,0 +1,92 @@ +/* + * 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._3126; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.SubclassExhaustiveStrategy; +import org.mapstruct.SubclassMapping; +import org.mapstruct.factory.Mappers; + +@Mapper(subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION) +public interface Issue3126Mapper { + + Issue3126Mapper INSTANCE = Mappers.getMapper( Issue3126Mapper.class ); + + @SubclassMapping(target = HomeAddressDto.class, source = HomeAddress.class) + @SubclassMapping(target = OfficeAddressDto.class, source = OfficeAddress.class) + @Mapping(target = ".", source = "auditable") + AddressDto map(Address address); + + interface AddressDto { + + } + + class HomeAddressDto implements AddressDto { + private String createdBy; + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + } + + class OfficeAddressDto implements AddressDto { + private String createdBy; + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + } + + class HomeAddress extends Address { + + public HomeAddress(Auditable auditable) { + super( auditable ); + } + } + + class OfficeAddress extends Address { + + public OfficeAddress(Auditable auditable) { + super( auditable ); + } + } + + abstract class Address { + + private final Auditable auditable; + + protected Address(Auditable auditable) { + this.auditable = auditable; + } + + public Auditable getAuditable() { + return auditable; + } + } + + class Auditable { + + private final String createdBy; + + public Auditable(String createdBy) { + this.createdBy = createdBy; + } + + public String getCreatedBy() { + return createdBy; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_3126/Issue3126Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3126/Issue3126Test.java new file mode 100644 index 000000000..35e3eb873 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_3126/Issue3126Test.java @@ -0,0 +1,28 @@ +/* + * 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._3126; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +import static org.assertj.core.api.Assertions.assertThat; + +@IssueKey("3126") +@WithClasses(Issue3126Mapper.class) +class Issue3126Test { + + @ProcessorTest + void shouldCompile() { + Issue3126Mapper.Auditable auditable = new Issue3126Mapper.Auditable( "home-user" ); + Issue3126Mapper.Address address = new Issue3126Mapper.HomeAddress( auditable ); + Issue3126Mapper.AddressDto addressDto = Issue3126Mapper.INSTANCE.map( address ); + + assertThat( addressDto ).isInstanceOfSatisfying( Issue3126Mapper.HomeAddressDto.class, homeAddress -> { + assertThat( homeAddress.getCreatedBy() ).isEqualTo( "home-user" ); + } ); + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedbeans/mixed/FishTankMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedbeans/mixed/FishTankMapperImpl.java index dc70a28ce..b98223fa6 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedbeans/mixed/FishTankMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/nestedbeans/mixed/FishTankMapperImpl.java @@ -57,9 +57,9 @@ public class FishTankMapperImpl implements FishTankMapper { FishTankDto fishTankDto = new FishTankDto(); - fishTankDto.setFish( fishToFishDto1( source.getFish() ) ); + fishTankDto.setFish( fishToFishDto( source.getFish() ) ); fishTankDto.setMaterial( fishTankToMaterialDto1( source ) ); - fishTankDto.setQuality( waterQualityToWaterQualityDto1( source.getQuality() ) ); + fishTankDto.setQuality( waterQualityToWaterQualityDto( source.getQuality() ) ); fishTankDto.setOrnament( ornamentToOrnamentDto( sourceInteriorOrnament( source ) ) ); fishTankDto.setPlant( waterPlantToWaterPlantDto( source.getPlant() ) ); fishTankDto.setName( source.getName() ); @@ -190,18 +190,6 @@ public class FishTankMapperImpl implements FishTankMapper { return waterPlantDto; } - protected FishDto fishToFishDto1(Fish fish) { - if ( fish == null ) { - return null; - } - - FishDto fishDto = new FishDto(); - - fishDto.setKind( fish.getType() ); - - return fishDto; - } - protected MaterialDto fishTankToMaterialDto1(FishTank fishTank) { if ( fishTank == null ) { return null; @@ -226,31 +214,6 @@ public class FishTankMapperImpl implements FishTankMapper { return waterQualityOrganisationDto; } - protected WaterQualityReportDto waterQualityReportToWaterQualityReportDto1(WaterQualityReport waterQualityReport) { - if ( waterQualityReport == null ) { - return null; - } - - WaterQualityReportDto waterQualityReportDto = new WaterQualityReportDto(); - - waterQualityReportDto.setOrganisation( waterQualityReportToWaterQualityOrganisationDto1( waterQualityReport ) ); - waterQualityReportDto.setVerdict( waterQualityReport.getVerdict() ); - - return waterQualityReportDto; - } - - protected WaterQualityDto waterQualityToWaterQualityDto1(WaterQuality waterQuality) { - if ( waterQuality == null ) { - return null; - } - - WaterQualityDto waterQualityDto = new WaterQualityDto(); - - waterQualityDto.setReport( waterQualityReportToWaterQualityReportDto1( waterQuality.getReport() ) ); - - return waterQualityDto; - } - protected Fish fishDtoToFish(FishDto fishDto) { if ( fishDto == null ) { return null;