mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#3601 Always use SourceParameterCondition when checking source parameter
This is a breaking change, with this change whenever a source parameter is used as a source for a target property the condition has to apply to source parameters and not properties
This commit is contained in:
parent
52877d36c2
commit
66f4288842
@ -8,7 +8,75 @@ Source properties are ignored anyway, the `BeanMapping#unmappedSourcePolicy` sho
|
|||||||
|
|
||||||
### Bugs
|
### Bugs
|
||||||
|
|
||||||
|
* Breaking change: Presence check method used only once when multiple source parameters are provided (#3601)
|
||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
|
|
||||||
### Build
|
### Build
|
||||||
|
|
||||||
|
## Breaking changes
|
||||||
|
|
||||||
|
### Presence checks for source parameters
|
||||||
|
|
||||||
|
In 1.6, support for presence checks on source parameters has been added.
|
||||||
|
This means that even if you want to map a source parameter directly to some target property the new `@SourceParameterCondition` or `@Condition(appliesTo = ConditionStrategy.SOURCE_PARAMETERS)` should be used.
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
|
||||||
|
If we had the following in 1.5:
|
||||||
|
```java
|
||||||
|
@Mapper
|
||||||
|
public interface OrderMapper {
|
||||||
|
|
||||||
|
@Mapping(source = "dto", target = "customer", conditionQualifiedByName = "mapCustomerFromOrder")
|
||||||
|
Order map(OrderDTO dto);
|
||||||
|
|
||||||
|
@Condition
|
||||||
|
@Named("mapCustomerFromOrder")
|
||||||
|
default boolean mapCustomerFromOrder(OrderDTO dto) {
|
||||||
|
return dto != null && dto.getCustomerName() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Them MapStruct would generate
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class OrderMapperImpl implements OrderMapper {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Order map(OrderDTO dto) {
|
||||||
|
if ( dto == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Order order = new Order();
|
||||||
|
|
||||||
|
if ( mapCustomerFromOrder( dto ) ) {
|
||||||
|
order.setCustomer( orderDtoToCustomer( orderDTO ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In order for the same to be generated in 1.6, the mapper needs to look like this:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Mapper
|
||||||
|
public interface OrderMapper {
|
||||||
|
|
||||||
|
@Mapping(source = "dto", target = "customer", conditionQualifiedByName = "mapCustomerFromOrder")
|
||||||
|
Order map(OrderDTO dto);
|
||||||
|
|
||||||
|
@SourceParameterCondition
|
||||||
|
@Named("mapCustomerFromOrder")
|
||||||
|
default boolean mapCustomerFromOrder(OrderDTO dto) {
|
||||||
|
return dto != null && dto.getCustomerName() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.mapstruct.ap.internal.model.source.selector;
|
package org.mapstruct.ap.internal.model.source.selector;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -22,50 +21,31 @@ import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
|||||||
*/
|
*/
|
||||||
public class SelectionCriteria {
|
public class SelectionCriteria {
|
||||||
|
|
||||||
private final List<TypeMirror> qualifiers = new ArrayList<>();
|
private final QualifyingInfo qualifyingInfo;
|
||||||
private final List<String> qualifiedByNames = new ArrayList<>();
|
|
||||||
private final String targetPropertyName;
|
private final String targetPropertyName;
|
||||||
private final TypeMirror qualifyingResultType;
|
|
||||||
private final SourceRHS sourceRHS;
|
private final SourceRHS sourceRHS;
|
||||||
private boolean ignoreQualifiers = false;
|
private boolean ignoreQualifiers = false;
|
||||||
private Type type;
|
private Type type;
|
||||||
private final boolean allowDirect;
|
private final MappingControl mappingControl;
|
||||||
private final boolean allowConversion;
|
|
||||||
private final boolean allowMappingMethod;
|
|
||||||
private final boolean allow2Steps;
|
|
||||||
|
|
||||||
public SelectionCriteria(SelectionParameters selectionParameters, MappingControl mappingControl,
|
public SelectionCriteria(SelectionParameters selectionParameters, MappingControl mappingControl,
|
||||||
String targetPropertyName, Type type) {
|
String targetPropertyName, Type type) {
|
||||||
if ( selectionParameters != null ) {
|
this(
|
||||||
if ( type == Type.PRESENCE_CHECK ) {
|
QualifyingInfo.fromSelectionParameters( selectionParameters ),
|
||||||
qualifiers.addAll( selectionParameters.getConditionQualifiers() );
|
selectionParameters != null ? selectionParameters.getSourceRHS() : null,
|
||||||
qualifiedByNames.addAll( selectionParameters.getConditionQualifyingNames() );
|
mappingControl,
|
||||||
}
|
targetPropertyName,
|
||||||
else {
|
type
|
||||||
qualifiers.addAll( selectionParameters.getQualifiers() );
|
);
|
||||||
qualifiedByNames.addAll( selectionParameters.getQualifyingNames() );
|
}
|
||||||
}
|
|
||||||
qualifyingResultType = selectionParameters.getResultType();
|
private SelectionCriteria(QualifyingInfo qualifyingInfo, SourceRHS sourceRHS, MappingControl mappingControl,
|
||||||
sourceRHS = selectionParameters.getSourceRHS();
|
String targetPropertyName, Type type) {
|
||||||
}
|
this.qualifyingInfo = qualifyingInfo;
|
||||||
else {
|
|
||||||
this.qualifyingResultType = null;
|
|
||||||
sourceRHS = null;
|
|
||||||
}
|
|
||||||
if ( mappingControl != null ) {
|
|
||||||
this.allowDirect = mappingControl.allowDirect();
|
|
||||||
this.allowConversion = mappingControl.allowTypeConversion();
|
|
||||||
this.allowMappingMethod = mappingControl.allowMappingMethod();
|
|
||||||
this.allow2Steps = mappingControl.allowBy2Steps();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.allowDirect = true;
|
|
||||||
this.allowConversion = true;
|
|
||||||
this.allowMappingMethod = true;
|
|
||||||
this.allow2Steps = true;
|
|
||||||
}
|
|
||||||
this.targetPropertyName = targetPropertyName;
|
this.targetPropertyName = targetPropertyName;
|
||||||
|
this.sourceRHS = sourceRHS;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
this.mappingControl = mappingControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -109,11 +89,11 @@ public class SelectionCriteria {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<TypeMirror> getQualifiers() {
|
public List<TypeMirror> getQualifiers() {
|
||||||
return ignoreQualifiers ? Collections.emptyList() : qualifiers;
|
return ignoreQualifiers ? Collections.emptyList() : qualifyingInfo.qualifiers();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getQualifiedByNames() {
|
public List<String> getQualifiedByNames() {
|
||||||
return ignoreQualifiers ? Collections.emptyList() : qualifiedByNames;
|
return ignoreQualifiers ? Collections.emptyList() : qualifyingInfo.qualifiedByNames();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTargetPropertyName() {
|
public String getTargetPropertyName() {
|
||||||
@ -121,7 +101,7 @@ public class SelectionCriteria {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public TypeMirror getQualifyingResultType() {
|
public TypeMirror getQualifyingResultType() {
|
||||||
return qualifyingResultType;
|
return qualifyingInfo.qualifyingResultType();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPreferUpdateMapping() {
|
public boolean isPreferUpdateMapping() {
|
||||||
@ -137,23 +117,23 @@ public class SelectionCriteria {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasQualfiers() {
|
public boolean hasQualfiers() {
|
||||||
return !qualifiedByNames.isEmpty() || !qualifiers.isEmpty();
|
return !qualifyingInfo.qualifiedByNames().isEmpty() || !qualifyingInfo.qualifiers().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAllowDirect() {
|
public boolean isAllowDirect() {
|
||||||
return allowDirect;
|
return mappingControl == null || mappingControl.allowDirect();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAllowConversion() {
|
public boolean isAllowConversion() {
|
||||||
return allowConversion;
|
return mappingControl == null || mappingControl.allowTypeConversion();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAllowMappingMethod() {
|
public boolean isAllowMappingMethod() {
|
||||||
return allowMappingMethod;
|
return mappingControl == null || mappingControl.allowMappingMethod();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAllow2Steps() {
|
public boolean isAllow2Steps() {
|
||||||
return allow2Steps;
|
return mappingControl == null || mappingControl.allowBy2Steps();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSelfAllowed() {
|
public boolean isSelfAllowed() {
|
||||||
@ -181,7 +161,22 @@ public class SelectionCriteria {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static SelectionCriteria forPresenceCheckMethods(SelectionParameters selectionParameters) {
|
public static SelectionCriteria forPresenceCheckMethods(SelectionParameters selectionParameters) {
|
||||||
return new SelectionCriteria( selectionParameters, null, null, Type.PRESENCE_CHECK );
|
SourceRHS sourceRHS = selectionParameters.getSourceRHS();
|
||||||
|
Type type;
|
||||||
|
QualifyingInfo qualifyingInfo = new QualifyingInfo(
|
||||||
|
selectionParameters.getConditionQualifiers(),
|
||||||
|
selectionParameters.getConditionQualifyingNames(),
|
||||||
|
selectionParameters.getResultType()
|
||||||
|
);
|
||||||
|
if ( sourceRHS != null && sourceRHS.isSourceReferenceParameter() ) {
|
||||||
|
// If the source reference is for a source parameter,
|
||||||
|
// then the presence check should be for the source parameter
|
||||||
|
type = Type.SOURCE_PARAMETER_CHECK;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
type = Type.PRESENCE_CHECK;
|
||||||
|
}
|
||||||
|
return new SelectionCriteria( qualifyingInfo, sourceRHS, null, null, type );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SelectionCriteria forSourceParameterCheckMethods(SelectionParameters selectionParameters) {
|
public static SelectionCriteria forSourceParameterCheckMethods(SelectionParameters selectionParameters) {
|
||||||
@ -193,6 +188,50 @@ public class SelectionCriteria {
|
|||||||
return new SelectionCriteria( selectionParameters, mappingControl, null, Type.SELF_NOT_ALLOWED );
|
return new SelectionCriteria( selectionParameters, mappingControl, null, Type.SELF_NOT_ALLOWED );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class QualifyingInfo {
|
||||||
|
|
||||||
|
private static final QualifyingInfo EMPTY = new QualifyingInfo(
|
||||||
|
Collections.emptyList(),
|
||||||
|
Collections.emptyList(),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
private final List<TypeMirror> qualifiers;
|
||||||
|
private final List<String> qualifiedByNames;
|
||||||
|
private final TypeMirror qualifyingResultType;
|
||||||
|
|
||||||
|
private QualifyingInfo(List<TypeMirror> qualifiers, List<String> qualifiedByNames,
|
||||||
|
TypeMirror qualifyingResultType) {
|
||||||
|
this.qualifiers = qualifiers;
|
||||||
|
this.qualifiedByNames = qualifiedByNames;
|
||||||
|
this.qualifyingResultType = qualifyingResultType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TypeMirror> qualifiers() {
|
||||||
|
return qualifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> qualifiedByNames() {
|
||||||
|
return qualifiedByNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeMirror qualifyingResultType() {
|
||||||
|
return qualifyingResultType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static QualifyingInfo fromSelectionParameters(SelectionParameters selectionParameters) {
|
||||||
|
if ( selectionParameters == null ) {
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
return new QualifyingInfo(
|
||||||
|
selectionParameters.getQualifiers(),
|
||||||
|
selectionParameters.getQualifyingNames(),
|
||||||
|
selectionParameters.getResultType()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public enum Type {
|
public enum Type {
|
||||||
PREFER_UPDATE_MAPPING,
|
PREFER_UPDATE_MAPPING,
|
||||||
OBJECT_FACTORY,
|
OBJECT_FACTORY,
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* 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._3601;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.SourceParameterCondition;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface Issue3601Mapper {
|
||||||
|
|
||||||
|
Issue3601Mapper INSTANCE = Mappers.getMapper( Issue3601Mapper.class );
|
||||||
|
|
||||||
|
@Mapping(target = "currentId", source = "source.uuid")
|
||||||
|
@Mapping(target = "targetIds", source = "sourceIds")
|
||||||
|
Target map(Source source, List<String> sourceIds);
|
||||||
|
|
||||||
|
@SourceParameterCondition
|
||||||
|
default boolean isNotEmpty(List<String> elements) {
|
||||||
|
return elements != null && !elements.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
class Source {
|
||||||
|
private final String uuid;
|
||||||
|
|
||||||
|
public Source(String uuid) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Target {
|
||||||
|
//CHECKSTYLE:OFF
|
||||||
|
public String currentId;
|
||||||
|
public List<String> targetIds;
|
||||||
|
//CHECKSTYLE:ON
|
||||||
|
}
|
||||||
|
}
|
@ -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.bugs._3601;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
import org.mapstruct.ap.testutil.IssueKey;
|
||||||
|
import org.mapstruct.ap.testutil.ProcessorTest;
|
||||||
|
import org.mapstruct.ap.testutil.WithClasses;
|
||||||
|
import org.mapstruct.ap.testutil.runner.GeneratedSource;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@IssueKey("3601")
|
||||||
|
class Issue3601Test {
|
||||||
|
|
||||||
|
@RegisterExtension
|
||||||
|
final GeneratedSource generatedSource = new GeneratedSource();
|
||||||
|
|
||||||
|
@ProcessorTest
|
||||||
|
@WithClasses( Issue3601Mapper.class )
|
||||||
|
void shouldUseSourceParameterPresenceCheckCorrectly() {
|
||||||
|
Issue3601Mapper.Target target = Issue3601Mapper.INSTANCE.map(
|
||||||
|
new Issue3601Mapper.Source( "test1" ),
|
||||||
|
Collections.emptyList()
|
||||||
|
);
|
||||||
|
|
||||||
|
assertThat( target ).isNotNull();
|
||||||
|
assertThat( target.currentId ).isEqualTo( "test1" );
|
||||||
|
assertThat( target.targetIds ).isNull();
|
||||||
|
|
||||||
|
target = Issue3601Mapper.INSTANCE.map(
|
||||||
|
null,
|
||||||
|
Collections.emptyList()
|
||||||
|
);
|
||||||
|
|
||||||
|
assertThat( target ).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -5,10 +5,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.mapstruct.ap.test.conditional.qualifier;
|
package org.mapstruct.ap.test.conditional.qualifier;
|
||||||
|
|
||||||
import org.mapstruct.Condition;
|
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.Mapping;
|
import org.mapstruct.Mapping;
|
||||||
import org.mapstruct.Named;
|
import org.mapstruct.Named;
|
||||||
|
import org.mapstruct.SourceParameterCondition;
|
||||||
import org.mapstruct.factory.Mappers;
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -29,13 +29,13 @@ public interface ConditionalMethodWithSourceToTargetMapper {
|
|||||||
|
|
||||||
Address convertToAddress(OrderDTO orderDTO);
|
Address convertToAddress(OrderDTO orderDTO);
|
||||||
|
|
||||||
@Condition
|
@SourceParameterCondition
|
||||||
@Named("mapCustomerFromOrder")
|
@Named("mapCustomerFromOrder")
|
||||||
default boolean mapCustomerFromOrder(OrderDTO orderDTO) {
|
default boolean mapCustomerFromOrder(OrderDTO orderDTO) {
|
||||||
return orderDTO != null && ( orderDTO.getCustomerName() != null || mapAddressFromOrder( orderDTO ) );
|
return orderDTO != null && ( orderDTO.getCustomerName() != null || mapAddressFromOrder( orderDTO ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Condition
|
@SourceParameterCondition
|
||||||
@Named("mapAddressFromOrder")
|
@Named("mapAddressFromOrder")
|
||||||
default boolean mapAddressFromOrder(OrderDTO orderDTO) {
|
default boolean mapAddressFromOrder(OrderDTO orderDTO) {
|
||||||
return orderDTO != null && ( orderDTO.getLine1() != null || orderDTO.getLine2() != null );
|
return orderDTO != null && ( orderDTO.getLine1() != null || orderDTO.getLine2() != null );
|
||||||
|
Loading…
x
Reference in New Issue
Block a user