Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,11 @@ private void addAllNonRedefined(Set<MappingOptions> inheritedMappings) {
}

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;
Expand All @@ -241,7 +243,7 @@ private boolean isRedefined(Set<String> redefinedNames, String inheritedName ) {
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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}

}
Original file line number Diff line number Diff line change
@@ -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
}

}
Original file line number Diff line number Diff line change
@@ -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
}

}
Original file line number Diff line number Diff line change
@@ -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
}

}
Loading