Skip to content

Commit 427f502

Browse files
authored
mapstruct#2195 @Beanmapping#resultType should be used to construct return type also if it's a builder (mapstruct#2196)
1 parent 8b22654 commit 427f502

7 files changed

Lines changed: 173 additions & 17 deletions

File tree

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

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ public static class Builder {
8787

8888
private MappingBuilderContext ctx;
8989
private Method method;
90+
private Type userDefinedReturnType;
9091

9192
/* returnType to construct can have a builder */
9293
private BuilderType returnTypeBuilder;
@@ -108,6 +109,11 @@ public Builder mappingContext(MappingBuilderContext mappingContext) {
108109
return this;
109110
}
110111

112+
public Builder userDefinedReturnType(Type userDefinedReturnType) {
113+
this.userDefinedReturnType = userDefinedReturnType;
114+
return this;
115+
}
116+
111117
public Builder returnTypeBuilder( BuilderType returnTypeBuilder ) {
112118
this.returnTypeBuilder = returnTypeBuilder;
113119
return this;
@@ -148,20 +154,22 @@ public BeanMappingMethod build() {
148154
// determine which return type to construct
149155
boolean cannotConstructReturnType = false;
150156
if ( !method.getReturnType().isVoid() ) {
151-
Type returnTypeImpl = getReturnTypeToConstructFromSelectionParameters( selectionParameters );
152-
if ( returnTypeImpl != null ) {
157+
Type returnTypeImpl = null;
158+
if ( isBuilderRequired() ) {
159+
// the userDefinedReturn type can also require a builder. That buildertype is already set
160+
returnTypeImpl = returnTypeBuilder.getBuilder();
153161
initializeFactoryMethod( returnTypeImpl, selectionParameters );
154-
if ( factoryMethod != null || canResultTypeFromBeanMappingBeConstructed( returnTypeImpl ) ) {
162+
if ( factoryMethod != null || canReturnTypeBeConstructed( returnTypeImpl ) ) {
155163
returnTypeToConstruct = returnTypeImpl;
156164
}
157165
else {
158166
cannotConstructReturnType = true;
159167
}
160168
}
161-
else if ( isBuilderRequired() ) {
162-
returnTypeImpl = returnTypeBuilder.getBuilder();
169+
else if ( userDefinedReturnType != null ) {
170+
returnTypeImpl = userDefinedReturnType;
163171
initializeFactoryMethod( returnTypeImpl, selectionParameters );
164-
if ( factoryMethod != null || canReturnTypeBeConstructed( returnTypeImpl ) ) {
172+
if ( factoryMethod != null || canResultTypeFromBeanMappingBeConstructed( returnTypeImpl ) ) {
165173
returnTypeToConstruct = returnTypeImpl;
166174
}
167175
else {
@@ -482,16 +490,6 @@ private void sortPropertyMappingsByDependencies() {
482490
}
483491
}
484492

485-
private Type getReturnTypeToConstructFromSelectionParameters(SelectionParameters selectionParams) {
486-
// resultType only applies to method that actually has @BeanMapping annotation, never to forged methods
487-
if ( !( method instanceof ForgedMethod )
488-
&& selectionParams != null
489-
&& selectionParams.getResultType() != null ) {
490-
return ctx.getTypeFactory().getType( selectionParams.getResultType() );
491-
}
492-
return null;
493-
}
494-
495493
private boolean canResultTypeFromBeanMappingBeConstructed(Type resultType) {
496494

497495
boolean error = true;

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,11 +367,14 @@ else if ( method.isStreamMapping() ) {
367367
else {
368368
this.messager.note( 1, Message.BEANMAPPING_CREATE_NOTE, method );
369369
BuilderGem builder = method.getOptions().getBeanMapping().getBuilder();
370+
Type userDefinedReturnType = getUserDesiredReturnType( method );
371+
Type builderBaseType = userDefinedReturnType != null ? userDefinedReturnType : method.getReturnType();
370372
BeanMappingMethod.Builder beanMappingBuilder = new BeanMappingMethod.Builder();
371373
BeanMappingMethod beanMappingMethod = beanMappingBuilder
372374
.mappingContext( mappingContext )
373375
.sourceMethod( method )
374-
.returnTypeBuilder( typeFactory.builderTypeFor( method.getReturnType(), builder ) )
376+
.userDefinedReturnType( userDefinedReturnType )
377+
.returnTypeBuilder( typeFactory.builderTypeFor( builderBaseType, builder ) )
375378
.build();
376379

377380
// We can consider that the bean mapping method can always be constructed. If there is a problem
@@ -392,6 +395,14 @@ else if ( method.isStreamMapping() ) {
392395
return mappingMethods;
393396
}
394397

398+
private Type getUserDesiredReturnType(SourceMethod method) {
399+
SelectionParameters selectionParameters = method.getOptions().getBeanMapping().getSelectionParameters();
400+
if ( selectionParameters != null && selectionParameters.getResultType() != null ) {
401+
return typeFactory.getType( selectionParameters.getResultType() );
402+
}
403+
return null;
404+
}
405+
395406
private <M extends ContainerMappingMethod> M createWithElementMappingMethod(SourceMethod method,
396407
MappingMethodOptions mappingMethodOptions, ContainerMappingMethodBuilder<?, M> builder) {
397408

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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._2195;
7+
8+
import org.mapstruct.BeanMapping;
9+
import org.mapstruct.Mapper;
10+
import org.mapstruct.ap.test.bugs._2195.dto.Source;
11+
import org.mapstruct.ap.test.bugs._2195.dto.Target;
12+
import org.mapstruct.ap.test.bugs._2195.dto.TargetBase;
13+
import org.mapstruct.factory.Mappers;
14+
15+
@Mapper
16+
public interface Issue2195Mapper {
17+
18+
Issue2195Mapper INSTANCE = Mappers.getMapper( Issue2195Mapper.class );
19+
20+
@BeanMapping( resultType = Target.class )
21+
TargetBase map(Source source);
22+
23+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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._2195;
7+
8+
import org.junit.Test;
9+
import org.junit.runner.RunWith;
10+
import org.mapstruct.ap.test.bugs._2195.dto.Source;
11+
import org.mapstruct.ap.test.bugs._2195.dto.Target;
12+
import org.mapstruct.ap.test.bugs._2195.dto.TargetBase;
13+
import org.mapstruct.ap.testutil.IssueKey;
14+
import org.mapstruct.ap.testutil.WithClasses;
15+
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
16+
17+
import static org.assertj.core.api.Assertions.assertThat;
18+
19+
@IssueKey("2195")
20+
@WithClasses( { Source.class, Target.class, TargetBase.class } )
21+
@RunWith(AnnotationProcessorTestRunner.class)
22+
public class Issue2195Test {
23+
24+
@Test
25+
@WithClasses( Issue2195Mapper.class )
26+
public void test() {
27+
28+
Source source = new Source();
29+
source.setName( "JohnDoe" );
30+
31+
TargetBase target = Issue2195Mapper.INSTANCE.map( source );
32+
33+
assertThat( target ).isInstanceOf( Target.class );
34+
}
35+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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._2195.dto;
7+
8+
public class Source {
9+
private String name;
10+
11+
public String getName() {
12+
return name;
13+
}
14+
15+
public void setName(String name) {
16+
this.name = name;
17+
}
18+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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._2195.dto;
7+
8+
public class Target extends TargetBase {
9+
10+
protected Target(Builder builder) {
11+
super( builder );
12+
}
13+
14+
public static Builder builder() {
15+
return new Builder();
16+
}
17+
18+
public static class Builder extends TargetBase.Builder {
19+
20+
protected Builder() {
21+
}
22+
23+
public Target build() {
24+
return new Target( this );
25+
}
26+
27+
public Builder name(String name) {
28+
return this;
29+
}
30+
}
31+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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._2195.dto;
7+
8+
public class TargetBase {
9+
10+
private final String name;
11+
12+
protected TargetBase(Builder builder) {
13+
this.name = builder.name;
14+
}
15+
16+
public static Builder builder() {
17+
return new Builder();
18+
}
19+
20+
public String getName() {
21+
return name;
22+
}
23+
24+
public static class Builder {
25+
26+
protected Builder() {
27+
}
28+
29+
private String name;
30+
31+
public TargetBase build() {
32+
return new TargetBase( this );
33+
}
34+
35+
public Builder name(String name) {
36+
this.name = name;
37+
return this;
38+
}
39+
}
40+
}

0 commit comments

Comments
 (0)