Skip to content

Commit 3371058

Browse files
authored
mapstruct#1742 & mapstruct#1661 refactoring and making builder optional (mapstruct#1811)
1 parent 60c159a commit 3371058

47 files changed

Lines changed: 912 additions & 530 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,12 @@
2929
* @return the method that needs to tbe invoked on the builder
3030
*/
3131
String buildMethod() default "build";
32+
33+
/**
34+
* Toggling builders on / off. Builders are sometimes used solely for unit testing (fluent testdata)
35+
* MapStruct will need to use the regular getters /setters in that case.
36+
*
37+
* @return when true, no builder patterns will be applied
38+
*/
39+
boolean disableBuilder() default false;
3240
}

documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,11 @@ In case of a `MoreThanOneBuilderCreationMethodException` MapStruct will write a
684684
If such type is found then MapStruct will use that type to perform the mapping to (i.e. it will look for setters into that type).
685685
To finish the mapping MapStruct generates code that will invoke the build method of the builder.
686686

687+
[NOTE]
688+
======
689+
Builder detection can be switched off by means of `@Builder#disableBuilder`. MapStruct will fall back on regular getters / setters in case builders are disabled.
690+
======
691+
687692
[NOTE]
688693
======
689694
The <<object-factories>> are also considered for the builder type.

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import javax.lang.model.element.AnnotationMirror;
99
import org.mapstruct.ap.internal.model.common.Assignment;
10+
import org.mapstruct.ap.internal.model.common.BuilderType;
1011
import org.mapstruct.ap.internal.model.common.ParameterBinding;
1112
import org.mapstruct.ap.internal.model.common.SourceRHS;
1213
import org.mapstruct.ap.internal.model.common.Type;
@@ -73,7 +74,7 @@ private boolean isDisableSubMappingMethodsGeneration() {
7374
*
7475
* @return See above
7576
*/
76-
Assignment createForgedAssignment(SourceRHS sourceRHS, ForgedMethod forgedMethod) {
77+
Assignment createForgedAssignment(SourceRHS sourceRHS, BuilderType builderType, ForgedMethod forgedMethod) {
7778

7879
if ( ctx.getForgedMethodsUnderCreation().containsKey( forgedMethod ) ) {
7980
return createAssignment( sourceRHS, ctx.getForgedMethodsUnderCreation().get( forgedMethod ) );
@@ -93,6 +94,7 @@ Assignment createForgedAssignment(SourceRHS sourceRHS, ForgedMethod forgedMethod
9394
else {
9495
forgedMappingMethod = new BeanMappingMethod.Builder()
9596
.forgedMethod( forgedMethod )
97+
.returnTypeBuilder( builderType )
9698
.mappingContext( ctx )
9799
.build();
98100
}

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import org.mapstruct.ap.internal.model.common.Assignment;
99
import org.mapstruct.ap.internal.model.common.SourceRHS;
1010
import org.mapstruct.ap.internal.model.common.Type;
11+
import org.mapstruct.ap.internal.model.source.BeanMapping;
1112
import org.mapstruct.ap.internal.model.source.ForgedMethod;
1213
import org.mapstruct.ap.internal.model.source.ForgedMethodHistory;
1314
import org.mapstruct.ap.internal.util.Strings;
@@ -63,7 +64,12 @@ Assignment forgeMapping(SourceRHS sourceRHS, Type sourceType, Type targetType) {
6364
true
6465
);
6566

66-
return createForgedAssignment( sourceRHS, forgedMethod );
67+
return createForgedAssignment(
68+
sourceRHS,
69+
ctx.getTypeFactory()
70+
.builderTypeFor( targetType, BeanMapping.builderPrismFor( method ).orElse( null ) ),
71+
forgedMethod
72+
);
6773
}
6874

6975
private String getName(Type sourceType, Type targetType) {

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

Lines changed: 188 additions & 121 deletions
Large diffs are not rendered by default.

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

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

88
import java.util.Collection;
9+
import java.util.Optional;
910
import javax.lang.model.element.ExecutableElement;
1011

1112
import org.mapstruct.ap.internal.model.common.BuilderType;
1213
import org.mapstruct.ap.internal.model.source.BeanMapping;
1314
import org.mapstruct.ap.internal.model.source.Method;
1415
import org.mapstruct.ap.internal.prism.BuilderPrism;
15-
import org.mapstruct.ap.internal.util.MapperConfiguration;
1616
import org.mapstruct.ap.internal.util.Message;
1717
import org.mapstruct.ap.internal.util.Strings;
1818

@@ -36,14 +36,14 @@ public static MethodReference getBuilderFinisherMethod(Method method, BuilderTyp
3636
return null;
3737
}
3838

39-
BuilderPrism builderMapping = builderMappingPrism( method, ctx );
40-
if ( builderMapping == null && buildMethods.size() == 1 ) {
39+
Optional<BuilderPrism> builderMapping = BeanMapping.builderPrismFor( method );
40+
if ( !builderMapping.isPresent() && buildMethods.size() == 1 ) {
4141
return MethodReference.forMethodCall( first( buildMethods ).getSimpleName().toString() );
4242
}
4343
else {
4444
String buildMethodPattern = DEFAULT_BUILD_METHOD_NAME;
45-
if ( builderMapping != null ) {
46-
buildMethodPattern = builderMapping.buildMethod();
45+
if ( builderMapping.isPresent() ) {
46+
buildMethodPattern = builderMapping.get().buildMethod();
4747
}
4848
for ( ExecutableElement buildMethod : buildMethods ) {
4949
String methodName = buildMethod.getSimpleName().toString();
@@ -52,7 +52,7 @@ public static MethodReference getBuilderFinisherMethod(Method method, BuilderTyp
5252
}
5353
}
5454

55-
if ( builderMapping == null ) {
55+
if ( !builderMapping.isPresent() ) {
5656
ctx.getMessager().printMessage(
5757
method.getExecutable(),
5858
Message.BUILDER_NO_BUILD_METHOD_FOUND_DEFAULT,
@@ -65,7 +65,7 @@ public static MethodReference getBuilderFinisherMethod(Method method, BuilderTyp
6565
else {
6666
ctx.getMessager().printMessage(
6767
method.getExecutable(),
68-
builderMapping.mirror,
68+
builderMapping.get().mirror,
6969
Message.BUILDER_NO_BUILD_METHOD_FOUND,
7070
buildMethodPattern,
7171
builderType.getBuilder(),
@@ -78,11 +78,4 @@ public static MethodReference getBuilderFinisherMethod(Method method, BuilderTyp
7878
return null;
7979
}
8080

81-
private static BuilderPrism builderMappingPrism(Method method, MappingBuilderContext ctx) {
82-
BeanMapping beanMapping = method.getMappingOptions().getBeanMapping();
83-
if ( beanMapping != null && beanMapping.getBuilder() != null ) {
84-
return beanMapping.getBuilder();
85-
}
86-
return MapperConfiguration.getInstanceOn( ctx.getMapperTypeElement() ).getBuilderPrism();
87-
}
8881
}

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism;
2121
import org.mapstruct.ap.internal.util.Message;
2222
import org.mapstruct.ap.internal.util.accessor.Accessor;
23+
import org.mapstruct.ap.internal.util.accessor.AccessorType;
2324

2425
import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.SET_TO_DEFAULT;
2526
import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.SET_TO_NULL;
@@ -57,7 +58,7 @@ public class CollectionAssignmentBuilder {
5758
private Accessor targetReadAccessor;
5859
private Type targetType;
5960
private String targetPropertyName;
60-
private PropertyMapping.TargetWriteAccessorType targetAccessorType;
61+
private AccessorType targetAccessorType;
6162
private Assignment assignment;
6263
private SourceRHS sourceRHS;
6364
private NullValueCheckStrategyPrism nvcs;
@@ -88,7 +89,7 @@ public CollectionAssignmentBuilder targetPropertyName(String targetPropertyName)
8889
return this;
8990
}
9091

91-
public CollectionAssignmentBuilder targetAccessorType(PropertyMapping.TargetWriteAccessorType targetAccessorType) {
92+
public CollectionAssignmentBuilder targetAccessorType(AccessorType targetAccessorType) {
9293
this.targetAccessorType = targetAccessorType;
9394
return this;
9495
}
@@ -129,8 +130,7 @@ public Assignment build() {
129130
CollectionMappingStrategyPrism cms = method.getMapperConfiguration().getCollectionMappingStrategy();
130131
boolean targetImmutable = cms == CollectionMappingStrategyPrism.TARGET_IMMUTABLE || targetReadAccessor == null;
131132

132-
if ( targetAccessorType == PropertyMapping.TargetWriteAccessorType.SETTER ||
133-
targetAccessorType == PropertyMapping.TargetWriteAccessorType.FIELD ) {
133+
if ( targetAccessorType == AccessorType.SETTER || targetAccessorType == AccessorType.FIELD ) {
134134

135135
if ( result.isCallingUpdateMethod() && !targetImmutable ) {
136136

@@ -149,7 +149,7 @@ public Assignment build() {
149149
result,
150150
method.getThrownTypes(),
151151
factoryMethod,
152-
PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType ),
152+
targetAccessorType == AccessorType.FIELD,
153153
targetType,
154154
true,
155155
nvpms == SET_TO_NULL && !targetType.isPrimitive(),
@@ -165,7 +165,7 @@ else if ( method.isUpdateMethod() && !targetImmutable ) {
165165
nvcs,
166166
nvpms,
167167
ctx.getTypeFactory(),
168-
PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType )
168+
targetAccessorType == AccessorType.FIELD
169169
);
170170
}
171171
else if ( result.getType() == Assignment.AssignmentType.DIRECT ||
@@ -176,7 +176,7 @@ else if ( result.getType() == Assignment.AssignmentType.DIRECT ||
176176
method.getThrownTypes(),
177177
targetType,
178178
ctx.getTypeFactory(),
179-
PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType )
179+
targetAccessorType == AccessorType.FIELD
180180
);
181181
}
182182
else {
@@ -185,7 +185,7 @@ else if ( result.getType() == Assignment.AssignmentType.DIRECT ||
185185
result,
186186
method.getThrownTypes(),
187187
targetType,
188-
PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType )
188+
targetAccessorType == AccessorType.FIELD
189189
);
190190
}
191191
}
@@ -203,7 +203,7 @@ else if ( result.getType() == Assignment.AssignmentType.DIRECT ||
203203
result,
204204
method.getThrownTypes(),
205205
targetType,
206-
PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType )
206+
targetAccessorType == AccessorType.FIELD
207207
);
208208
}
209209

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ public final M build() {
139139

140140
MethodReference factoryMethod = null;
141141
if ( !method.isUpdateMethod() ) {
142-
factoryMethod = ObjectFactoryMethodResolver.getFactoryMethod( method, method.getResultType(), null, ctx );
142+
factoryMethod = ObjectFactoryMethodResolver.getFactoryMethod( method, null, ctx );
143143
}
144144

145145
Set<String> existingVariables = new HashSet<>( method.getParameterNames() );

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import java.util.Collections;
1111
import java.util.List;
1212
import java.util.Set;
13-
1413
import javax.lang.model.element.ExecutableElement;
1514

1615
import org.mapstruct.ap.internal.model.common.Accessibility;

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

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,60 @@ private LifecycleMethodResolver() {
3232

3333
/**
3434
* @param method the method to obtain the beforeMapping methods for
35+
* @param alternativeTarget alternative to {@link Method#getResultType()} e.g. when target is abstract
3536
* @param selectionParameters method selectionParameters
3637
* @param ctx the builder context
3738
* @param existingVariableNames the existing variable names in the mapping method
3839
* @return all applicable {@code @BeforeMapping} methods for the given method
3940
*/
4041
public static List<LifecycleCallbackMethodReference> beforeMappingMethods(Method method,
42+
Type alternativeTarget,
4143
SelectionParameters selectionParameters,
4244
MappingBuilderContext ctx,
4345
Set<String> existingVariableNames) {
4446
return collectLifecycleCallbackMethods( method,
47+
alternativeTarget,
48+
selectionParameters,
49+
filterBeforeMappingMethods( getAllAvailableMethods( method, ctx.getSourceModel() ) ),
50+
ctx,
51+
existingVariableNames );
52+
}
53+
54+
/**
55+
* @param method the method to obtain the afterMapping methods for
56+
* @param alternativeTarget alternative to {@link Method#getResultType()} e.g. when target is abstract
57+
* @param selectionParameters method selectionParameters
58+
* @param ctx the builder context
59+
* @param existingVariableNames list of already used variable names
60+
* @return all applicable {@code @AfterMapping} methods for the given method
61+
*/
62+
public static List<LifecycleCallbackMethodReference> afterMappingMethods(Method method,
63+
Type alternativeTarget,
64+
SelectionParameters selectionParameters,
65+
MappingBuilderContext ctx,
66+
Set<String> existingVariableNames) {
67+
return collectLifecycleCallbackMethods( method,
68+
alternativeTarget,
69+
selectionParameters,
70+
filterAfterMappingMethods( getAllAvailableMethods( method, ctx.getSourceModel() ) ),
71+
ctx,
72+
existingVariableNames );
73+
}
74+
75+
76+
/**
77+
* @param method the method to obtain the beforeMapping methods for
78+
* @param selectionParameters method selectionParameters
79+
* @param ctx the builder context
80+
* @param existingVariableNames the existing variable names in the mapping method
81+
* @return all applicable {@code @BeforeMapping} methods for the given method
82+
*/
83+
public static List<LifecycleCallbackMethodReference> beforeMappingMethods(Method method,
84+
SelectionParameters selectionParameters,
85+
MappingBuilderContext ctx,
86+
Set<String> existingVariableNames) {
87+
return collectLifecycleCallbackMethods( method,
88+
method.getResultType(),
4589
selectionParameters,
4690
filterBeforeMappingMethods( getAllAvailableMethods( method, ctx.getSourceModel() ) ),
4791
ctx,
@@ -60,6 +104,7 @@ public static List<LifecycleCallbackMethodReference> afterMappingMethods(Method
60104
MappingBuilderContext ctx,
61105
Set<String> existingVariableNames) {
62106
return collectLifecycleCallbackMethods( method,
107+
method.getResultType(),
63108
selectionParameters,
64109
filterAfterMappingMethods( getAllAvailableMethods( method, ctx.getSourceModel() ) ),
65110
ctx,
@@ -87,18 +132,12 @@ private static List<SourceMethod> getAllAvailableMethods(Method method, List<Sou
87132
}
88133

89134
private static List<LifecycleCallbackMethodReference> collectLifecycleCallbackMethods(
90-
Method method, SelectionParameters selectionParameters, List<SourceMethod> callbackMethods,
135+
Method method, Type targetType, SelectionParameters selectionParameters, List<SourceMethod> callbackMethods,
91136
MappingBuilderContext ctx, Set<String> existingVariableNames) {
92137

93138
MethodSelectors selectors =
94139
new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getTypeFactory(), ctx.getMessager() );
95140

96-
Type targetType = method.getResultType();
97-
98-
if ( !method.isUpdateMethod() ) {
99-
targetType = targetType.getEffectiveType();
100-
}
101-
102141
List<SelectedMethod<SourceMethod>> matchingMethods = selectors.getMatchingMethods(
103142
method,
104143
callbackMethods,

0 commit comments

Comments
 (0)