#2236 Different nested target mappings should generate different intermediate methods

Make sure that MappingReferences are taken into consideration when comparing whether 2 mapping methods are equal.
This makes sure that when using nested target mappings that have the same property mappings, but different mappings 2 distinct methods will be created
This commit is contained in:
Filip Hrisafov 2020-10-18 16:11:18 +02:00
parent 50aa9cdbdc
commit 6102d0cc8e
9 changed files with 344 additions and 5 deletions

View File

@ -85,6 +85,8 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
private final BuilderType returnTypeBuilder; private final BuilderType returnTypeBuilder;
private final MethodReference finalizerMethod; private final MethodReference finalizerMethod;
private final MappingReferences mappingReferences;
public static class Builder { public static class Builder {
private MappingBuilderContext ctx; private MappingBuilderContext ctx;
@ -337,7 +339,8 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
returnTypeBuilder, returnTypeBuilder,
beforeMappingMethods, beforeMappingMethods,
afterMappingMethods, afterMappingMethods,
finalizeMethod finalizeMethod,
mappingReferences
); );
} }
@ -1488,6 +1491,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
} }
} }
//CHECKSTYLE:OFF
private BeanMappingMethod(Method method, private BeanMappingMethod(Method method,
Collection<String> existingVariableNames, Collection<String> existingVariableNames,
List<PropertyMapping> propertyMappings, List<PropertyMapping> propertyMappings,
@ -1497,7 +1501,8 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
BuilderType returnTypeBuilder, BuilderType returnTypeBuilder,
List<LifecycleCallbackMethodReference> beforeMappingReferences, List<LifecycleCallbackMethodReference> beforeMappingReferences,
List<LifecycleCallbackMethodReference> afterMappingReferences, List<LifecycleCallbackMethodReference> afterMappingReferences,
MethodReference finalizerMethod) { MethodReference finalizerMethod,
MappingReferences mappingReferences) {
super( super(
method, method,
existingVariableNames, existingVariableNames,
@ -1506,10 +1511,12 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
beforeMappingReferences, beforeMappingReferences,
afterMappingReferences afterMappingReferences
); );
//CHECKSTYLE:ON
this.propertyMappings = propertyMappings; this.propertyMappings = propertyMappings;
this.returnTypeBuilder = returnTypeBuilder; this.returnTypeBuilder = returnTypeBuilder;
this.finalizerMethod = finalizerMethod; this.finalizerMethod = finalizerMethod;
this.mappingReferences = mappingReferences;
// intialize constant mappings as all mappings, but take out the ones that can be contributed to a // intialize constant mappings as all mappings, but take out the ones that can be contributed to a
// parameter mapping. // parameter mapping.
@ -1628,7 +1635,17 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
if ( !super.equals( obj ) ) { if ( !super.equals( obj ) ) {
return false; return false;
} }
return Objects.equals( propertyMappings, that.propertyMappings );
if ( !Objects.equals( propertyMappings, that.propertyMappings ) ) {
return false;
}
if ( !Objects.equals( mappingReferences, that.mappingReferences ) ) {
return false;
}
return true;
} }
} }

View File

@ -9,6 +9,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.Type;
@ -139,6 +140,38 @@ public class MappingReferences {
return targetThisReferences; return targetThisReferences;
} }
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( !( o instanceof MappingReferences ) ) {
return false;
}
MappingReferences that = (MappingReferences) o;
if ( restrictToDefinedMappings != that.restrictToDefinedMappings ) {
return false;
}
if ( forForgedMethods != that.forForgedMethods ) {
return false;
}
if ( !Objects.equals( mappingReferences, that.mappingReferences ) ) {
return false;
}
if ( Objects.equals( targetThisReferences, that.targetThisReferences ) ) {
return false;
}
return true;
}
@Override
public int hashCode() {
return mappingReferences != null ? mappingReferences.hashCode() : 0;
}
/** /**
* MapStruct filters automatically inversed invalid methods out. TODO: this is a principle we should discuss! * MapStruct filters automatically inversed invalid methods out. TODO: this is a principle we should discuss!
* @param mappingRef * @param mappingRef

View File

@ -0,0 +1,49 @@
/*
* 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._2236;
/**
* @author Filip Hrisafov
*/
public class Car {
private String name;
private String type;
private Owner ownerA;
private Owner ownerB;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Owner getOwnerA() {
return ownerA;
}
public void setOwnerA(Owner ownerA) {
this.ownerA = ownerA;
}
public Owner getOwnerB() {
return ownerB;
}
public void setOwnerB(Owner ownerB) {
this.ownerB = ownerB;
}
}

View File

@ -0,0 +1,58 @@
/*
* 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._2236;
/**
* @author Filip Hrisafov
*/
public class CarDto {
private String name;
private String ownerNameA;
private String ownerNameB;
private String ownerCityA;
private String ownerCityB;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getOwnerNameA() {
return ownerNameA;
}
public void setOwnerNameA(String ownerNameA) {
this.ownerNameA = ownerNameA;
}
public String getOwnerNameB() {
return ownerNameB;
}
public void setOwnerNameB(String ownerNameB) {
this.ownerNameB = ownerNameB;
}
public String getOwnerCityA() {
return ownerCityA;
}
public void setOwnerCityA(String ownerCityA) {
this.ownerCityA = ownerCityA;
}
public String getOwnerCityB() {
return ownerCityB;
}
public void setOwnerCityB(String ownerCityB) {
this.ownerCityB = ownerCityB;
}
}

View File

@ -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.bugs._2236;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
/**
* @author Filip Hrisafov
*/
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );
@Mapping(target = "ownerA.name", source = "carDto.ownerNameA")
@Mapping(target = "ownerA.city", source = "carDto.ownerCityA")
@Mapping(target = "ownerB.name", source = "carDto.ownerNameB")
@Mapping(target = "ownerB.city", source = "carDto.ownerCityB")
@Mapping(target = "name", source = "carDto.name")
@Mapping(target = "type", source = "type")
Car vehicleToCar(Vehicle vehicle);
}

View File

@ -0,0 +1,57 @@
/*
* 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._2236;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Filip Hrisafov
*/
@IssueKey("2236")
@RunWith(AnnotationProcessorTestRunner.class)
@WithClasses({
Car.class,
CarDto.class,
CarMapper.class,
Owner.class,
Vehicle.class,
})
public class Issue2236Test {
@Test
public void shouldCorrectlyMapSameTypesWithDifferentNestedMappings() {
Vehicle vehicle = new Vehicle();
vehicle.setType( "Sedan" );
CarDto carDto = new CarDto();
vehicle.setCarDto( carDto );
carDto.setName( "Private car" );
carDto.setOwnerNameA( "Owner A" );
carDto.setOwnerCityA( "Zurich" );
carDto.setOwnerNameB( "Owner B" );
carDto.setOwnerCityB( "Bern" );
Car car = CarMapper.INSTANCE.vehicleToCar( vehicle );
assertThat( car ).isNotNull();
assertThat( car.getType() ).isEqualTo( "Sedan" );
assertThat( car.getName() ).isEqualTo( "Private car" );
assertThat( car.getOwnerA() ).isNotNull();
assertThat( car.getOwnerA().getName() ).isEqualTo( "Owner A" );
assertThat( car.getOwnerA().getCity() ).isEqualTo( "Zurich" );
assertThat( car.getOwnerB() ).isNotNull();
assertThat( car.getOwnerB().getName() ).isEqualTo( "Owner B" );
assertThat( car.getOwnerB().getCity() ).isEqualTo( "Bern" );
}
}

View File

@ -0,0 +1,31 @@
/*
* 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._2236;
/**
* @author Filip Hrisafov
*/
public class Owner {
private String name;
private String city;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}

View File

@ -0,0 +1,30 @@
/*
* 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._2236;
/**
* @author Filip Hrisafov
*/
public class Vehicle {
private CarDto carDto;
private String type;
public CarDto getCarDto() {
return carDto;
}
public void setCarDto(CarDto carDto) {
this.carDto = carDto;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}

View File

@ -57,9 +57,9 @@ public class FishTankMapperImpl implements FishTankMapper {
FishTankDto fishTankDto = new FishTankDto(); FishTankDto fishTankDto = new FishTankDto();
fishTankDto.setFish( fishToFishDto( source.getFish() ) ); fishTankDto.setFish( fishToFishDto1( source.getFish() ) );
fishTankDto.setMaterial( fishTankToMaterialDto1( source ) ); fishTankDto.setMaterial( fishTankToMaterialDto1( source ) );
fishTankDto.setQuality( waterQualityToWaterQualityDto( source.getQuality() ) ); fishTankDto.setQuality( waterQualityToWaterQualityDto1( source.getQuality() ) );
fishTankDto.setOrnament( ornamentToOrnamentDto( sourceInteriorOrnament( source ) ) ); fishTankDto.setOrnament( ornamentToOrnamentDto( sourceInteriorOrnament( source ) ) );
fishTankDto.setPlant( waterPlantToWaterPlantDto( source.getPlant() ) ); fishTankDto.setPlant( waterPlantToWaterPlantDto( source.getPlant() ) );
fishTankDto.setName( source.getName() ); fishTankDto.setName( source.getName() );
@ -197,6 +197,18 @@ public class FishTankMapperImpl implements FishTankMapper {
return waterPlantDto; 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) { protected MaterialDto fishTankToMaterialDto1(FishTank fishTank) {
if ( fishTank == null ) { if ( fishTank == null ) {
return null; return null;
@ -221,6 +233,31 @@ public class FishTankMapperImpl implements FishTankMapper {
return waterQualityOrganisationDto; 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) { protected Fish fishDtoToFish(FishDto fishDto) {
if ( fishDto == null ) { if ( fishDto == null ) {
return null; return null;