mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
Co-authored-by: sjaakd <sjaakderksen@zonnet.nl>
This commit is contained in:
parent
dfc7528096
commit
8478a5455b
@ -230,18 +230,20 @@ public class MappingMethodOptions {
|
||||
}
|
||||
|
||||
private boolean isRedefined(Set<String> redefinedNames, String inheritedName ) {
|
||||
if ( inheritedName != null ) {
|
||||
for ( String redefinedName : redefinedNames ) {
|
||||
if ( elementsAreContainedIn( redefinedName, inheritedName ) ) {
|
||||
if ( elementsAreContainedIn( inheritedName, redefinedName ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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" );
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user