Skip to content

Commit e4bc1cd

Browse files
authored
mapstruct#3884 Ensure NullValuePropertyMappingStrategy.SET_TO_DEFAULT initializes empty collection/map when target is null
Signed-off-by: TangYang <tangyang9464@163.com>
1 parent c90c936 commit e4bc1cd

7 files changed

Lines changed: 248 additions & 0 deletions

File tree

processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
<@lib.handleLocalVarNullCheck needs_explicit_local_var=directAssignment>
3131
${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if directAssignment><@wrapLocalVarInCollectionInitializer/><#else><@lib.handleWithAssignmentOrNullCheckVar/></#if></@lib.handleWrite>;
3232
</@lib.handleLocalVarNullCheck>
33+
<#if !ext.defaultValueAssignment?? && !sourcePresenceCheckerReference?? && mapNullToDefault>else {
34+
${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><@lib.initTargetObject/></@lib.handleWrite>;
35+
}
36+
</#if>
3337
</#macro>
3438
<#--
3539
wraps the local variable in a collection initializer (new collection, or EnumSet.copyOf)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.test.bugs._3884;
7+
8+
import java.util.List;
9+
import java.util.Map;
10+
11+
/**
12+
* Destination type interface for testing null value property mapping strategy with Map properties.
13+
*/
14+
public interface DestinationType {
15+
Map<String, String> getAttributes();
16+
17+
void setAttributes(Map<String, String> attributes);
18+
19+
List<String> getItems();
20+
21+
void setItems(List<String> items);
22+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.test.bugs._3884;
7+
8+
import org.mapstruct.Mapper;
9+
import org.mapstruct.MappingTarget;
10+
import org.mapstruct.NullValuePropertyMappingStrategy;
11+
import org.mapstruct.factory.Mappers;
12+
13+
/**
14+
* Mapper for testing null value property mapping strategy with Map properties.
15+
*/
16+
@Mapper(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT)
17+
public interface Issue3884Mapper {
18+
Issue3884Mapper INSTANCE = Mappers.getMapper( Issue3884Mapper.class );
19+
20+
void update(@MappingTarget DestinationType destination, SourceType source);
21+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.test.bugs._3884;
7+
8+
import java.util.ArrayList;
9+
import java.util.HashMap;
10+
import java.util.List;
11+
import java.util.Map;
12+
13+
import org.mapstruct.ap.testutil.IssueKey;
14+
import org.mapstruct.ap.testutil.ProcessorTest;
15+
import org.mapstruct.ap.testutil.WithClasses;
16+
17+
import static org.assertj.core.api.Assertions.assertThat;
18+
import static org.assertj.core.api.Assertions.entry;
19+
20+
/**
21+
* Test for issue 3884: NullValuePropertyMappingStrategy.SET_TO_DEFAULT should set target Map/Collection to default
22+
* when source and target are all null.
23+
*/
24+
@IssueKey("3884")
25+
@WithClasses({
26+
DestinationType.class,
27+
SourceType.class,
28+
Issue3884Mapper.class
29+
})
30+
public class Issue3884Test {
31+
32+
@ProcessorTest
33+
public void shouldSetTargetToDefaultWhenBothSourceAndTargetAreNull() {
34+
DestinationType target = new SourceType();
35+
SourceType source = new SourceType();
36+
37+
assertThat( source.getAttributes() ).isNull();
38+
assertThat( target.getAttributes() ).isNull();
39+
assertThat( source.getItems() ).isNull();
40+
assertThat( target.getItems() ).isNull();
41+
42+
Issue3884Mapper.INSTANCE.update( target, source );
43+
44+
assertThat( target.getAttributes() ).isEmpty();
45+
assertThat( target.getItems() ).isEmpty();
46+
}
47+
48+
@ProcessorTest
49+
public void shouldClearTargetWhenSourceIsNullAndTargetIsInitialized() {
50+
DestinationType target = new SourceType();
51+
SourceType source = new SourceType();
52+
53+
Map<String, String> targetAttributes = new HashMap<>();
54+
targetAttributes.put( "targetKey", "targetValue" );
55+
target.setAttributes( targetAttributes );
56+
57+
List<String> targetItems = new ArrayList<>();
58+
targetItems.add( "targetItem" );
59+
target.setItems( targetItems );
60+
61+
assertThat( source.getAttributes() ).isNull();
62+
assertThat( target.getAttributes() ).isNotEmpty();
63+
assertThat( source.getItems() ).isNull();
64+
assertThat( target.getItems() ).isNotEmpty();
65+
66+
Issue3884Mapper.INSTANCE.update( target, source );
67+
68+
assertThat( target.getAttributes() ).isEmpty();
69+
assertThat( target.getItems() ).isEmpty();
70+
}
71+
72+
@ProcessorTest
73+
public void shouldCopySourceToTargetWhenSourceIsInitializedAndTargetIsNull() {
74+
DestinationType target = new SourceType();
75+
SourceType source = new SourceType();
76+
77+
source.setAttributes( Map.of( "sourceKey", "sourceValue" ) );
78+
source.setItems( List.of( "sourceItem" ) );
79+
80+
assertThat( source.getAttributes() ).isNotEmpty();
81+
assertThat( target.getAttributes() ).isNull();
82+
assertThat( source.getItems() ).isNotEmpty();
83+
assertThat( target.getItems() ).isNull();
84+
85+
Issue3884Mapper.INSTANCE.update( target, source );
86+
87+
assertThat( target.getAttributes() ).containsOnly( entry( "sourceKey", "sourceValue" ) );
88+
assertThat( target.getItems() ).containsExactly( "sourceItem" );
89+
}
90+
91+
@ProcessorTest
92+
public void shouldCopySourceToTargetWhenBothSourceAndTargetAreInitialized() {
93+
DestinationType target = new SourceType();
94+
SourceType source = new SourceType();
95+
96+
source.setAttributes( Map.of( "sourceKey", "sourceValue" ) );
97+
source.setItems( List.of( "sourceItem" ) );
98+
99+
Map<String, String> targetAttributes = new HashMap<>();
100+
targetAttributes.put( "targetKey", "targetValue" );
101+
target.setAttributes( targetAttributes );
102+
List<String> targetItems = new ArrayList<>();
103+
targetItems.add( "targetItem" );
104+
target.setItems( targetItems );
105+
106+
assertThat( source.getAttributes() ).isNotEmpty();
107+
assertThat( target.getAttributes() ).isNotEmpty();
108+
assertThat( source.getItems() ).isNotEmpty();
109+
assertThat( target.getItems() ).isNotEmpty();
110+
111+
Issue3884Mapper.INSTANCE.update( target, source );
112+
113+
assertThat( target.getAttributes() ).containsOnly( entry( "sourceKey", "sourceValue" ) );
114+
assertThat( target.getItems() ).containsExactly( "sourceItem" );
115+
}
116+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.test.bugs._3884;
7+
8+
import java.util.List;
9+
import java.util.Map;
10+
11+
/**
12+
* Source type class implementing DestinationType for testing null value property mapping strategy with Map properties.
13+
*/
14+
public class SourceType implements DestinationType {
15+
private Map<String, String> attributes;
16+
private List<String> items;
17+
18+
@Override
19+
public Map<String, String> getAttributes() {
20+
return attributes;
21+
}
22+
23+
@Override
24+
public void setAttributes(Map<String, String> attributes) {
25+
this.attributes = attributes;
26+
}
27+
28+
@Override
29+
public List<String> getItems() {
30+
return items;
31+
}
32+
33+
@Override
34+
public void setItems(List<String> items) {
35+
this.items = items;
36+
}
37+
}

processor/src/test/resources/fixtures/21/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapperImpl.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ public void update(Dto source, Domain target) {
6767
if ( list != null ) {
6868
target.setStrings( new LinkedHashSet<String>( list ) );
6969
}
70+
else {
71+
target.setStrings( new LinkedHashSet<String>() );
72+
}
7073
}
7174
if ( target.getLongs() != null ) {
7275
Set<Long> set = stringListToLongSet( source.getStrings() );
@@ -83,6 +86,9 @@ public void update(Dto source, Domain target) {
8386
if ( set != null ) {
8487
target.setLongs( set );
8588
}
89+
else {
90+
target.setLongs( new LinkedHashSet<Long>() );
91+
}
8692
}
8793
if ( target.getStringsInitialized() != null ) {
8894
List<String> list1 = source.getStringsInitialized();
@@ -99,6 +105,9 @@ public void update(Dto source, Domain target) {
99105
if ( list1 != null ) {
100106
target.setStringsInitialized( new LinkedHashSet<String>( list1 ) );
101107
}
108+
else {
109+
target.setStringsInitialized( new LinkedHashSet<String>() );
110+
}
102111
}
103112
if ( target.getLongsInitialized() != null ) {
104113
Set<Long> set1 = stringListToLongSet( source.getStringsInitialized() );
@@ -115,6 +124,9 @@ public void update(Dto source, Domain target) {
115124
if ( set1 != null ) {
116125
target.setLongsInitialized( set1 );
117126
}
127+
else {
128+
target.setLongsInitialized( new LinkedHashSet<Long>() );
129+
}
118130
}
119131
if ( target.getStringsWithDefault() != null ) {
120132
List<String> list2 = source.getStringsWithDefault();
@@ -157,6 +169,9 @@ public Domain updateWithReturn(Dto source, Domain target) {
157169
if ( list != null ) {
158170
target.setStrings( new LinkedHashSet<String>( list ) );
159171
}
172+
else {
173+
target.setStrings( new LinkedHashSet<String>() );
174+
}
160175
}
161176
if ( target.getLongs() != null ) {
162177
Set<Long> set = stringListToLongSet( source.getStrings() );
@@ -173,6 +188,9 @@ public Domain updateWithReturn(Dto source, Domain target) {
173188
if ( set != null ) {
174189
target.setLongs( set );
175190
}
191+
else {
192+
target.setLongs( new LinkedHashSet<Long>() );
193+
}
176194
}
177195
if ( target.getStringsInitialized() != null ) {
178196
List<String> list1 = source.getStringsInitialized();
@@ -189,6 +207,9 @@ public Domain updateWithReturn(Dto source, Domain target) {
189207
if ( list1 != null ) {
190208
target.setStringsInitialized( new LinkedHashSet<String>( list1 ) );
191209
}
210+
else {
211+
target.setStringsInitialized( new LinkedHashSet<String>() );
212+
}
192213
}
193214
if ( target.getLongsInitialized() != null ) {
194215
Set<Long> set1 = stringListToLongSet( source.getStringsInitialized() );
@@ -205,6 +226,9 @@ public Domain updateWithReturn(Dto source, Domain target) {
205226
if ( set1 != null ) {
206227
target.setLongsInitialized( set1 );
207228
}
229+
else {
230+
target.setLongsInitialized( new LinkedHashSet<Long>() );
231+
}
208232
}
209233
if ( target.getStringsWithDefault() != null ) {
210234
List<String> list2 = source.getStringsWithDefault();

processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapperImpl.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ public void update(Dto source, Domain target) {
6767
if ( list != null ) {
6868
target.setStrings( new LinkedHashSet<String>( list ) );
6969
}
70+
else {
71+
target.setStrings( new LinkedHashSet<String>() );
72+
}
7073
}
7174
if ( target.getLongs() != null ) {
7275
Set<Long> set = stringListToLongSet( source.getStrings() );
@@ -83,6 +86,9 @@ public void update(Dto source, Domain target) {
8386
if ( set != null ) {
8487
target.setLongs( set );
8588
}
89+
else {
90+
target.setLongs( new LinkedHashSet<Long>() );
91+
}
8692
}
8793
if ( target.getStringsInitialized() != null ) {
8894
List<String> list1 = source.getStringsInitialized();
@@ -99,6 +105,9 @@ public void update(Dto source, Domain target) {
99105
if ( list1 != null ) {
100106
target.setStringsInitialized( new LinkedHashSet<String>( list1 ) );
101107
}
108+
else {
109+
target.setStringsInitialized( new LinkedHashSet<String>() );
110+
}
102111
}
103112
if ( target.getLongsInitialized() != null ) {
104113
Set<Long> set1 = stringListToLongSet( source.getStringsInitialized() );
@@ -115,6 +124,9 @@ public void update(Dto source, Domain target) {
115124
if ( set1 != null ) {
116125
target.setLongsInitialized( set1 );
117126
}
127+
else {
128+
target.setLongsInitialized( new LinkedHashSet<Long>() );
129+
}
118130
}
119131
if ( target.getStringsWithDefault() != null ) {
120132
List<String> list2 = source.getStringsWithDefault();
@@ -157,6 +169,9 @@ public Domain updateWithReturn(Dto source, Domain target) {
157169
if ( list != null ) {
158170
target.setStrings( new LinkedHashSet<String>( list ) );
159171
}
172+
else {
173+
target.setStrings( new LinkedHashSet<String>() );
174+
}
160175
}
161176
if ( target.getLongs() != null ) {
162177
Set<Long> set = stringListToLongSet( source.getStrings() );
@@ -173,6 +188,9 @@ public Domain updateWithReturn(Dto source, Domain target) {
173188
if ( set != null ) {
174189
target.setLongs( set );
175190
}
191+
else {
192+
target.setLongs( new LinkedHashSet<Long>() );
193+
}
176194
}
177195
if ( target.getStringsInitialized() != null ) {
178196
List<String> list1 = source.getStringsInitialized();
@@ -189,6 +207,9 @@ public Domain updateWithReturn(Dto source, Domain target) {
189207
if ( list1 != null ) {
190208
target.setStringsInitialized( new LinkedHashSet<String>( list1 ) );
191209
}
210+
else {
211+
target.setStringsInitialized( new LinkedHashSet<String>() );
212+
}
192213
}
193214
if ( target.getLongsInitialized() != null ) {
194215
Set<Long> set1 = stringListToLongSet( source.getStringsInitialized() );
@@ -205,6 +226,9 @@ public Domain updateWithReturn(Dto source, Domain target) {
205226
if ( set1 != null ) {
206227
target.setLongsInitialized( set1 );
207228
}
229+
else {
230+
target.setLongsInitialized( new LinkedHashSet<Long>() );
231+
}
208232
}
209233
if ( target.getStringsWithDefault() != null ) {
210234
List<String> list2 = source.getStringsWithDefault();

0 commit comments

Comments
 (0)