Skip to content

Commit

Permalink
#1742 & #1661 refactoring and making builder optional (#1811)
Browse files Browse the repository at this point in the history
  • Loading branch information
sjaakd committed May 24, 2019
1 parent 60c159a commit 3371058
Show file tree
Hide file tree
Showing 47 changed files with 912 additions and 530 deletions.
8 changes: 8 additions & 0 deletions core/src/main/java/org/mapstruct/Builder.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,12 @@
* @return the method that needs to tbe invoked on the builder
*/
String buildMethod() default "build";

/**
* Toggling builders on / off. Builders are sometimes used solely for unit testing (fluent testdata)
* MapStruct will need to use the regular getters /setters in that case.
*
* @return when true, no builder patterns will be applied
*/
boolean disableBuilder() default false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,11 @@ In case of a `MoreThanOneBuilderCreationMethodException` MapStruct will write a
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).
To finish the mapping MapStruct generates code that will invoke the build method of the builder.

[NOTE]
======
Builder detection can be switched off by means of `@Builder#disableBuilder`. MapStruct will fall back on regular getters / setters in case builders are disabled.
======

[NOTE]
======
The <<object-factories>> are also considered for the builder type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import javax.lang.model.element.AnnotationMirror;
import org.mapstruct.ap.internal.model.common.Assignment;
import org.mapstruct.ap.internal.model.common.BuilderType;
import org.mapstruct.ap.internal.model.common.ParameterBinding;
import org.mapstruct.ap.internal.model.common.SourceRHS;
import org.mapstruct.ap.internal.model.common.Type;
Expand Down Expand Up @@ -73,7 +74,7 @@ private boolean isDisableSubMappingMethodsGeneration() {
*
* @return See above
*/
Assignment createForgedAssignment(SourceRHS sourceRHS, ForgedMethod forgedMethod) {
Assignment createForgedAssignment(SourceRHS sourceRHS, BuilderType builderType, ForgedMethod forgedMethod) {

if ( ctx.getForgedMethodsUnderCreation().containsKey( forgedMethod ) ) {
return createAssignment( sourceRHS, ctx.getForgedMethodsUnderCreation().get( forgedMethod ) );
Expand All @@ -93,6 +94,7 @@ Assignment createForgedAssignment(SourceRHS sourceRHS, ForgedMethod forgedMethod
else {
forgedMappingMethod = new BeanMappingMethod.Builder()
.forgedMethod( forgedMethod )
.returnTypeBuilder( builderType )
.mappingContext( ctx )
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.mapstruct.ap.internal.model.common.Assignment;
import org.mapstruct.ap.internal.model.common.SourceRHS;
import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.model.source.BeanMapping;
import org.mapstruct.ap.internal.model.source.ForgedMethod;
import org.mapstruct.ap.internal.model.source.ForgedMethodHistory;
import org.mapstruct.ap.internal.util.Strings;
Expand Down Expand Up @@ -63,7 +64,12 @@ Assignment forgeMapping(SourceRHS sourceRHS, Type sourceType, Type targetType) {
true
);

return createForgedAssignment( sourceRHS, forgedMethod );
return createForgedAssignment(
sourceRHS,
ctx.getTypeFactory()
.builderTypeFor( targetType, BeanMapping.builderPrismFor( method ).orElse( null ) ),
forgedMethod
);
}

private String getName(Type sourceType, Type targetType) {
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
package org.mapstruct.ap.internal.model;

import java.util.Collection;
import java.util.Optional;
import javax.lang.model.element.ExecutableElement;

import org.mapstruct.ap.internal.model.common.BuilderType;
import org.mapstruct.ap.internal.model.source.BeanMapping;
import org.mapstruct.ap.internal.model.source.Method;
import org.mapstruct.ap.internal.prism.BuilderPrism;
import org.mapstruct.ap.internal.util.MapperConfiguration;
import org.mapstruct.ap.internal.util.Message;
import org.mapstruct.ap.internal.util.Strings;

Expand All @@ -36,14 +36,14 @@ public static MethodReference getBuilderFinisherMethod(Method method, BuilderTyp
return null;
}

BuilderPrism builderMapping = builderMappingPrism( method, ctx );
if ( builderMapping == null && buildMethods.size() == 1 ) {
Optional<BuilderPrism> builderMapping = BeanMapping.builderPrismFor( method );
if ( !builderMapping.isPresent() && buildMethods.size() == 1 ) {
return MethodReference.forMethodCall( first( buildMethods ).getSimpleName().toString() );
}
else {
String buildMethodPattern = DEFAULT_BUILD_METHOD_NAME;
if ( builderMapping != null ) {
buildMethodPattern = builderMapping.buildMethod();
if ( builderMapping.isPresent() ) {
buildMethodPattern = builderMapping.get().buildMethod();
}
for ( ExecutableElement buildMethod : buildMethods ) {
String methodName = buildMethod.getSimpleName().toString();
Expand All @@ -52,7 +52,7 @@ public static MethodReference getBuilderFinisherMethod(Method method, BuilderTyp
}
}

if ( builderMapping == null ) {
if ( !builderMapping.isPresent() ) {
ctx.getMessager().printMessage(
method.getExecutable(),
Message.BUILDER_NO_BUILD_METHOD_FOUND_DEFAULT,
Expand All @@ -65,7 +65,7 @@ public static MethodReference getBuilderFinisherMethod(Method method, BuilderTyp
else {
ctx.getMessager().printMessage(
method.getExecutable(),
builderMapping.mirror,
builderMapping.get().mirror,
Message.BUILDER_NO_BUILD_METHOD_FOUND,
buildMethodPattern,
builderType.getBuilder(),
Expand All @@ -78,11 +78,4 @@ public static MethodReference getBuilderFinisherMethod(Method method, BuilderTyp
return null;
}

private static BuilderPrism builderMappingPrism(Method method, MappingBuilderContext ctx) {
BeanMapping beanMapping = method.getMappingOptions().getBeanMapping();
if ( beanMapping != null && beanMapping.getBuilder() != null ) {
return beanMapping.getBuilder();
}
return MapperConfiguration.getInstanceOn( ctx.getMapperTypeElement() ).getBuilderPrism();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism;
import org.mapstruct.ap.internal.util.Message;
import org.mapstruct.ap.internal.util.accessor.Accessor;
import org.mapstruct.ap.internal.util.accessor.AccessorType;

import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.SET_TO_DEFAULT;
import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.SET_TO_NULL;
Expand Down Expand Up @@ -57,7 +58,7 @@ public class CollectionAssignmentBuilder {
private Accessor targetReadAccessor;
private Type targetType;
private String targetPropertyName;
private PropertyMapping.TargetWriteAccessorType targetAccessorType;
private AccessorType targetAccessorType;
private Assignment assignment;
private SourceRHS sourceRHS;
private NullValueCheckStrategyPrism nvcs;
Expand Down Expand Up @@ -88,7 +89,7 @@ public CollectionAssignmentBuilder targetPropertyName(String targetPropertyName)
return this;
}

public CollectionAssignmentBuilder targetAccessorType(PropertyMapping.TargetWriteAccessorType targetAccessorType) {
public CollectionAssignmentBuilder targetAccessorType(AccessorType targetAccessorType) {
this.targetAccessorType = targetAccessorType;
return this;
}
Expand Down Expand Up @@ -129,8 +130,7 @@ public Assignment build() {
CollectionMappingStrategyPrism cms = method.getMapperConfiguration().getCollectionMappingStrategy();
boolean targetImmutable = cms == CollectionMappingStrategyPrism.TARGET_IMMUTABLE || targetReadAccessor == null;

if ( targetAccessorType == PropertyMapping.TargetWriteAccessorType.SETTER ||
targetAccessorType == PropertyMapping.TargetWriteAccessorType.FIELD ) {
if ( targetAccessorType == AccessorType.SETTER || targetAccessorType == AccessorType.FIELD ) {

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

Expand All @@ -149,7 +149,7 @@ public Assignment build() {
result,
method.getThrownTypes(),
factoryMethod,
PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType ),
targetAccessorType == AccessorType.FIELD,
targetType,
true,
nvpms == SET_TO_NULL && !targetType.isPrimitive(),
Expand All @@ -165,7 +165,7 @@ else if ( method.isUpdateMethod() && !targetImmutable ) {
nvcs,
nvpms,
ctx.getTypeFactory(),
PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType )
targetAccessorType == AccessorType.FIELD
);
}
else if ( result.getType() == Assignment.AssignmentType.DIRECT ||
Expand All @@ -176,7 +176,7 @@ else if ( result.getType() == Assignment.AssignmentType.DIRECT ||
method.getThrownTypes(),
targetType,
ctx.getTypeFactory(),
PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType )
targetAccessorType == AccessorType.FIELD
);
}
else {
Expand All @@ -185,7 +185,7 @@ else if ( result.getType() == Assignment.AssignmentType.DIRECT ||
result,
method.getThrownTypes(),
targetType,
PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType )
targetAccessorType == AccessorType.FIELD
);
}
}
Expand All @@ -203,7 +203,7 @@ else if ( result.getType() == Assignment.AssignmentType.DIRECT ||
result,
method.getThrownTypes(),
targetType,
PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType )
targetAccessorType == AccessorType.FIELD
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ public final M build() {

MethodReference factoryMethod = null;
if ( !method.isUpdateMethod() ) {
factoryMethod = ObjectFactoryMethodResolver.getFactoryMethod( method, method.getResultType(), null, ctx );
factoryMethod = ObjectFactoryMethodResolver.getFactoryMethod( method, null, ctx );
}

Set<String> existingVariables = new HashSet<>( method.getParameterNames() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Set;

import javax.lang.model.element.ExecutableElement;

import org.mapstruct.ap.internal.model.common.Accessibility;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,60 @@ private LifecycleMethodResolver() {

/**
* @param method the method to obtain the beforeMapping methods for
* @param alternativeTarget alternative to {@link Method#getResultType()} e.g. when target is abstract
* @param selectionParameters method selectionParameters
* @param ctx the builder context
* @param existingVariableNames the existing variable names in the mapping method
* @return all applicable {@code @BeforeMapping} methods for the given method
*/
public static List<LifecycleCallbackMethodReference> beforeMappingMethods(Method method,
Type alternativeTarget,
SelectionParameters selectionParameters,
MappingBuilderContext ctx,
Set<String> existingVariableNames) {
return collectLifecycleCallbackMethods( method,
alternativeTarget,
selectionParameters,
filterBeforeMappingMethods( getAllAvailableMethods( method, ctx.getSourceModel() ) ),
ctx,
existingVariableNames );
}

/**
* @param method the method to obtain the afterMapping methods for
* @param alternativeTarget alternative to {@link Method#getResultType()} e.g. when target is abstract
* @param selectionParameters method selectionParameters
* @param ctx the builder context
* @param existingVariableNames list of already used variable names
* @return all applicable {@code @AfterMapping} methods for the given method
*/
public static List<LifecycleCallbackMethodReference> afterMappingMethods(Method method,
Type alternativeTarget,
SelectionParameters selectionParameters,
MappingBuilderContext ctx,
Set<String> existingVariableNames) {
return collectLifecycleCallbackMethods( method,
alternativeTarget,
selectionParameters,
filterAfterMappingMethods( getAllAvailableMethods( method, ctx.getSourceModel() ) ),
ctx,
existingVariableNames );
}


/**
* @param method the method to obtain the beforeMapping methods for
* @param selectionParameters method selectionParameters
* @param ctx the builder context
* @param existingVariableNames the existing variable names in the mapping method
* @return all applicable {@code @BeforeMapping} methods for the given method
*/
public static List<LifecycleCallbackMethodReference> beforeMappingMethods(Method method,
SelectionParameters selectionParameters,
MappingBuilderContext ctx,
Set<String> existingVariableNames) {
return collectLifecycleCallbackMethods( method,
method.getResultType(),
selectionParameters,
filterBeforeMappingMethods( getAllAvailableMethods( method, ctx.getSourceModel() ) ),
ctx,
Expand All @@ -60,6 +104,7 @@ public static List<LifecycleCallbackMethodReference> afterMappingMethods(Method
MappingBuilderContext ctx,
Set<String> existingVariableNames) {
return collectLifecycleCallbackMethods( method,
method.getResultType(),
selectionParameters,
filterAfterMappingMethods( getAllAvailableMethods( method, ctx.getSourceModel() ) ),
ctx,
Expand Down Expand Up @@ -87,18 +132,12 @@ private static List<SourceMethod> getAllAvailableMethods(Method method, List<Sou
}

private static List<LifecycleCallbackMethodReference> collectLifecycleCallbackMethods(
Method method, SelectionParameters selectionParameters, List<SourceMethod> callbackMethods,
Method method, Type targetType, SelectionParameters selectionParameters, List<SourceMethod> callbackMethods,
MappingBuilderContext ctx, Set<String> existingVariableNames) {

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

Type targetType = method.getResultType();

if ( !method.isUpdateMethod() ) {
targetType = targetType.getEffectiveType();
}

List<SelectedMethod<SourceMethod>> matchingMethods = selectors.getMatchingMethods(
method,
callbackMethods,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ public MapMappingMethod build() {
MethodReference factoryMethod = null;
if ( !method.isUpdateMethod() ) {
factoryMethod = ObjectFactoryMethodResolver
.getFactoryMethod( method, method.getResultType(), null, ctx );
.getFactoryMethod( method, null, ctx );
}

keyAssignment = new LocalVarWrapper( keyAssignment, method.getThrownTypes(), keyTargetType, false );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ public void setAssignment( Assignment assignment ) {

@Override
public String getSourceReference() {
return assignment.getSourceReference();
return assignment != null ? assignment.getSourceReference() : null;
}

@Override
Expand Down Expand Up @@ -353,7 +353,8 @@ public static MethodReference forMethodCall(String methodName) {
@Override
public String toString() {
String mapper = declaringMapper != null ? declaringMapper.getType().getName().toString() : "";
String argument = getAssignment() != null ? getAssignment().toString() : getSourceReference();
String argument = getAssignment() != null ? getAssignment().toString() :
( getSourceReference() != null ? getSourceReference() : "" );
String returnTypeAsString = returnType != null ? returnType.toString() : "";
List<String> arguments = sourceParameters.stream()
.map( p -> p.isMappingContext() || p.isMappingTarget() || p.isTargetType() ? p.getName() : argument )
Expand Down

0 comments on commit 3371058

Please sign in to comment.