#3126 Apply target this references in the BeanMappingMethod

This commit is contained in:
Filip Hrisafov 2023-05-21 23:01:58 +02:00
parent 51f4e7eba9
commit c2eed45df1
5 changed files with 137 additions and 68 deletions

View File

@ -117,6 +117,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
private final Map<String, Set<MappingReference>> unprocessedDefinedTargets = new LinkedHashMap<>();
private MappingReferences mappingReferences;
private List<MappingReference> 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<String> 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<SourceReference> sourceRefs = targetThis

View File

@ -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<MappingReference> mappingReferences;
private final List<MappingReference> targetThisReferences;
private final boolean restrictToDefinedMappings;
private final boolean forForgedMethods;
@ -38,7 +35,6 @@ public class MappingReferences {
TypeFactory typeFactory) {
Set<MappingReference> references = new LinkedHashSet<>();
List<MappingReference> 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<MappingReference> mappingReferences, List<MappingReference> targetThisReferences,
boolean restrictToDefinedMappings) {
this.mappingReferences = mappingReferences;
this.restrictToDefinedMappings = restrictToDefinedMappings;
this.forForgedMethods = restrictToDefinedMappings;
this.targetThisReferences = targetThisReferences;
return new MappingReferences( references, false );
}
public MappingReferences(Set<MappingReference> mappingReferences, boolean restrictToDefinedMappings) {
this.mappingReferences = mappingReferences;
this.restrictToDefinedMappings = restrictToDefinedMappings;
this.forForgedMethods = restrictToDefinedMappings;
this.targetThisReferences = Collections.emptyList();
}
public MappingReferences(Set<MappingReference> 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<MappingReference> getMappingReferences() {
@ -136,10 +117,6 @@ public class MappingReferences {
return false;
}
public List<MappingReference> 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;
}

View File

@ -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;
}
}
}

View File

@ -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" );
} );
}
}

View File

@ -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;