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 javax.lang.model.element.AnnotationMirror;
|
||||
|
||||
/**
|
||||
* 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 String errorMessagePart;
|
||||
private String callingContextTargetPropertyName;
|
||||
private AnnotationMirror positionHint;
|
||||
|
||||
ContainerMappingMethodBuilder(Class<B> selfType, String errorMessagePart) {
|
||||
super( selfType );
|
||||
@ -58,6 +61,11 @@ public abstract class ContainerMappingMethodBuilder<B extends ContainerMappingMe
|
||||
return myself;
|
||||
}
|
||||
|
||||
public B positionHint(AnnotationMirror positionHint) {
|
||||
this.positionHint = positionHint;
|
||||
return myself;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final M build() {
|
||||
Type sourceParameterType = first( method.getSourceParameters() ).getType();
|
||||
@ -88,7 +96,7 @@ public abstract class ContainerMappingMethodBuilder<B extends ContainerMappingMe
|
||||
formattingParameters,
|
||||
criteria,
|
||||
sourceRHS,
|
||||
null,
|
||||
positionHint,
|
||||
() -> forge( sourceRHS, sourceElementType, targetElementType )
|
||||
);
|
||||
|
||||
|
@ -617,6 +617,7 @@ public class PropertyMapping extends ModelElement {
|
||||
.method( methodRef )
|
||||
.selectionParameters( selectionParameters )
|
||||
.callingContextTargetPropertyName( targetPropertyName )
|
||||
.positionHint( positionHint )
|
||||
.build();
|
||||
|
||||
return createForgedAssignment( source, methodRef, iterableMappingMethod );
|
||||
|
@ -298,7 +298,13 @@ public class MappingResolverImpl implements MappingResolver {
|
||||
}
|
||||
|
||||
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() ) {
|
||||
// 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() {
|
||||
}
|
||||
|
||||
@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.WithClasses;
|
||||
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" )
|
||||
@WithClasses( {
|
||||
Cities.class,
|
||||
CityDto.class,
|
||||
CityEntity.class,
|
||||
RiverDto.class,
|
||||
RiverEntity.class,
|
||||
Rivers.class,
|
||||
TopologyDto.class,
|
||||
TopologyEntity.class,
|
||||
TopologyFeatureDto.class,
|
||||
TopologyFeatureEntity.class,
|
||||
TopologyMapper.class
|
||||
} )
|
||||
@RunWith( AnnotationProcessorTestRunner.class )
|
||||
public class IterableAndQualifiersTest {
|
||||
|
||||
@Test
|
||||
@WithClasses(TopologyMapper.class)
|
||||
public void testGenerationBasedOnQualifier() {
|
||||
|
||||
TopologyDto topologyDto1 = new TopologyDto();
|
||||
@ -67,4 +70,39 @@ public class IterableAndQualifiersTest {
|
||||
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;
|
||||
|
||||
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 org.mapstruct.IterableMapping;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Qualifier;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
@ -64,16 +59,4 @@ public abstract class TopologyMapper {
|
||||
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