mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
With this change we are relaxing the error handling when qualifiers are used in `@Mapping` and we allow defining qualifiers for collections / array mapping to mean the same as if it was defined on `@IterableMapping` i.e. apply the qualifier on the element mapping
This commit is contained in:
parent
0c51f7a5aa
commit
bef3482af5
@ -22,6 +22,8 @@ import org.mapstruct.ap.internal.util.Strings;
|
|||||||
|
|
||||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||||
|
|
||||||
|
import javax.lang.model.element.AnnotationMirror;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder that can be used to build {@link ContainerMappingMethod}(s).
|
* Builder that can be used to build {@link ContainerMappingMethod}(s).
|
||||||
*
|
*
|
||||||
@ -37,6 +39,7 @@ public abstract class ContainerMappingMethodBuilder<B extends ContainerMappingMe
|
|||||||
private FormattingParameters formattingParameters;
|
private FormattingParameters formattingParameters;
|
||||||
private String errorMessagePart;
|
private String errorMessagePart;
|
||||||
private String callingContextTargetPropertyName;
|
private String callingContextTargetPropertyName;
|
||||||
|
private AnnotationMirror positionHint;
|
||||||
|
|
||||||
ContainerMappingMethodBuilder(Class<B> selfType, String errorMessagePart) {
|
ContainerMappingMethodBuilder(Class<B> selfType, String errorMessagePart) {
|
||||||
super( selfType );
|
super( selfType );
|
||||||
@ -58,6 +61,11 @@ public abstract class ContainerMappingMethodBuilder<B extends ContainerMappingMe
|
|||||||
return myself;
|
return myself;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public B positionHint(AnnotationMirror positionHint) {
|
||||||
|
this.positionHint = positionHint;
|
||||||
|
return myself;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final M build() {
|
public final M build() {
|
||||||
Type sourceParameterType = first( method.getSourceParameters() ).getType();
|
Type sourceParameterType = first( method.getSourceParameters() ).getType();
|
||||||
@ -88,7 +96,7 @@ public abstract class ContainerMappingMethodBuilder<B extends ContainerMappingMe
|
|||||||
formattingParameters,
|
formattingParameters,
|
||||||
criteria,
|
criteria,
|
||||||
sourceRHS,
|
sourceRHS,
|
||||||
null,
|
positionHint,
|
||||||
() -> forge( sourceRHS, sourceElementType, targetElementType )
|
() -> forge( sourceRHS, sourceElementType, targetElementType )
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -617,6 +617,7 @@ public class PropertyMapping extends ModelElement {
|
|||||||
.method( methodRef )
|
.method( methodRef )
|
||||||
.selectionParameters( selectionParameters )
|
.selectionParameters( selectionParameters )
|
||||||
.callingContextTargetPropertyName( targetPropertyName )
|
.callingContextTargetPropertyName( targetPropertyName )
|
||||||
|
.positionHint( positionHint )
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
return createForgedAssignment( source, methodRef, iterableMappingMethod );
|
return createForgedAssignment( source, methodRef, iterableMappingMethod );
|
||||||
|
@ -298,7 +298,13 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( hasQualfiers() ) {
|
if ( hasQualfiers() ) {
|
||||||
printQualifierMessage( selectionCriteria );
|
if ((sourceType.isCollectionType() || sourceType.isArrayType()) && targetType.isIterableType()) {
|
||||||
|
// Allow forging iterable mapping when no iterable mapping already found
|
||||||
|
return forger.get();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printQualifierMessage( selectionCriteria );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if ( allowMappingMethod() ) {
|
else if ( allowMappingMethod() ) {
|
||||||
// only forge if we would allow mapping method
|
// only forge if we would allow mapping method
|
||||||
|
@ -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.selection.qualifier.errors;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ErroneousMessageByNamedWithIterableMapper {
|
||||||
|
|
||||||
|
@Mapping(target = "elements", qualifiedByName = "SelectMe")
|
||||||
|
Target map(Source source);
|
||||||
|
|
||||||
|
// CHECKSTYLE:OFF
|
||||||
|
class Source {
|
||||||
|
|
||||||
|
public Collection<String> elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Target {
|
||||||
|
|
||||||
|
public Collection<String> elements;
|
||||||
|
}
|
||||||
|
// CHECKSTYLE ON
|
||||||
|
|
||||||
|
}
|
@ -87,4 +87,27 @@ public class QualfierMessageTest {
|
|||||||
)
|
)
|
||||||
public void testNoQualifyingMethodByAnnotationAndNamedFound() {
|
public void testNoQualifyingMethodByAnnotationAndNamedFound() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses(ErroneousMessageByNamedWithIterableMapper.class)
|
||||||
|
@ExpectedCompilationOutcome(
|
||||||
|
value = CompilationResult.FAILED,
|
||||||
|
diagnostics = {
|
||||||
|
@Diagnostic(
|
||||||
|
type = ErroneousMessageByNamedWithIterableMapper.class,
|
||||||
|
kind = ERROR,
|
||||||
|
line = 16,
|
||||||
|
message = "Qualifier error. No method found annotated with @Named#value: [ SelectMe ]. " +
|
||||||
|
"See https://mapstruct.org/faq/#qualifier for more info."),
|
||||||
|
@Diagnostic(
|
||||||
|
type = ErroneousMessageByNamedWithIterableMapper.class,
|
||||||
|
kind = ERROR,
|
||||||
|
line = 16,
|
||||||
|
messageRegExp = "Can't map property.*"),
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public void testNoQualifyingMethodByNamedForForgedIterableFound() {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* 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.selection.qualifier.iterable;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import org.mapstruct.Qualifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Qualifier
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.CLASS)
|
||||||
|
public @interface Cities {
|
||||||
|
|
||||||
|
}
|
@ -15,6 +15,7 @@ import org.junit.runner.RunWith;
|
|||||||
import org.mapstruct.ap.testutil.IssueKey;
|
import org.mapstruct.ap.testutil.IssueKey;
|
||||||
import org.mapstruct.ap.testutil.WithClasses;
|
import org.mapstruct.ap.testutil.WithClasses;
|
||||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -22,20 +23,22 @@ import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
|||||||
*/
|
*/
|
||||||
@IssueKey( "707" )
|
@IssueKey( "707" )
|
||||||
@WithClasses( {
|
@WithClasses( {
|
||||||
|
Cities.class,
|
||||||
CityDto.class,
|
CityDto.class,
|
||||||
CityEntity.class,
|
CityEntity.class,
|
||||||
RiverDto.class,
|
RiverDto.class,
|
||||||
RiverEntity.class,
|
RiverEntity.class,
|
||||||
|
Rivers.class,
|
||||||
TopologyDto.class,
|
TopologyDto.class,
|
||||||
TopologyEntity.class,
|
TopologyEntity.class,
|
||||||
TopologyFeatureDto.class,
|
TopologyFeatureDto.class,
|
||||||
TopologyFeatureEntity.class,
|
TopologyFeatureEntity.class,
|
||||||
TopologyMapper.class
|
|
||||||
} )
|
} )
|
||||||
@RunWith( AnnotationProcessorTestRunner.class )
|
@RunWith( AnnotationProcessorTestRunner.class )
|
||||||
public class IterableAndQualifiersTest {
|
public class IterableAndQualifiersTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@WithClasses(TopologyMapper.class)
|
||||||
public void testGenerationBasedOnQualifier() {
|
public void testGenerationBasedOnQualifier() {
|
||||||
|
|
||||||
TopologyDto topologyDto1 = new TopologyDto();
|
TopologyDto topologyDto1 = new TopologyDto();
|
||||||
@ -67,4 +70,39 @@ public class IterableAndQualifiersTest {
|
|||||||
assertThat( ( (CityEntity) result2.getTopologyFeatures().get( 0 ) ).getPopulation() ).isEqualTo( 800000 );
|
assertThat( ( (CityEntity) result2.getTopologyFeatures().get( 0 ) ).getPopulation() ).isEqualTo( 800000 );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithClasses(TopologyWithoutIterableMappingMapper.class)
|
||||||
|
public void testIterableGeneratorBasedOnQualifier() {
|
||||||
|
|
||||||
|
TopologyWithoutIterableMappingMapper mapper = Mappers.getMapper( TopologyWithoutIterableMappingMapper.class );
|
||||||
|
|
||||||
|
TopologyDto riverTopologyDto = new TopologyDto();
|
||||||
|
List<TopologyFeatureDto> topologyFeatures1 = new ArrayList<>();
|
||||||
|
RiverDto riverDto = new RiverDto();
|
||||||
|
riverDto.setName( "Rhine" );
|
||||||
|
riverDto.setLength( 5 );
|
||||||
|
topologyFeatures1.add( riverDto );
|
||||||
|
riverTopologyDto.setTopologyFeatures( topologyFeatures1 );
|
||||||
|
|
||||||
|
TopologyEntity riverTopology = mapper.mapTopologyAsRiver( riverTopologyDto );
|
||||||
|
assertThat( riverTopology.getTopologyFeatures() ).hasSize( 1 );
|
||||||
|
assertThat( riverTopology.getTopologyFeatures().get( 0 ).getName() ).isEqualTo( "Rhine" );
|
||||||
|
assertThat( riverTopology.getTopologyFeatures().get( 0 ) ).isInstanceOf( RiverEntity.class );
|
||||||
|
assertThat( ( (RiverEntity) riverTopology.getTopologyFeatures().get( 0 ) ).getLength() ).isEqualTo( 5 );
|
||||||
|
|
||||||
|
TopologyDto cityTopologyDto = new TopologyDto();
|
||||||
|
List<TopologyFeatureDto> topologyFeatures2 = new ArrayList<>();
|
||||||
|
CityDto cityDto = new CityDto();
|
||||||
|
cityDto.setName( "Amsterdam" );
|
||||||
|
cityDto.setPopulation( 800000 );
|
||||||
|
topologyFeatures2.add( cityDto );
|
||||||
|
cityTopologyDto.setTopologyFeatures( topologyFeatures2 );
|
||||||
|
|
||||||
|
TopologyEntity cityTopology = mapper.mapTopologyAsCity( cityTopologyDto );
|
||||||
|
assertThat( cityTopology.getTopologyFeatures() ).hasSize( 1 );
|
||||||
|
assertThat( cityTopology.getTopologyFeatures().get( 0 ).getName() ).isEqualTo( "Amsterdam" );
|
||||||
|
assertThat( cityTopology.getTopologyFeatures().get( 0 ) ).isInstanceOf( CityEntity.class );
|
||||||
|
assertThat( ( (CityEntity) cityTopology.getTopologyFeatures().get( 0 ) ).getPopulation() ).isEqualTo( 800000 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* 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.selection.qualifier.iterable;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import org.mapstruct.Qualifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Qualifier
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.CLASS)
|
||||||
|
public @interface Rivers {
|
||||||
|
|
||||||
|
}
|
@ -5,16 +5,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.mapstruct.ap.test.selection.qualifier.iterable;
|
package org.mapstruct.ap.test.selection.qualifier.iterable;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.mapstruct.IterableMapping;
|
import org.mapstruct.IterableMapping;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.Mapping;
|
import org.mapstruct.Mapping;
|
||||||
import org.mapstruct.Qualifier;
|
|
||||||
import org.mapstruct.factory.Mappers;
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,16 +59,4 @@ public abstract class TopologyMapper {
|
|||||||
return topologyFeatureEntity;
|
return topologyFeatureEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Qualifier
|
|
||||||
@Target(ElementType.METHOD)
|
|
||||||
@Retention(RetentionPolicy.CLASS)
|
|
||||||
public @interface Rivers {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Qualifier
|
|
||||||
@Target(ElementType.METHOD)
|
|
||||||
@Retention(RetentionPolicy.CLASS)
|
|
||||||
public @interface Cities {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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.selection.qualifier.iterable;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface TopologyWithoutIterableMappingMapper {
|
||||||
|
|
||||||
|
@Mapping( target = "topologyFeatures", qualifiedBy = Rivers.class )
|
||||||
|
TopologyEntity mapTopologyAsRiver(TopologyDto dto);
|
||||||
|
|
||||||
|
@Mapping( target = "topologyFeatures", qualifiedBy = Cities.class )
|
||||||
|
TopologyEntity mapTopologyAsCity(TopologyDto dto);
|
||||||
|
|
||||||
|
@Rivers
|
||||||
|
default TopologyFeatureEntity mapRiver( TopologyFeatureDto dto ) {
|
||||||
|
TopologyFeatureEntity topologyFeatureEntity = null;
|
||||||
|
if ( dto instanceof RiverDto ) {
|
||||||
|
RiverEntity riverEntity = new RiverEntity();
|
||||||
|
riverEntity.setLength( ( (RiverDto) dto ).getLength() );
|
||||||
|
topologyFeatureEntity = riverEntity;
|
||||||
|
topologyFeatureEntity.setName( dto.getName() );
|
||||||
|
}
|
||||||
|
return topologyFeatureEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Cities
|
||||||
|
default TopologyFeatureEntity mapCity( TopologyFeatureDto dto ) {
|
||||||
|
TopologyFeatureEntity topologyFeatureEntity = null;
|
||||||
|
if ( dto instanceof CityDto ) {
|
||||||
|
CityEntity cityEntity = new CityEntity();
|
||||||
|
cityEntity.setPopulation( ( (CityDto) dto ).getPopulation() );
|
||||||
|
topologyFeatureEntity = cityEntity;
|
||||||
|
topologyFeatureEntity.setName( dto.getName() );
|
||||||
|
}
|
||||||
|
return topologyFeatureEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user