#2278 inherited property ignored due to ignore on nested level (#2332)

Co-authored-by: sjaakd <sjaakderksen@zonnet.nl>
This commit is contained in:
Sjaak Derksen 2021-01-23 09:15:46 +01:00 committed by Filip Hrisafov
parent 15b3469f04
commit 0c51f7a5aa
7 changed files with 435 additions and 4 deletions

View File

@ -230,9 +230,11 @@ public class MappingMethodOptions {
}
private boolean isRedefined(Set<String> redefinedNames, String inheritedName ) {
for ( String redefinedName : redefinedNames ) {
if ( elementsAreContainedIn( redefinedName, inheritedName ) ) {
return true;
if ( inheritedName != null ) {
for ( String redefinedName : redefinedNames ) {
if ( elementsAreContainedIn( inheritedName, redefinedName ) ) {
return true;
}
}
}
return false;
@ -241,7 +243,7 @@ public class MappingMethodOptions {
private boolean elementsAreContainedIn( String redefinedName, String inheritedName ) {
if ( inheritedName != null && redefinedName.startsWith( inheritedName ) ) {
// it is possible to redefine an exact matching source name, because the same source can be mapped to
// multiple targets. It is not possible for target, but caught by the Set and equals methoded in
// multiple targets. It is not possible for target, but caught by the Set and equals method in
// MappingOptions. SourceName == null also could hint at redefinition
if ( redefinedName.length() > inheritedName.length() ) {
// redefined.lenght() > inherited.length(), first following character should be separator

View File

@ -0,0 +1,62 @@
/*
* 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._2278;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
/**
* ReproducerA
*
*/
@Mapper
public interface Issue2278MapperA {
Issue2278MapperA INSTANCE = Mappers.getMapper( Issue2278MapperA.class );
@Mapping( target = "detailsDTO", source = "details" )
@Mapping( target = "detailsDTO.fuelType", ignore = true )
CarDTO map(Car in);
// checkout the Issue2278ReferenceMapper, the @InheritInverseConfiguration
// is de-facto @Mapping( target = "details", source = "detailsDTO" )
@InheritInverseConfiguration
@Mapping( target = "details.model", ignore = true )
@Mapping( target = "details.type", constant = "gto")
@Mapping( target = "details.fuel", source = "detailsDTO.fuelType")
Car map(CarDTO in);
class Car {
//CHECKSTYLE:OFF
public Details details;
//CHECKSTYLE:ON
}
class CarDTO {
//CHECKSTYLE:OFF
public DetailsDTO detailsDTO;
//CHECKSTYLE:ON
}
class Details {
//CHECKSTYLE:OFF
public String brand;
public String model;
public String type;
public String fuel;
//CHECKSTYLE:ON
}
class DetailsDTO {
//CHECKSTYLE:OFF
public String brand;
public String fuelType;
//CHECKSTYLE:ON
}
}

View File

@ -0,0 +1,65 @@
/*
* 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._2278;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
/**
* ReproducerB
*
*/
@Mapper
public interface Issue2278MapperB {
Issue2278MapperB INSTANCE = Mappers.getMapper( Issue2278MapperB.class );
// id mapping is cros-linked
@Mapping( target = "amount", source = "price" )
@Mapping( target = "detailsDTO.brand", source = "details.type" )
@Mapping( target = "detailsDTO.id1", source = "details.id2" )
@Mapping( target = "detailsDTO.id2", source = "details.id1" )
CarDTO map(Car in);
// inherit inverse, but undo cross-link in one sweep
@InheritInverseConfiguration // inherits all
@Mapping( target = "details", source = "detailsDTO" ) // resets everything on details <> detailsDto
@Mapping( target = "details.type", source = "detailsDTO.brand" ) // so this needs to be redone
Car map2(CarDTO in);
class Car {
//CHECKSTYLE:OFF
public float price;
public Details details;
//CHECKSTYLE:ON
}
class CarDTO {
//CHECKSTYLE:OFF
public float amount;
public DetailsDTO detailsDTO;
//CHECKSTYLE:ON
}
class Details {
//CHECKSTYLE:OFF
public String type;
public String id1;
public String id2;
//CHECKSTYLE:ON
}
class DetailsDTO {
//CHECKSTYLE:OFF
public String brand;
public String id1;
public String id2;
//CHECKSTYLE:ON
}
}

View File

@ -0,0 +1,54 @@
/*
* 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._2278;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
/**
* A reference mapper, that checks how a regular forward merge works
*/
@Mapper
public interface Issue2278ReferenceMapper {
Issue2278ReferenceMapper INSTANCE = Mappers.getMapper( Issue2278ReferenceMapper.class );
@Mapping( target = "details", source = "detailsDTO" )
@Mapping( target = "details.model", ignore = true )
@Mapping( target = "details.type", constant = "gto")
@Mapping( target = "details.fuel", source = "detailsDTO.fuelType")
Car map(CarDTO in);
class Car {
//CHECKSTYLE:OFF
public Details details;
//CHECKSTYLE:ON
}
class CarDTO {
//CHECKSTYLE:OFF
public DetailsDTO detailsDTO;
//CHECKSTYLE:ON
}
class Details {
//CHECKSTYLE:OFF
public String brand;
public String model;
public String type;
public String fuel;
//CHECKSTYLE:ON
}
class DetailsDTO {
//CHECKSTYLE:OFF
public String brand;
public String fuelType;
//CHECKSTYLE:ON
}
}

View File

@ -0,0 +1,95 @@
/*
* 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._2278;
import static org.assertj.core.api.Assertions.assertThat;
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;
@IssueKey("2278")
@RunWith( AnnotationProcessorTestRunner.class)
public class Issue2278Test {
@Test
@WithClasses( Issue2278ReferenceMapper.class )
public void testReferenceMergeBehaviour() {
Issue2278ReferenceMapper.CarDTO dto = new Issue2278ReferenceMapper.CarDTO();
dto.detailsDTO = new Issue2278ReferenceMapper.DetailsDTO();
dto.detailsDTO.brand = "Ford";
dto.detailsDTO.fuelType = "petrol";
Issue2278ReferenceMapper.Car target = Issue2278ReferenceMapper.INSTANCE.map( dto );
assertThat( target ).isNotNull();
assertThat( target.details ).isNotNull();
assertThat( target.details.brand ).isEqualTo( "Ford" );
assertThat( target.details.model ).isNull();
assertThat( target.details.type ).isEqualTo( "gto" );
assertThat( target.details.fuel ).isEqualTo( "petrol" );
}
@Test
@WithClasses( Issue2278MapperA.class )
public void shouldBehaveJustAsTestReferenceMergeBehaviour() {
Issue2278MapperA.CarDTO dto = new Issue2278MapperA.CarDTO();
dto.detailsDTO = new Issue2278MapperA.DetailsDTO();
dto.detailsDTO.brand = "Ford";
dto.detailsDTO.fuelType = "petrol";
Issue2278MapperA.Car target = Issue2278MapperA.INSTANCE.map( dto );
assertThat( target ).isNotNull();
assertThat( target.details ).isNotNull();
assertThat( target.details.brand ).isEqualTo( "Ford" );
assertThat( target.details.model ).isNull();
assertThat( target.details.type ).isEqualTo( "gto" );
assertThat( target.details.fuel ).isEqualTo( "petrol" );
}
@Test
@WithClasses( Issue2278MapperB.class )
public void shouldOverrideDetailsMappingWithRedefined() {
Issue2278MapperB.Car source = new Issue2278MapperB.Car();
source.details = new Issue2278MapperB.Details();
source.details.type = "Ford";
source.details.id1 = "id1";
source.details.id2 = "id2";
source.price = 20000f;
Issue2278MapperB.CarDTO target1 = Issue2278MapperB.INSTANCE.map( source );
assertThat( target1 ).isNotNull();
assertThat( target1.amount ).isEqualTo( 20000f );
assertThat( target1.detailsDTO ).isNotNull();
assertThat( target1.detailsDTO.brand ).isEqualTo( "Ford" );
assertThat( target1.detailsDTO.id1 ).isEqualTo( "id2" );
assertThat( target1.detailsDTO.id2 ).isEqualTo( "id1" );
// restore the mappings, just to make it logical again
target1.detailsDTO.id1 = "id1";
target1.detailsDTO.id2 = "id2";
// now check the reverse inheritance
Issue2278MapperB.Car target2 = Issue2278MapperB.INSTANCE.map2( target1 );
assertThat( target2 ).isNotNull();
assertThat( target2.price ).isEqualTo( 20000f );
assertThat( target2.details ).isNotNull();
assertThat( target2.details.type ).isEqualTo( "Ford" ); // should inherit
assertThat( target2.details.id1 ).isEqualTo( "id1" ); // should be undone
assertThat( target2.details.id2 ).isEqualTo( "id2" ); // should be undone
}
}

View File

@ -0,0 +1,115 @@
/*
* 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._2318;
import org.mapstruct.InheritConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.MapperConfig;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper(config = Issue2318Mapper.Config.class)
public interface Issue2318Mapper {
Issue2318Mapper INSTANCE = Mappers.getMapper( Issue2318Mapper.class );
@MapperConfig
interface Config {
@Mapping(target = "parentValue1", source = "holder")
TargetParent mapParent(SourceParent parent);
@InheritConfiguration(name = "mapParent")
@Mapping(target = "childValue", source = "value")
@Mapping(target = "parentValue2", source = "holder.parentValue2")
TargetChild mapChild(SourceChild child);
}
@InheritConfiguration(name = "mapChild")
TargetChild mapChild(SourceChild child);
default String parentValue1(SourceParent.Holder holder) {
return holder.getParentValue1();
}
class SourceParent {
private Holder holder;
public Holder getHolder() {
return holder;
}
public void setHolder(Holder holder) {
this.holder = holder;
}
public static class Holder {
private String parentValue1;
private Integer parentValue2;
public String getParentValue1() {
return parentValue1;
}
public void setParentValue1(String parentValue1) {
this.parentValue1 = parentValue1;
}
public Integer getParentValue2() {
return parentValue2;
}
public void setParentValue2(Integer parentValue2) {
this.parentValue2 = parentValue2;
}
}
}
class SourceChild extends SourceParent {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
class TargetParent {
private String parentValue1;
private Integer parentValue2;
public String getParentValue1() {
return parentValue1;
}
public void setParentValue1(String parentValue1) {
this.parentValue1 = parentValue1;
}
public Integer getParentValue2() {
return parentValue2;
}
public void setParentValue2(Integer parentValue2) {
this.parentValue2 = parentValue2;
}
}
class TargetChild extends TargetParent {
private String childValue;
public String getChildValue() {
return childValue;
}
public void setChildValue(String childValue) {
this.childValue = childValue;
}
}
}

View File

@ -0,0 +1,38 @@
/*
* 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._2318;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.ap.test.bugs._2318.Issue2318Mapper.SourceChild;
import org.mapstruct.ap.test.bugs._2318.Issue2318Mapper.TargetChild;
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;
@IssueKey("2318")
@RunWith(AnnotationProcessorTestRunner.class)
public class Issue2318Test {
@Test
@WithClasses( Issue2318Mapper.class )
public void shouldMap() {
SourceChild source = new SourceChild();
source.setValue( "From child" );
source.setHolder( new Issue2318Mapper.SourceParent.Holder() );
source.getHolder().setParentValue1( "From parent" );
source.getHolder().setParentValue2( 12 );
TargetChild target = Issue2318Mapper.INSTANCE.mapChild( source );
assertThat( target.getParentValue1() ).isEqualTo( "From parent" );
assertThat( target.getParentValue2() ).isEqualTo( 12 );
assertThat( target.getChildValue() ).isEqualTo( "From child" );
}
}