Skip to content

Commit 459f57e

Browse files
authored
mapstruct#1571 apply nullvaluecheck strategy on all relevant levels
1 parent dcddba6 commit 459f57e

19 files changed

Lines changed: 373 additions & 45 deletions

File tree

core-common/src/main/java/org/mapstruct/BeanMapping.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import java.lang.annotation.RetentionPolicy;
1212
import java.lang.annotation.Target;
1313

14+
import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
15+
1416
/**
1517
* Configures the mapping between two bean types.
1618
* <p>
@@ -61,6 +63,15 @@
6163
*/
6264
NullValueMappingStrategy nullValueMappingStrategy() default NullValueMappingStrategy.RETURN_NULL;
6365

66+
/**
67+
* Determines when to include a null check on the source property value of a bean mapping.
68+
*
69+
* Can be overridden by the one on {@link MapperConfig}, {@link Mapper} or {@link Mapping}.
70+
*
71+
* @return strategy how to do null checking
72+
*/
73+
NullValueCheckStrategy nullValueCheckStrategy() default ON_IMPLICIT_CONVERSION;
74+
6475
/**
6576
* Default ignore all mappings. All mappings have to be defined manually. No automatic mapping will take place. No
6677
* warning will be issued on missing target properties.

core-common/src/main/java/org/mapstruct/Mapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@
153153
/**
154154
* Determines when to include a null check on the source property value of a bean mapping.
155155
*
156-
* Can be overridden by the one on {@link MapperConfig} or {@link Mapping}.
156+
* Can be overridden by the one on {@link MapperConfig}, {@link BeanMapping} or {@link Mapping}.
157157
*
158158
* @return strategy how to do null checking
159159
*/

core-common/src/main/java/org/mapstruct/MapperConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ MappingInheritanceStrategy mappingInheritanceStrategy()
138138
/**
139139
* Determines when to include a null check on the source property value of a bean mapping.
140140
*
141-
* Can be overridden by the one on {@link Mapper} or {@link Mapping}.
141+
* Can be overridden by the one on {@link Mapper}, {@link BeanMapping} or {@link Mapping}.
142142
*
143143
* @return strategy how to do null checking
144144
*/

core-jdk8/src/main/java/org/mapstruct/Mapping.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import java.text.DecimalFormat;
1616
import java.util.Date;
1717

18+
import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
19+
1820
/**
1921
* Configures the mapping of one bean attribute or enum constant.
2022
* <p>
@@ -255,4 +257,14 @@
255257
* @return Default value to set in case the source property is {@code null}.
256258
*/
257259
String defaultValue() default "";
260+
261+
/**
262+
* Determines when to include a null check on the source property value of a bean mapping.
263+
*
264+
* Can be overridden by the one on {@link MapperConfig}, {@link Mapper} or {@link BeanMapping}.
265+
*
266+
* @return strategy how to do null checking
267+
*/
268+
NullValueCheckStrategy nullValueCheckStrategy() default ON_IMPLICIT_CONVERSION;
269+
258270
}

core/src/main/java/org/mapstruct/Mapping.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import java.text.DecimalFormat;
1515
import java.util.Date;
1616

17+
import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
18+
1719
/**
1820
* Configures the mapping of one bean attribute or enum constant.
1921
* <p>
@@ -260,4 +262,14 @@
260262
* @return Default value to set in case the source property is {@code null}.
261263
*/
262264
String defaultValue() default "";
265+
266+
/**
267+
* Determines when to include a null check on the source property value of a bean mapping.
268+
*
269+
* Can be overridden by the one on {@link MapperConfig}, {@link Mapper} or {@link BeanMapping}.
270+
*
271+
* @return strategy how to do null checking
272+
*/
273+
NullValueCheckStrategy nullValueCheckStrategy() default ON_IMPLICIT_CONVERSION;
274+
263275
}

processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,6 @@ public static class Builder {
7575
private Set<String> targetProperties;
7676
private final List<PropertyMapping> propertyMappings = new ArrayList<PropertyMapping>();
7777
private final Set<Parameter> unprocessedSourceParameters = new HashSet<Parameter>();
78-
private NullValueMappingStrategyPrism nullValueMappingStrategy;
79-
private SelectionParameters selectionParameters;
8078
private final Set<String> existingVariableNames = new HashSet<String>();
8179
private Map<String, List<Mapping>> methodMappings;
8280
private SingleMappingByTargetPropertyNameFunction singleMapping;
@@ -135,16 +133,6 @@ private Builder setupMethodWithMapping(Method sourceMethod) {
135133
return this;
136134
}
137135

138-
public Builder selectionParameters(SelectionParameters selectionParameters) {
139-
this.selectionParameters = selectionParameters;
140-
return this;
141-
}
142-
143-
public Builder nullValueMappingStrategy(NullValueMappingStrategyPrism nullValueMappingStrategy) {
144-
this.nullValueMappingStrategy = nullValueMappingStrategy;
145-
return this;
146-
}
147-
148136
public BeanMappingMethod build() {
149137
// map properties with mapping
150138
boolean mappingErrorOccured = handleDefinedMappings();
@@ -168,12 +156,20 @@ public BeanMappingMethod build() {
168156
reportErrorForUnmappedTargetPropertiesIfRequired();
169157
reportErrorForUnmappedSourcePropertiesIfRequired();
170158

159+
// get bean mapping (when specified as annotation )
160+
BeanMapping beanMapping = method.getMappingOptions().getBeanMapping();
161+
BeanMappingPrism beanMappingPrism = BeanMappingPrism.getInstanceOn( method.getExecutable() );
162+
171163
// mapNullToDefault
164+
NullValueMappingStrategyPrism nullValueMappingStrategy =
165+
beanMapping != null ? beanMapping.getNullValueMappingStrategy() : null;
172166
boolean mapNullToDefault = method.getMapperConfiguration().isMapToDefault( nullValueMappingStrategy );
173167

174168

175-
BeanMappingPrism beanMappingPrism = BeanMappingPrism.getInstanceOn( method.getExecutable() );
169+
// selectionParameters
170+
SelectionParameters selectionParameters = beanMapping != null ? beanMapping.getSelectionParameters() : null;
176171

172+
// check if there's a factory method for the result type
177173
MethodReference factoryMethod = null;
178174
if ( !method.isUpdateMethod() ) {
179175
factoryMethod = ObjectFactoryMethodResolver.getFactoryMethod(
@@ -481,6 +477,7 @@ else if ( mapping.getSourceName() != null ) {
481477
.dependsOn( mapping.getDependsOn() )
482478
.defaultValue( mapping.getDefaultValue() )
483479
.defaultJavaExpression( mapping.getDefaultJavaExpression() )
480+
.nullValueCheckStrategyPrism( mapping.getNullValueCheckStrategy() )
484481
.build();
485482
handledTargets.add( propertyName );
486483
unprocessedSourceParameters.remove( sourceRef.getParameter() );
@@ -597,6 +594,8 @@ private void applyPropertyNameBasedMapping() {
597594
.existingVariableNames( existingVariableNames )
598595
.dependsOn( mapping != null ? mapping.getDependsOn() : Collections.<String>emptyList() )
599596
.forgeMethodWithMappingOptions( extractAdditionalOptions( targetPropertyName, false ) )
597+
.nullValueCheckStrategyPrism( mapping != null ? mapping.getNullValueCheckStrategy()
598+
: null )
600599
.build();
601600

602601
unprocessedSourceParameters.remove( sourceParameter );
@@ -660,6 +659,7 @@ private void applyParameterNameBasedMapping() {
660659
.existingVariableNames( existingVariableNames )
661660
.dependsOn( mapping != null ? mapping.getDependsOn() : Collections.<String>emptyList() )
662661
.forgeMethodWithMappingOptions( extractAdditionalOptions( targetProperty.getKey(), false ) )
662+
.nullValueCheckStrategyPrism( mapping != null ? mapping.getNullValueCheckStrategy() : null )
663663
.build();
664664

665665
propertyMappings.add( propertyMapping );

processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ public class CollectionAssignmentBuilder {
5757
private PropertyMapping.TargetWriteAccessorType targetAccessorType;
5858
private Assignment assignment;
5959
private SourceRHS sourceRHS;
60+
private NullValueCheckStrategyPrism nullValueCheckStrategy;
6061

6162
public CollectionAssignmentBuilder mappingBuilderContext(MappingBuilderContext ctx) {
6263
this.ctx = ctx;
@@ -108,6 +109,11 @@ public CollectionAssignmentBuilder rightHandSide(SourceRHS sourceRHS) {
108109
return this;
109110
}
110111

112+
public CollectionAssignmentBuilder nullValueCheckStrategy( NullValueCheckStrategyPrism nullValueCheckStrategy ) {
113+
this.nullValueCheckStrategy = nullValueCheckStrategy;
114+
return this;
115+
}
116+
111117
public Assignment build() {
112118
Assignment result = assignment;
113119

@@ -146,14 +152,14 @@ else if ( method.isUpdateMethod() && !targetImmutable ) {
146152
result,
147153
method.getThrownTypes(),
148154
targetType,
149-
method.getMapperConfiguration().getNullValueCheckStrategy(),
155+
nullValueCheckStrategy,
150156
ctx.getTypeFactory(),
151157
PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType ),
152158
mapNullToDefault()
153159
);
154160
}
155161
else if ( result.getType() == Assignment.AssignmentType.DIRECT ||
156-
method.getMapperConfiguration().getNullValueCheckStrategy() == NullValueCheckStrategyPrism.ALWAYS ) {
162+
nullValueCheckStrategy == NullValueCheckStrategyPrism.ALWAYS ) {
157163

158164
result = new SetterWrapperForCollectionsAndMapsWithNullCheck(
159165
result,
@@ -199,4 +205,5 @@ private boolean mapNullToDefault() {
199205
return method.getMapperConfiguration().getNullValueMappingStrategy()
200206
== NullValueMappingStrategyPrism.RETURN_DEFAULT;
201207
}
208+
202209
}

processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,11 @@
55
*/
66
package org.mapstruct.ap.internal.model;
77

8-
import static org.mapstruct.ap.internal.model.common.Assignment.AssignmentType.DIRECT;
9-
import static org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism.ALWAYS;
10-
import static org.mapstruct.ap.internal.util.Collections.first;
11-
import static org.mapstruct.ap.internal.util.Collections.last;
12-
138
import java.util.ArrayList;
149
import java.util.Arrays;
1510
import java.util.Collections;
1611
import java.util.List;
1712
import java.util.Set;
18-
1913
import javax.lang.model.element.AnnotationMirror;
2014
import javax.lang.model.element.ExecutableElement;
2115
import javax.lang.model.type.DeclaredType;
@@ -33,6 +27,7 @@
3327
import org.mapstruct.ap.internal.model.common.Parameter;
3428
import org.mapstruct.ap.internal.model.common.SourceRHS;
3529
import org.mapstruct.ap.internal.model.common.Type;
30+
import org.mapstruct.ap.internal.model.source.BeanMapping;
3631
import org.mapstruct.ap.internal.model.source.ForgedMethod;
3732
import org.mapstruct.ap.internal.model.source.ForgedMethodHistory;
3833
import org.mapstruct.ap.internal.model.source.MappingOptions;
@@ -52,6 +47,11 @@
5247
import org.mapstruct.ap.internal.util.ValueProvider;
5348
import org.mapstruct.ap.internal.util.accessor.Accessor;
5449

50+
import static org.mapstruct.ap.internal.model.common.Assignment.AssignmentType.DIRECT;
51+
import static org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism.ALWAYS;
52+
import static org.mapstruct.ap.internal.util.Collections.first;
53+
import static org.mapstruct.ap.internal.util.Collections.last;
54+
5555
/**
5656
* Represents the mapping between a source and target property, e.g. from {@code String Source#foo} to
5757
* {@code int Target#bar}. Name and type of source and target property can differ. If they have different types, the
@@ -197,6 +197,7 @@ public static class PropertyMappingBuilder extends MappingBuilderBase<PropertyMa
197197
private MappingOptions forgeMethodWithMappingOptions;
198198
private boolean forceUpdateMethod;
199199
private boolean forgedNamedBased = true;
200+
private NullValueCheckStrategyPrism nullValueCheckStrategyPrism;
200201

201202
PropertyMappingBuilder() {
202203
super( PropertyMappingBuilder.class );
@@ -253,6 +254,12 @@ public PropertyMappingBuilder forgedNamedBased(boolean forgedNamedBased) {
253254
return this;
254255
}
255256

257+
public PropertyMappingBuilder nullValueCheckStrategyPrism(
258+
NullValueCheckStrategyPrism nullValueCheckStrategyPrism) {
259+
this.nullValueCheckStrategyPrism = nullValueCheckStrategyPrism;
260+
return this;
261+
}
262+
256263
public PropertyMapping build() {
257264
// handle source
258265
this.rightHandSide = getSourceRHS( sourceReference );
@@ -427,11 +434,16 @@ private Assignment assignToPlainViaSetter(Type targetType, Assignment rhs) {
427434
!rhs.isSourceReferenceParameter(), mapNullToDefault );
428435
}
429436
else {
430-
NullValueCheckStrategyPrism nvcs = method.getMapperConfiguration().getNullValueCheckStrategy();
431-
return new SetterWrapper( rhs, method.getThrownTypes(), nvcs, isFieldAssignment(), targetType );
437+
return new SetterWrapper( rhs, method.getThrownTypes(), getNvcs(), isFieldAssignment(), targetType );
432438
}
433439
}
434440

441+
private NullValueCheckStrategyPrism getNvcs() {
442+
BeanMapping beanMapping = method.getMappingOptions().getBeanMapping();
443+
NullValueCheckStrategyPrism nvcsBean = beanMapping != null ? beanMapping.getNullValueCheckStrategy() : null;
444+
return method.getMapperConfiguration().getNullValueCheckStrategy( nvcsBean, nullValueCheckStrategyPrism );
445+
}
446+
435447
private Assignment assignToPlainViaAdder( Assignment rightHandSide) {
436448

437449
Assignment result = rightHandSide;
@@ -461,6 +473,7 @@ private Assignment assignToCollection(Type targetType, TargetWriteAccessorType t
461473
.targetAccessorType( targetAccessorType )
462474
.rightHandSide( rightHandSide )
463475
.assignment( rhs )
476+
.nullValueCheckStrategy( getNvcs() )
464477
.build();
465478
}
466479

processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMapping.java

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import org.mapstruct.ap.internal.prism.BeanMappingPrism;
1515
import org.mapstruct.ap.internal.prism.BuilderPrism;
16+
import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism;
1617
import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism;
1718
import org.mapstruct.ap.internal.prism.ReportingPolicyPrism;
1819
import org.mapstruct.ap.internal.util.FormattingMessager;
@@ -27,6 +28,7 @@ public class BeanMapping {
2728

2829
private final SelectionParameters selectionParameters;
2930
private final NullValueMappingStrategyPrism nullValueMappingStrategy;
31+
private final NullValueCheckStrategyPrism nullValueCheckStrategy;
3032
private final ReportingPolicyPrism reportingPolicy;
3133
private final boolean ignoreByDefault;
3234
private final List<String> ignoreUnmappedSourceProperties;
@@ -42,6 +44,7 @@ public static BeanMapping forInheritance( BeanMapping map ) {
4244
return new BeanMapping(
4345
map.selectionParameters,
4446
map.nullValueMappingStrategy,
47+
map.nullValueCheckStrategy,
4548
map.reportingPolicy,
4649
false,
4750
map.ignoreUnmappedSourceProperties,
@@ -63,6 +66,11 @@ public static BeanMapping fromPrism(BeanMappingPrism beanMapping, ExecutableElem
6366
? null
6467
: NullValueMappingStrategyPrism.valueOf( beanMapping.nullValueMappingStrategy() );
6568

69+
NullValueCheckStrategyPrism nullValueCheckStrategy =
70+
null == beanMapping.values.nullValueCheckStrategy()
71+
? null
72+
: NullValueCheckStrategyPrism.valueOf( beanMapping.nullValueCheckStrategy() );
73+
6674
boolean ignoreByDefault = beanMapping.ignoreByDefault();
6775
BuilderPrism builderMapping = null;
6876
if ( beanMapping.values.builder() != null ) {
@@ -71,7 +79,7 @@ public static BeanMapping fromPrism(BeanMappingPrism beanMapping, ExecutableElem
7179

7280
if ( !resultTypeIsDefined && beanMapping.qualifiedBy().isEmpty() && beanMapping.qualifiedByName().isEmpty()
7381
&& beanMapping.ignoreUnmappedSourceProperties().isEmpty()
74-
&& ( nullValueMappingStrategy == null ) && !ignoreByDefault
82+
&& ( nullValueMappingStrategy == null ) && ( nullValueCheckStrategy == null ) && !ignoreByDefault
7583
&& builderMapping == null ) {
7684

7785
messager.printMessage( method, Message.BEANMAPPING_NO_ELEMENTS );
@@ -88,6 +96,7 @@ public static BeanMapping fromPrism(BeanMappingPrism beanMapping, ExecutableElem
8896
return new BeanMapping(
8997
cmp,
9098
nullValueMappingStrategy,
99+
nullValueCheckStrategy,
91100
null,
92101
ignoreByDefault,
93102
beanMapping.ignoreUnmappedSourceProperties(),
@@ -102,14 +111,24 @@ public static BeanMapping fromPrism(BeanMappingPrism beanMapping, ExecutableElem
102111
* @return bean mapping that needs to be used for Mappings
103112
*/
104113
public static BeanMapping forForgedMethods() {
105-
return new BeanMapping( null, null, ReportingPolicyPrism.IGNORE, false, Collections.<String>emptyList(), null );
114+
return new BeanMapping(
115+
null,
116+
null,
117+
null,
118+
ReportingPolicyPrism.IGNORE,
119+
false,
120+
Collections.<String>emptyList(),
121+
null
122+
);
106123
}
107124

108125
private BeanMapping(SelectionParameters selectionParameters, NullValueMappingStrategyPrism nvms,
126+
NullValueCheckStrategyPrism nvcs,
109127
ReportingPolicyPrism reportingPolicy, boolean ignoreByDefault,
110-
List<String> ignoreUnmappedSourceProperties, BuilderPrism builder) {
128+
List<String> ignoreUnmappedSourceProperties, BuilderPrism builder) {
111129
this.selectionParameters = selectionParameters;
112130
this.nullValueMappingStrategy = nvms;
131+
this.nullValueCheckStrategy = nvcs;
113132
this.reportingPolicy = reportingPolicy;
114133
this.ignoreByDefault = ignoreByDefault;
115134
this.ignoreUnmappedSourceProperties = ignoreUnmappedSourceProperties;
@@ -124,6 +143,10 @@ public NullValueMappingStrategyPrism getNullValueMappingStrategy() {
124143
return nullValueMappingStrategy;
125144
}
126145

146+
public NullValueCheckStrategyPrism getNullValueCheckStrategy() {
147+
return nullValueCheckStrategy;
148+
}
149+
127150
public ReportingPolicyPrism getReportingPolicy() {
128151
return reportingPolicy;
129152
}

0 commit comments

Comments
 (0)