Skip to content

Commit ade4f4d

Browse files
authored
mapstruct#1821 nullpointer due to @BeanMapping via inheritance (mapstruct#1822)
1 parent 1c4bbba commit ade4f4d

File tree

7 files changed

+198
-100
lines changed

7 files changed

+198
-100
lines changed

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
import org.mapstruct.ap.internal.model.source.Method;
4343
import org.mapstruct.ap.internal.model.source.SelectionParameters;
4444
import org.mapstruct.ap.internal.model.source.SourceMethod;
45-
import org.mapstruct.ap.internal.prism.BeanMappingPrism;
4645
import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism;
4746
import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism;
4847
import org.mapstruct.ap.internal.prism.ReportingPolicyPrism;
@@ -393,7 +392,7 @@ private boolean canResultTypeFromBeanMappingBeConstructed(Type resultType) {
393392
if ( resultType.isAbstract() ) {
394393
ctx.getMessager().printMessage(
395394
method.getExecutable(),
396-
BeanMappingPrism.getInstanceOn( method.getExecutable() ).mirror,
395+
method.getMappingOptions().getBeanMapping().getMirror(),
397396
BEANMAPPING_ABSTRACT,
398397
resultType,
399398
method.getResultType()
@@ -403,7 +402,7 @@ private boolean canResultTypeFromBeanMappingBeConstructed(Type resultType) {
403402
else if ( !resultType.isAssignableTo( method.getResultType() ) ) {
404403
ctx.getMessager().printMessage(
405404
method.getExecutable(),
406-
BeanMappingPrism.getInstanceOn( method.getExecutable() ).mirror,
405+
method.getMappingOptions().getBeanMapping().getMirror(),
407406
BEANMAPPING_NOT_ASSIGNABLE,
408407
resultType,
409408
method.getResultType()
@@ -413,7 +412,7 @@ else if ( !resultType.isAssignableTo( method.getResultType() ) ) {
413412
else if ( !resultType.hasEmptyAccessibleConstructor() ) {
414413
ctx.getMessager().printMessage(
415414
method.getExecutable(),
416-
BeanMappingPrism.getInstanceOn( method.getExecutable() ).mirror,
415+
method.getMappingOptions().getBeanMapping().getMirror(),
417416
Message.GENERAL_NO_SUITABLE_CONSTRUCTOR,
418417
resultType
419418
);

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

Lines changed: 114 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@
66
package org.mapstruct.ap.internal.model.source;
77

88
import java.util.List;
9+
import java.util.Objects;
910
import java.util.Optional;
11+
import javax.lang.model.element.AnnotationMirror;
1012
import javax.lang.model.element.ExecutableElement;
1113
import javax.lang.model.type.TypeKind;
1214
import javax.lang.model.util.Types;
1315

16+
import org.mapstruct.ap.internal.model.common.TypeFactory;
1417
import org.mapstruct.ap.internal.prism.BeanMappingPrism;
1518
import org.mapstruct.ap.internal.prism.BuilderPrism;
1619
import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism;
@@ -36,87 +39,134 @@ public class BeanMapping {
3639
private final BuilderPrism builder;
3740
private final NullValuePropertyMappingStrategyPrism nullValuePropertyMappingStrategy;
3841

42+
private final AnnotationMirror mirror;
43+
3944
/**
40-
* creates a mapping for inheritance. Will set ignoreByDefault to false.
45+
* creates a mapping for inheritance. Will set
46+
* - ignoreByDefault to false.
47+
* - resultType to null
4148
*
42-
* @param map
43-
* @return
49+
* @return new mapping
4450
*/
45-
public static BeanMapping forInheritance( BeanMapping map ) {
51+
public static BeanMapping forInheritance(BeanMapping beanMapping) {
4652
return new BeanMapping(
47-
map.selectionParameters,
48-
map.nullValueMappingStrategy,
49-
map.nullValuePropertyMappingStrategy,
50-
map.nullValueCheckStrategy,
51-
map.reportingPolicy,
53+
SelectionParameters.forInheritance( beanMapping.selectionParameters ),
54+
beanMapping.nullValueMappingStrategy,
55+
beanMapping.nullValuePropertyMappingStrategy,
56+
beanMapping.nullValueCheckStrategy,
57+
beanMapping.reportingPolicy,
5258
false,
53-
map.ignoreUnmappedSourceProperties,
54-
map.builder
59+
beanMapping.ignoreUnmappedSourceProperties,
60+
beanMapping.builder,
61+
beanMapping.mirror
5562
);
5663
}
5764

58-
public static BeanMapping fromPrism(BeanMappingPrism beanMapping, ExecutableElement method,
59-
FormattingMessager messager, Types typeUtils) {
60-
61-
if ( beanMapping == null ) {
62-
return null;
63-
}
64-
65-
boolean resultTypeIsDefined = !TypeKind.VOID.equals( beanMapping.resultType().getKind() );
65+
public static class Builder {
6666

67-
NullValueMappingStrategyPrism nullValueMappingStrategy =
68-
null == beanMapping.values.nullValueMappingStrategy()
69-
? null
70-
: NullValueMappingStrategyPrism.valueOf( beanMapping.nullValueMappingStrategy() );
67+
private BeanMappingPrism prism;
68+
private ExecutableElement method;
69+
private FormattingMessager messager;
70+
private Types typeUtils;
71+
private TypeFactory typeFactory;
7172

72-
NullValuePropertyMappingStrategyPrism nullValuePropertyMappingStrategy =
73-
null == beanMapping.values.nullValuePropertyMappingStrategy()
74-
? null
75-
: NullValuePropertyMappingStrategyPrism.valueOf( beanMapping.nullValuePropertyMappingStrategy() );
76-
77-
NullValueCheckStrategyPrism nullValueCheckStrategy =
78-
null == beanMapping.values.nullValueCheckStrategy()
79-
? null
80-
: NullValueCheckStrategyPrism.valueOf( beanMapping.nullValueCheckStrategy() );
73+
public Builder beanMappingPrism(BeanMappingPrism beanMappingPrism) {
74+
this.prism = beanMappingPrism;
75+
return this;
76+
}
8177

82-
boolean ignoreByDefault = beanMapping.ignoreByDefault();
83-
BuilderPrism builderMapping = null;
84-
if ( beanMapping.values.builder() != null ) {
85-
builderMapping = beanMapping.builder();
78+
public Builder method(ExecutableElement method) {
79+
this.method = method;
80+
return this;
8681
}
8782

88-
if ( !resultTypeIsDefined && beanMapping.qualifiedBy().isEmpty() && beanMapping.qualifiedByName().isEmpty()
89-
&& beanMapping.ignoreUnmappedSourceProperties().isEmpty()
90-
&& ( nullValueMappingStrategy == null ) && ( nullValuePropertyMappingStrategy == null )
91-
&& ( nullValueCheckStrategy == null ) && !ignoreByDefault && builderMapping == null ) {
83+
public Builder messager(FormattingMessager messager) {
84+
this.messager = messager;
85+
return this;
86+
}
9287

93-
messager.printMessage( method, Message.BEANMAPPING_NO_ELEMENTS );
88+
public Builder typeUtils(Types typeUtils) {
89+
this.typeUtils = typeUtils;
90+
return this;
9491
}
9592

96-
SelectionParameters cmp = new SelectionParameters(
97-
beanMapping.qualifiedBy(),
98-
beanMapping.qualifiedByName(),
99-
resultTypeIsDefined ? beanMapping.resultType() : null,
100-
typeUtils
101-
);
93+
public Builder typeFactory(TypeFactory typeFactory) {
94+
this.typeFactory = typeFactory;
95+
return this;
96+
}
10297

103-
//TODO Do we want to add the reporting policy to the BeanMapping as well? To give more granular support?
104-
return new BeanMapping(
105-
cmp,
106-
nullValueMappingStrategy,
107-
nullValuePropertyMappingStrategy,
108-
nullValueCheckStrategy,
109-
null,
110-
ignoreByDefault,
111-
beanMapping.ignoreUnmappedSourceProperties(),
112-
builderMapping
113-
);
98+
public BeanMapping build() {
99+
100+
if ( prism == null ) {
101+
return null;
102+
}
103+
104+
Objects.requireNonNull( method );
105+
Objects.requireNonNull( messager );
106+
Objects.requireNonNull( method );
107+
Objects.requireNonNull( typeUtils );
108+
Objects.requireNonNull( typeFactory );
109+
110+
boolean resultTypeIsDefined = !TypeKind.VOID.equals( prism.resultType().getKind() );
111+
112+
NullValueMappingStrategyPrism nullValueMappingStrategy =
113+
null == prism.values.nullValueMappingStrategy()
114+
? null
115+
: NullValueMappingStrategyPrism.valueOf( prism.nullValueMappingStrategy() );
116+
117+
NullValuePropertyMappingStrategyPrism nullValuePropertyMappingStrategy =
118+
null == prism.values.nullValuePropertyMappingStrategy()
119+
? null
120+
:
121+
NullValuePropertyMappingStrategyPrism.valueOf( prism.nullValuePropertyMappingStrategy() );
122+
123+
NullValueCheckStrategyPrism nullValueCheckStrategy =
124+
null == prism.values.nullValueCheckStrategy()
125+
? null
126+
: NullValueCheckStrategyPrism.valueOf( prism.nullValueCheckStrategy() );
127+
128+
boolean ignoreByDefault = prism.ignoreByDefault();
129+
BuilderPrism builderMapping = null;
130+
if ( prism.values.builder() != null ) {
131+
builderMapping = prism.builder();
132+
}
133+
134+
if ( !resultTypeIsDefined && prism.qualifiedBy().isEmpty() &&
135+
prism.qualifiedByName().isEmpty()
136+
&& prism.ignoreUnmappedSourceProperties().isEmpty()
137+
&& ( nullValueMappingStrategy == null ) && ( nullValuePropertyMappingStrategy == null )
138+
&& ( nullValueCheckStrategy == null ) && !ignoreByDefault && builderMapping == null ) {
139+
140+
messager.printMessage( method, Message.BEANMAPPING_NO_ELEMENTS );
141+
}
142+
143+
SelectionParameters cmp = new SelectionParameters(
144+
prism.qualifiedBy(),
145+
prism.qualifiedByName(),
146+
resultTypeIsDefined ? prism.resultType() : null,
147+
typeUtils
148+
);
149+
150+
//TODO Do we want to add the reporting policy to the BeanMapping as well? To give more granular support?
151+
return new BeanMapping(
152+
cmp,
153+
nullValueMappingStrategy,
154+
nullValuePropertyMappingStrategy,
155+
nullValueCheckStrategy,
156+
null,
157+
ignoreByDefault,
158+
prism.ignoreUnmappedSourceProperties(),
159+
builderMapping,
160+
prism.mirror
161+
);
162+
}
114163
}
115164

116165
private BeanMapping(SelectionParameters selectionParameters, NullValueMappingStrategyPrism nvms,
117166
NullValuePropertyMappingStrategyPrism nvpms, NullValueCheckStrategyPrism nvcs,
118167
ReportingPolicyPrism reportingPolicy, boolean ignoreByDefault,
119-
List<String> ignoreUnmappedSourceProperties, BuilderPrism builder) {
168+
List<String> ignoreUnmappedSourceProperties, BuilderPrism builder,
169+
AnnotationMirror mirror) {
120170
this.selectionParameters = selectionParameters;
121171
this.nullValueMappingStrategy = nvms;
122172
this.nullValuePropertyMappingStrategy = nvpms;
@@ -125,6 +175,7 @@ private BeanMapping(SelectionParameters selectionParameters, NullValueMappingStr
125175
this.ignoreByDefault = ignoreByDefault;
126176
this.ignoreUnmappedSourceProperties = ignoreUnmappedSourceProperties;
127177
this.builder = builder;
178+
this.mirror = mirror;
128179
}
129180

130181
public SelectionParameters getSelectionParameters() {
@@ -155,6 +206,10 @@ public List<String> getIgnoreUnmappedSourceProperties() {
155206
return ignoreUnmappedSourceProperties;
156207
}
157208

209+
public AnnotationMirror getMirror() {
210+
return mirror;
211+
}
212+
158213
/**
159214
* derives the builder prism given the options and configuration
160215
* @param method containing mandatory configuration and the mapping options (optionally containing a beanmapping)

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

Lines changed: 0 additions & 36 deletions
This file was deleted.

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,23 @@ public class SelectionParameters {
2626
private final Types typeUtils;
2727
private final SourceRHS sourceRHS;
2828

29+
/**
30+
* Returns new selection parameters
31+
*
32+
* ResultType is not inherited.
33+
*
34+
* @param selectionParameters
35+
* @return
36+
*/
37+
public static SelectionParameters forInheritance(SelectionParameters selectionParameters) {
38+
return new SelectionParameters(
39+
selectionParameters.qualifiers,
40+
selectionParameters.qualifyingNames,
41+
null,
42+
selectionParameters.typeUtils
43+
);
44+
}
45+
2946
public SelectionParameters(List<TypeMirror> qualifiers, List<String> qualifyingNames, TypeMirror resultType,
3047
Types typeUtils) {
3148
this( qualifiers, qualifyingNames, resultType, typeUtils, null );

processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,14 @@ private SourceMethod getMethodRequiringImplementation(ExecutableType methodType,
254254
.setMapMapping(
255255
MapMapping.fromPrism( MapMappingPrism.getInstanceOn( method ), method, messager, typeUtils ) )
256256
.setBeanMapping(
257-
BeanMapping.fromPrism( BeanMappingPrism.getInstanceOn( method ), method, messager, typeUtils ) )
257+
new BeanMapping.Builder()
258+
.beanMappingPrism( BeanMappingPrism.getInstanceOn( method ) )
259+
.messager( messager )
260+
.method( method )
261+
.typeUtils( typeUtils )
262+
.typeFactory( typeFactory )
263+
.build()
264+
)
258265
.setValueMappings( getValueMappings( method ) )
259266
.setTypeUtils( typeUtils )
260267
.setTypeFactory( typeFactory )
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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._1821;
7+
8+
import org.mapstruct.BeanMapping;
9+
import org.mapstruct.InheritConfiguration;
10+
import org.mapstruct.Mapper;
11+
import org.mapstruct.factory.Mappers;
12+
13+
@Mapper
14+
public interface Issue1821Mapper {
15+
16+
Issue1821Mapper INSTANCE = Mappers.getMapper( Issue1821Mapper.class );
17+
18+
@BeanMapping( resultType = Target.class )
19+
Target map(Source source);
20+
21+
@InheritConfiguration( name = "map" )
22+
ExtendedTarget mapExtended(Source source);
23+
24+
class Target {
25+
}
26+
27+
class ExtendedTarget extends Target {
28+
}
29+
30+
class Source {
31+
}
32+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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._1821;
7+
8+
import org.junit.Test;
9+
import org.junit.runner.RunWith;
10+
import org.mapstruct.ap.testutil.IssueKey;
11+
import org.mapstruct.ap.testutil.WithClasses;
12+
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
13+
14+
@IssueKey("1797")
15+
@RunWith( AnnotationProcessorTestRunner.class)
16+
@WithClasses( Issue1821Mapper.class )
17+
public class Issue1821Test {
18+
19+
@Test
20+
public void shouldNotGiveNullPtr() {
21+
Issue1821Mapper.INSTANCE.map( new Issue1821Mapper.Source() );
22+
}
23+
24+
}

0 commit comments

Comments
 (0)