diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java index 58e6c1b6d..099655cd8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java @@ -152,6 +152,11 @@ public class MethodReference extends MappingMethod implements Assignment { assignment.setSourceLocalVarName( sourceLocalVarName ); } + @Override + public String getSourceParameterName() { + return assignment.getSourceParameterName(); + } + /** * @return the type of the single source parameter that is not the {@code @TargetType} parameter */ @@ -206,7 +211,7 @@ public class MethodReference extends MappingMethod implements Assignment { } @Override - public boolean isUpdateMethod() { + public boolean isCallingUpdateMethod() { return isUpdateMethod; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 253603b7b..e7dcb6f43 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -19,9 +19,6 @@ package org.mapstruct.ap.internal.model; import static org.mapstruct.ap.internal.model.assignment.Assignment.AssignmentType.DIRECT; -import static org.mapstruct.ap.internal.model.assignment.Assignment.AssignmentType.MAPPED_TYPE_CONVERTED; -import static org.mapstruct.ap.internal.model.assignment.Assignment.AssignmentType.TYPE_CONVERTED; -import static org.mapstruct.ap.internal.model.assignment.Assignment.AssignmentType.TYPE_CONVERTED_MAPPED; import static org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism.ALWAYS; import static org.mapstruct.ap.internal.util.Collections.first; import static org.mapstruct.ap.internal.util.Collections.last; @@ -43,7 +40,6 @@ import org.mapstruct.ap.internal.model.assignment.GetterWrapperForCollectionsAnd import org.mapstruct.ap.internal.model.assignment.NullCheckWrapper; import org.mapstruct.ap.internal.model.assignment.SetterWrapper; import org.mapstruct.ap.internal.model.assignment.SetterWrapperForCollectionsAndMaps; -import org.mapstruct.ap.internal.model.assignment.UpdateNullCheckWrapper; import org.mapstruct.ap.internal.model.assignment.UpdateWrapper; import org.mapstruct.ap.internal.model.common.ModelElement; import org.mapstruct.ap.internal.model.common.Parameter; @@ -55,6 +51,7 @@ import org.mapstruct.ap.internal.model.source.PropertyEntry; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SourceReference; +import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism; import org.mapstruct.ap.internal.util.Executables; import org.mapstruct.ap.internal.util.MapperConfiguration; import org.mapstruct.ap.internal.util.Message; @@ -335,7 +332,7 @@ public class PropertyMapping extends ModelElement { if ( targetAccessorType == TargetWriteAccessorType.SETTER || targetAccessorType == TargetWriteAccessorType.FIELD ) { - result = assignToPlainViaSetter( sourceType, targetType, rightHandSide ); + result = assignToPlainViaSetter( targetType, rightHandSide ); } else { result = assignToPlainViaAdder( rightHandSide ); @@ -344,11 +341,9 @@ public class PropertyMapping extends ModelElement { } - private Assignment assignToPlainViaSetter(Type sourceType, Type targetType, Assignment rhs) { + private Assignment assignToPlainViaSetter(Type targetType, Assignment rhs) { - Assignment result; - - if ( rhs.isUpdateMethod() ) { + if ( rhs.isCallingUpdateMethod() ) { if ( targetReadAccessor == null ) { ctx.getMessager().printMessage( method.getExecutable(), @@ -356,59 +351,13 @@ public class PropertyMapping extends ModelElement { targetPropertyName ); } - Assignment factoryMethod = ctx.getMappingResolver().getFactoryMethod( method, targetType, null ); - result = new UpdateWrapper( rhs, method.getThrownTypes(), factoryMethod, - targetType, isFieldAssignment() - ); + Assignment factory = ctx.getMappingResolver().getFactoryMethod( method, targetType, null ); + return new UpdateWrapper( rhs, method.getThrownTypes(), factory, isFieldAssignment(), targetType, + true ); } else { - result = new SetterWrapper( rhs, method.getThrownTypes(), isFieldAssignment() ); - } - - // if the sourceReference is the bean mapping method parameter itself, no null check is required - // since this is handled by the BeanMapping - if ( sourceReference.getPropertyEntries().isEmpty() ) { - return result; - } - - // add a null / presence checked when required - if ( sourceType.isPrimitive() ) { - if ( rhs.getSourcePresenceCheckerReference() != null ) { - result = new NullCheckWrapper( result ); - } - } - else { - - if ( result.isUpdateMethod() ) { - result = new UpdateNullCheckWrapper( result, isFieldAssignment() ); - } - else if ( rhs.getSourcePresenceCheckerReference() != null ) { - result = new NullCheckWrapper( result ); - } - else if ( ALWAYS.equals( method.getMapperConfiguration().getNullValueCheckStrategy() ) ) { - result = new NullCheckWrapper( result ); - useLocalVarWhenNested( result ); - } - else if ( result.getType() == TYPE_CONVERTED - || result.getType() == TYPE_CONVERTED_MAPPED - || result.getType() == MAPPED_TYPE_CONVERTED - || (result.getType() == DIRECT && targetType.isPrimitive() ) ) { - // for primitive types null check is not possible at all, but a conversion needs - // a null check. - result = new NullCheckWrapper( result ); - useLocalVarWhenNested( result ); - } - } - - return result; - } - - private void useLocalVarWhenNested(Assignment rightHandSide) { - if ( sourceReference.getPropertyEntries().size() > 1 ) { - String name = first( sourceReference.getPropertyEntries() ).getName(); - String safeName = Strings.getSaveVariableName( name, existingVariableNames ); - existingVariableNames.add( safeName ); - rightHandSide.setSourceLocalVarName( safeName ); + NullValueCheckStrategyPrism nvcs = method.getMapperConfiguration().getNullValueCheckStrategy(); + return new SetterWrapper( rhs, method.getThrownTypes(), nvcs, isFieldAssignment(), targetType ); } } @@ -421,8 +370,7 @@ public class PropertyMapping extends ModelElement { } else { // Possibly adding null to a target collection. So should be surrounded by an null check. - result = new SetterWrapper( result, method.getThrownTypes(), isFieldAssignment() ); - result = new NullCheckWrapper( result ); + result = new SetterWrapper( result, method.getThrownTypes(), ALWAYS, isFieldAssignment(), targetType ); } return result; } @@ -435,7 +383,7 @@ public class PropertyMapping extends ModelElement { if ( targetAccessorType == TargetWriteAccessorType.SETTER || targetAccessorType == TargetWriteAccessorType.FIELD ) { - if ( result.isUpdateMethod() ) { + if ( result.isCallingUpdateMethod() ) { // call to an update method if ( targetReadAccessor == null ) { ctx.getMessager().printMessage( @@ -449,8 +397,9 @@ public class PropertyMapping extends ModelElement { result, method.getThrownTypes(), factoryMethod, + isFieldAssignment(), targetType, - isFieldAssignment() + true ); } else { @@ -458,9 +407,8 @@ public class PropertyMapping extends ModelElement { result = new SetterWrapperForCollectionsAndMaps( result, method.getThrownTypes(), - existingVariableNames, targetType, - ALWAYS == method.getMapperConfiguration().getNullValueCheckStrategy(), + method.getMapperConfiguration().getNullValueCheckStrategy(), ctx.getTypeFactory(), targetWriteAccessorType == TargetWriteAccessorType.FIELD ); @@ -470,7 +418,6 @@ public class PropertyMapping extends ModelElement { // target accessor is getter, so wrap the setter in getter map/ collection handling result = new GetterWrapperForCollectionsAndMaps( result, method.getThrownTypes(), - existingVariableNames, targetType, isFieldAssignment() ); @@ -560,6 +507,11 @@ public class PropertyMapping extends ModelElement { existingVariableNames, sourceReference.toString() ); + + // create a local variable to which forged method can be assigned. + String desiredName = first( sourceReference.getPropertyEntries() ).getName(); + sourceRhs.setSourceLocalVarName( sourceRhs.createLocalVarName( desiredName ) ); + return sourceRhs; } @@ -767,7 +719,7 @@ public class PropertyMapping extends ModelElement { Executables.isFieldAccessor( targetWriteAccessor ) ) { // target accessor is setter, so decorate assignment as setter - if ( assignment.isUpdateMethod() ) { + if ( assignment.isCallingUpdateMethod() ) { if ( targetReadAccessor == null ) { ctx.getMessager().printMessage( method.getExecutable(), @@ -778,8 +730,7 @@ public class PropertyMapping extends ModelElement { Assignment factoryMethod = ctx.getMappingResolver().getFactoryMethod( method, targetType, null ); assignment = new UpdateWrapper( assignment, method.getThrownTypes(), factoryMethod, - targetType, isFieldAssignment() - ); + isFieldAssignment(), targetType, false ); } else { assignment = new SetterWrapper( assignment, method.getThrownTypes(), isFieldAssignment() ); @@ -790,7 +741,6 @@ public class PropertyMapping extends ModelElement { // target accessor is getter, so getter map/ collection handling assignment = new GetterWrapperForCollectionsAndMaps( assignment, method.getThrownTypes(), - existingVariableNames, targetType, isFieldAssignment() ); @@ -863,7 +813,6 @@ public class PropertyMapping extends ModelElement { // target accessor is getter, so wrap the setter in getter map/ collection handling assignment = new GetterWrapperForCollectionsAndMaps( assignment, method.getThrownTypes(), - existingVariableNames, targetType, isFieldAssignment() ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/SourceRHS.java b/processor/src/main/java/org/mapstruct/ap/internal/model/SourceRHS.java index 58ef565fa..5a2f555d3 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/SourceRHS.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/SourceRHS.java @@ -47,7 +47,7 @@ public class SourceRHS extends ModelElement implements Assignment { public SourceRHS(String sourceReference, Type sourceType, Set existingVariableNames, String sourceErrorMessagePart ) { - this( null, sourceReference, null, sourceType, existingVariableNames, sourceErrorMessagePart ); + this( sourceReference, sourceReference, null, sourceType, existingVariableNames, sourceErrorMessagePart ); } public SourceRHS(String sourceParameterName, String sourceReference, String sourcePresenceCheckerReference, @@ -77,7 +77,9 @@ public class SourceRHS extends ModelElement implements Assignment { @Override public String createLocalVarName(String desiredName) { - return Strings.getSaveVariableName( desiredName, existingVariableNames ); + String result = Strings.getSaveVariableName( desiredName, existingVariableNames ); + existingVariableNames.add( result ); + return result; } @Override @@ -111,7 +113,7 @@ public class SourceRHS extends ModelElement implements Assignment { } @Override - public boolean isUpdateMethod() { + public boolean isCallingUpdateMethod() { return false; } @@ -137,12 +139,13 @@ public class SourceRHS extends ModelElement implements Assignment { /** * For collection type, use element as source type to find a suitable mapping method. * - * @param useElementAsSourceTypeForMatching + * @param useElementAsSourceTypeForMatching uses the element of a collection as source type for the matching process */ public void setUseElementAsSourceTypeForMatching(boolean useElementAsSourceTypeForMatching) { this.useElementAsSourceTypeForMatching = useElementAsSourceTypeForMatching; } + @Override public String getSourceParameterName() { return sourceParameterName; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/TypeConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/model/TypeConversion.java index afd94fad5..d7a83075b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/TypeConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/TypeConversion.java @@ -111,6 +111,11 @@ public class TypeConversion extends ModelElement implements Assignment { assignment.setSourceLocalVarName( sourceLocalVarName ); } + @Override + public String getSourceParameterName() { + return assignment.getSourceParameterName(); + } + @Override public void setAssignment( Assignment assignment ) { this.assignment = assignment; @@ -130,7 +135,7 @@ public class TypeConversion extends ModelElement implements Assignment { } @Override - public boolean isUpdateMethod() { + public boolean isCallingUpdateMethod() { return false; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AdderWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AdderWrapper.java index 1843301c6..9d6142ed6 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AdderWrapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AdderWrapper.java @@ -33,14 +33,12 @@ import org.mapstruct.ap.internal.model.common.Type; public class AdderWrapper extends AssignmentWrapper { private final List thrownTypesToExclude; - private final String sourceIteratorName; - public AdderWrapper( Assignment decoratedAssignment, List thrownTypesToExclude, boolean fieldAssignment ) { - super( decoratedAssignment, fieldAssignment ); + public AdderWrapper( Assignment rhs, List thrownTypesToExclude, boolean fieldAssignment ) { + super( rhs, fieldAssignment ); this.thrownTypesToExclude = thrownTypesToExclude; - String desiredName = decoratedAssignment.getSourceType().getTypeParameters().get( 0 ).getName(); - this.sourceIteratorName = decoratedAssignment.createLocalVarName( desiredName ); - decoratedAssignment.setSourceLocalVarName( sourceIteratorName ); + String desiredName = rhs.getSourceType().getTypeParameters().get( 0 ).getName(); + rhs.setSourceLocalVarName( rhs.createLocalVarName( desiredName ) ); } @Override @@ -65,7 +63,4 @@ public class AdderWrapper extends AssignmentWrapper { return imported; } - public String getSourceIteratorName() { - return sourceIteratorName; - } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/Assignment.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/Assignment.java index d02b5410e..09ef8bed1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/Assignment.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/Assignment.java @@ -32,17 +32,34 @@ public interface Assignment { enum AssignmentType { /** assignment is direct */ - DIRECT, + DIRECT( true, false ), /** assignment is type converted */ - TYPE_CONVERTED, + TYPE_CONVERTED( false, true ), /** assignment is mapped (builtin/custom) */ - MAPPED, + MAPPED( false, false ), /** 2 mapping methods (builtin/custom) are applied to get the target */ - MAPPED_TWICE, + MAPPED_TWICE( false, false ), /** assignment is first mapped (builtin/custom), then the result is type converted */ - MAPPED_TYPE_CONVERTED, + MAPPED_TYPE_CONVERTED( false, true ), /** assignment is first type converted, and then mapped (builtin/custom) */ - TYPE_CONVERTED_MAPPED + TYPE_CONVERTED_MAPPED( false, true ); + + private final boolean direct; + private final boolean converted; + + AssignmentType( boolean isDirect, boolean isConverted ) { + this.direct = isDirect; + this.converted = isConverted; + } + + public boolean isDirect() { + return direct; + } + + public boolean isConverted() { + return converted; + } + } /** @@ -106,6 +123,14 @@ public interface Assignment { */ String getSourceLocalVarName(); + /** + * Returns the source parameter name, to which this assignment applies. Note: the source parameter itself might + * be mapped by this assignment, or one of its properties + * + * @return the source parameter name + */ + String getSourceParameterName(); + /** * Use sourceLocalVarName iso sourceReference * @param sourceLocalVarName source local variable name @@ -119,5 +144,5 @@ public interface Assignment { */ AssignmentType getType(); - boolean isUpdateMethod(); + boolean isCallingUpdateMethod(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AssignmentWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AssignmentWrapper.java index 50d2058a4..8ed573f95 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AssignmentWrapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AssignmentWrapper.java @@ -83,14 +83,19 @@ public abstract class AssignmentWrapper extends ModelElement implements Assignme decoratedAssignment.setSourceLocalVarName( sourceLocalVarName ); } + @Override + public String getSourceParameterName() { + return decoratedAssignment.getSourceParameterName(); + } + @Override public AssignmentType getType() { return decoratedAssignment.getType(); } @Override - public boolean isUpdateMethod() { - return decoratedAssignment.isUpdateMethod(); + public boolean isCallingUpdateMethod() { + return decoratedAssignment.isCallingUpdateMethod(); } @Override diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.java index 80bb060bf..2c25f0a46 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.java @@ -19,7 +19,6 @@ package org.mapstruct.ap.internal.model.assignment; import java.util.List; -import java.util.Set; import org.mapstruct.ap.internal.model.common.Type; @@ -39,22 +38,19 @@ import org.mapstruct.ap.internal.model.common.Type; public class GetterWrapperForCollectionsAndMaps extends WrapperForCollectionsAndMaps { /** - * @param decoratedAssignment - * @param thrownTypesToExclude - * @param existingVariableNames - * @param targetType - * @param fieldAssignment + * @param decoratedAssignment source RHS + * @param thrownTypesToExclude set of types to exclude from re-throwing + * @param targetType the target type + * @param fieldAssignment true when this the assignment is to a field rather than via accessors */ public GetterWrapperForCollectionsAndMaps(Assignment decoratedAssignment, List thrownTypesToExclude, - Set existingVariableNames, Type targetType, boolean fieldAssignment) { super( decoratedAssignment, thrownTypesToExclude, - existingVariableNames, targetType, fieldAssignment ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapper.java index 7595109f2..8fe40dd30 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapper.java @@ -22,6 +22,9 @@ import java.util.ArrayList; import java.util.List; import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism; + +import static org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism.ALWAYS; /** * Wraps the assignment in a target setter. @@ -31,10 +34,23 @@ import org.mapstruct.ap.internal.model.common.Type; public class SetterWrapper extends AssignmentWrapper { private final List thrownTypesToExclude; + private final boolean includeSourceNullCheck; - public SetterWrapper(Assignment decoratedAssignment, List thrownTypesToExclude, boolean fieldAssignment) { - super( decoratedAssignment, fieldAssignment ); + public SetterWrapper(Assignment rhs, + List thrownTypesToExclude, + NullValueCheckStrategyPrism nvms, + boolean fieldAssignment, + Type targetType) { + + super( rhs, fieldAssignment ); this.thrownTypesToExclude = thrownTypesToExclude; + this.includeSourceNullCheck = includeSourceNullCheck( rhs, nvms, targetType ); + } + + public SetterWrapper(Assignment rhs, List thrownTypesToExclude, boolean fieldAssignment ) { + super( rhs, fieldAssignment ); + this.thrownTypesToExclude = thrownTypesToExclude; + this.includeSourceNullCheck = false; } @Override @@ -51,4 +67,30 @@ public class SetterWrapper extends AssignmentWrapper { return result; } + public boolean isIncludeSourceNullCheck() { + return includeSourceNullCheck; + } + + /** + * Wraps the assignment in a target setter. include a null check when + * + * - Not if source is the parameter iso property, because the null check is than handled by the bean mapping + * - Not when source is primitive, you can't null check a primitive + * - The source property is fed to a conversion somehow before its assigned to the target + * - The user decided to ALLWAYS include a null check + * - When there's a source local variable name defined (e.g. nested source properties) + * - TODO: The last one I forgot..? + * + * @param rhs the source righthand side + * @param nvms null value check strategy + * @param targetType the target type + * + * @return include a null check + */ + private boolean includeSourceNullCheck(Assignment rhs, NullValueCheckStrategyPrism nvms, Type targetType) { + return !rhs.getSourceReference().equals( rhs.getSourceParameterName() ) + && !rhs.getSourceType().isPrimitive() + && (ALWAYS == nvms || rhs.getType().isConverted() || rhs.getSourceLocalVarName() != null + || (rhs.getType().isDirect() && targetType.isPrimitive())); + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMaps.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMaps.java index a97490a57..a6b179e0d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMaps.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMaps.java @@ -26,6 +26,9 @@ import java.util.Set; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; +import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism; + +import static org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism.ALWAYS; /** * This wrapper handles the situation were an assignment is done via the setter. @@ -41,26 +44,24 @@ import org.mapstruct.ap.internal.model.common.TypeFactory; */ public class SetterWrapperForCollectionsAndMaps extends WrapperForCollectionsAndMaps { - private final boolean allwaysIncludeNullCheck; + private final boolean includeSourceNullCheck; private final Type targetType; private final TypeFactory typeFactory; public SetterWrapperForCollectionsAndMaps(Assignment decoratedAssignment, List thrownTypesToExclude, - Set existingVariableNames, Type targetType, - boolean allwaysIncludeNullCheck, + NullValueCheckStrategyPrism nvms, TypeFactory typeFactory, boolean fieldAssignment) { super( decoratedAssignment, thrownTypesToExclude, - existingVariableNames, targetType, fieldAssignment ); - this.allwaysIncludeNullCheck = allwaysIncludeNullCheck; + this.includeSourceNullCheck = ALWAYS == nvms; this.targetType = targetType; this.typeFactory = typeFactory; } @@ -83,8 +84,8 @@ public class SetterWrapperForCollectionsAndMaps extends WrapperForCollectionsAnd return imported; } - public boolean isAllwaysIncludeNullCheck() { - return allwaysIncludeNullCheck; + public boolean isIncludeSourceNullCheck() { + return includeSourceNullCheck; } public boolean isDirectAssignment() { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.java index e610c1f64..a5bdb99e9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.java @@ -35,13 +35,19 @@ public class UpdateWrapper extends AssignmentWrapper { private final List thrownTypesToExclude; private final Assignment factoryMethod; private final Type targetImplementationType; + private final boolean includeSourceNullCheck; - public UpdateWrapper(Assignment decoratedAssignment, List thrownTypesToExclude, Assignment factoryMethod, - Type targetImplementationType, boolean fieldAssignment ) { + public UpdateWrapper( Assignment decoratedAssignment, + List thrownTypesToExclude, + Assignment factoryMethod, + boolean fieldAssignment, + Type targetType, + boolean includeSourceNullCheck ) { super( decoratedAssignment, fieldAssignment ); this.thrownTypesToExclude = thrownTypesToExclude; this.factoryMethod = factoryMethod; - this.targetImplementationType = determineImplType( factoryMethod, targetImplementationType ); + this.targetImplementationType = determineImplType( factoryMethod, targetType ); + this.includeSourceNullCheck = includeSourceNullCheck; } private static Type determineImplType(Assignment factoryMethod, Type targetType) { @@ -87,4 +93,7 @@ public class UpdateWrapper extends AssignmentWrapper { return factoryMethod; } + public boolean isIncludeSourceNullCheck() { + return includeSourceNullCheck; + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/WrapperForCollectionsAndMaps.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/WrapperForCollectionsAndMaps.java index a00a585e6..592d5c0ef 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/WrapperForCollectionsAndMaps.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/WrapperForCollectionsAndMaps.java @@ -24,7 +24,6 @@ import java.util.List; import java.util.Set; import org.mapstruct.ap.internal.model.common.Type; -import org.mapstruct.ap.internal.util.Strings; /** * This is the base class for the {@link GetterWrapperForCollectionsAndMaps} and @@ -35,26 +34,24 @@ import org.mapstruct.ap.internal.util.Strings; public class WrapperForCollectionsAndMaps extends AssignmentWrapper { private final List thrownTypesToExclude; - private final String localVarName; - private final Type localVarType; + private final String nullCheckLocalVarName; + private final Type nullCheckLocalVarType; - public WrapperForCollectionsAndMaps(Assignment decoratedAssignment, + public WrapperForCollectionsAndMaps(Assignment rhs, List thrownTypesToExclude, - Set existingVariableNames, Type targetType, boolean fieldAssignment) { - super( decoratedAssignment, fieldAssignment ); + super( rhs, fieldAssignment ); this.thrownTypesToExclude = thrownTypesToExclude; - if ( getType() == AssignmentType.DIRECT && getSourceType() != null ) { - this.localVarType = getSourceType(); + if ( rhs.getType() == AssignmentType.DIRECT && rhs.getSourceType() != null ) { + this.nullCheckLocalVarType = rhs.getSourceType(); } else { - this.localVarType = targetType; + this.nullCheckLocalVarType = targetType; } - this.localVarName = Strings.getSaveVariableName( localVarType.getName(), existingVariableNames ); - existingVariableNames.add( this.localVarName ); + this.nullCheckLocalVarName = rhs.createLocalVarName( nullCheckLocalVarType.getName() ); } @Override @@ -75,16 +72,16 @@ public class WrapperForCollectionsAndMaps extends AssignmentWrapper { public Set getImportTypes() { Set imported = new HashSet(); imported.addAll( super.getImportTypes() ); - imported.add( localVarType ); - imported.addAll( localVarType.getTypeParameters() ); + imported.add( nullCheckLocalVarType ); + imported.addAll( nullCheckLocalVarType.getTypeParameters() ); return imported; } - public String getLocalVarName() { - return localVarName; + public String getNullCheckLocalVarName() { + return nullCheckLocalVarName; } - public Type getLocalVarType() { - return localVarType; + public Type getNullCheckLocalVarType() { + return nullCheckLocalVarType; } } diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/AdderWrapper.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/AdderWrapper.ftl index fa454e92f..14aa22439 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/AdderWrapper.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/AdderWrapper.ftl @@ -21,7 +21,7 @@ <#import "../macro/CommonMacros.ftl" as lib> <@lib.handleExceptions> if ( ${sourceReference} != null ) { - for ( <@includeModel object=sourceType.typeParameters[0]/> ${sourceIteratorName} : ${sourceReference} ) { + for ( <@includeModel object=sourceType.typeParameters[0]/> ${sourceLocalVarName} : ${sourceReference} ) { ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><@lib.handleAssignment/>; } } diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.ftl index d66dcbe31..1cc8cad11 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.ftl @@ -19,13 +19,14 @@ --> <#import "../macro/CommonMacros.ftl" as lib> +<@lib.sourceLocalVarAssignment/> if ( ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWriteAccesing /> != null ) { <@lib.handleExceptions> <#if ext.existingInstanceMapping> ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWriteAccesing />.clear(); <@lib.handleNullCheck> - ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWriteAccesing />.<#if ext.targetType.collectionType>addAll<#else>putAll( ${localVarName} ); + ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWriteAccesing />.<#if ext.targetType.collectionType>addAll<#else>putAll( ${nullCheckLocalVarName} ); } diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapper.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapper.ftl index a02142f52..468e6fcf0 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapper.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapper.ftl @@ -19,44 +19,33 @@ --> <#import "../macro/CommonMacros.ftl" as lib> -<#if (thrownTypes?size == 0) > - <@assignment_w_defaultValue/> -<#else> - try { - <@assignment_w_defaultValue/> - } - <#list thrownTypes as exceptionType> - catch ( <@includeModel object=exceptionType/> e ) { - throw new RuntimeException( e ); - } - - -<#macro _assignment> - <@includeModel object=assignment - targetBeanName=ext.targetBeanName - existingInstanceMapping=ext.existingInstanceMapping - targetReadAccessorName=ext.targetReadAccessorName - targetWriteAccessorName=ext.targetWriteAccessorName - targetType=ext.targetType - defaultValueAssignment=ext.defaultValueAssignment/> - -<#macro _defaultValueAssignment> - <@includeModel object=ext.defaultValueAssignment.assignment - targetBeanName=ext.targetBeanName - existingInstanceMapping=ext.existingInstanceMapping - targetWriteAccessorName=ext.targetWriteAccessorName - targetType=ext.targetType/> - -<#macro assignment_w_defaultValue> - <#if ext.defaultValueAssignment?? > - <#-- if the assignee property is a primitive, defaulValueAssignment will not be set --> - if ( ${sourceReference} != null ) { - ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><@_assignment/>; +<@lib.handleExceptions> + <@lib.sourceLocalVarAssignment/> + <#if sourcePresenceCheckerReference??> + if ( ${sourcePresenceCheckerReference} ) { + <@assignment/>; } - else { - ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><@_defaultValueAssignment/>; + <@elseDefaultAssignment/> + <#elseif includeSourceNullCheck || ext.defaultValueAssignment??> + if ( <#if sourceLocalVarName??>${sourceLocalVarName}<#else>${sourceReference} != null ) { + <@assignment/>; } + <@elseDefaultAssignment/> <#else> - ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><@_assignment/>; + <@assignment/>; - + +<#-- + standard assignment +--> +<#macro assignment>${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><@lib.handleAssignment/> +<#-- + add default assignment when required +--> +<#macro elseDefaultAssignment> + <#if ext.defaultValueAssignment?? > + else { + <@lib.handeDefaultAssigment/> + } + + \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMaps.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMaps.ftl index f9bbbfe31..ef6c1e0c7 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMaps.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMaps.ftl @@ -19,14 +19,15 @@ --> <#import "../macro/CommonMacros.ftl" as lib> +<@lib.sourceLocalVarAssignment/> <@lib.handleExceptions> <#if ext.existingInstanceMapping> if ( ${ext.targetBeanName}.${ext.targetReadAccessorName} != null ) { <@lib.handleNullCheck> ${ext.targetBeanName}.${ext.targetReadAccessorName}.clear(); - ${ext.targetBeanName}.${ext.targetReadAccessorName}.<#if ext.targetType.collectionType>addAll<#else>putAll( ${localVarName} ); + ${ext.targetBeanName}.${ext.targetReadAccessorName}.<#if ext.targetType.collectionType>addAll<#else>putAll( ${nullCheckLocalVarName} ); - <#if !ext.defaultValueAssignment?? && !sourcePresenceCheckerReference?? && !allwaysIncludeNullCheck>else {<#-- the opposite (defaultValueAssignment) case is handeld inside lib.handleNullCheck --> + <#if !ext.defaultValueAssignment?? && !sourcePresenceCheckerReference?? && !includeSourceNullCheck>else {<#-- the opposite (defaultValueAssignment) case is handeld inside lib.handleNullCheck --> ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite>null; } @@ -43,7 +44,7 @@ --> <#macro callTargetWriteAccessor> <@lib.handleNullCheck> - ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if directAssignment><@wrapLocalVarInCollectionInitializer/><#else>${localVarName}; + ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if directAssignment><@wrapLocalVarInCollectionInitializer/><#else>${nullCheckLocalVarName}; <#-- @@ -51,8 +52,8 @@ --> <#macro wrapLocalVarInCollectionInitializer><@compress single_line=true> <#if enumSet> - EnumSet.copyOf( ${localVarName} ) + EnumSet.copyOf( ${nullCheckLocalVarName} ) <#else> - new <#if ext.targetType.implementationType??><@includeModel object=ext.targetType.implementationType/><#else><@includeModel object=ext.targetType/>( ${localVarName} ) + new <#if ext.targetType.implementationType??><@includeModel object=ext.targetType.implementationType/><#else><@includeModel object=ext.targetType/>( ${nullCheckLocalVarName} ) \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.ftl index 59a915634..28e7bbe77 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.ftl @@ -19,28 +19,25 @@ --> <#import '../macro/CommonMacros.ftl' as lib > -<#if (thrownTypes?size == 0) > - <@_assignment/>; -<#else> - try { - <@_assignment/>; +<@lib.handleExceptions> + <#if includeSourceNullCheck> + if ( <#if sourcePresenceCheckerReference?? >${sourcePresenceCheckerReference}<#else>${sourceReference} != null ) { + <@assignToExistingTarget/> + <@lib.handleAssignment/>; } - <#list thrownTypes as exceptionType> - catch ( <@includeModel object=exceptionType/> e ) { - throw new RuntimeException( e ); + else { + ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite>null; } - - -<#macro _assignment> + <#else> + <@assignToExistingTarget/> + <@lib.handleAssignment/>; + + +<#-- + target innner check and assignment +--> +<#macro assignToExistingTarget> if ( ${ext.targetBeanName}.${ext.targetReadAccessorName} == null ) { - ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if factoryMethod??><@includeModel object=factoryMethod targetType=ext.targetType/><#else><@_newObject/>; + ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><@lib.initTargetObject/>; } - <@includeModel object=assignment - targetBeanName=ext.targetBeanName - existingInstanceMapping=ext.existingInstanceMapping - targetReadAccessorName=ext.targetReadAccessorName - targetWriteAccessorName=ext.targetWriteAccessorName - targetType=ext.targetType/> -<#macro _newObject>new <#if ext.targetType.implementationType??><@includeModel object=ext.targetType.implementationType/><#else><@includeModel object=ext.targetType/>() - diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl index 060fcb513..c79a8e66e 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl @@ -30,18 +30,18 @@ <#macro handleNullCheck> <#if sourcePresenceCheckerReference??> if ( ${sourcePresenceCheckerReference} ) { - <@includeModel object=localVarType/> ${localVarName} = <@lib.handleAssignment/>; + <@includeModel object=nullCheckLocalVarType/> ${nullCheckLocalVarName} = <@lib.handleAssignment/>; <#nested> } <#else> - <@includeModel object=localVarType/> ${localVarName} = <@lib.handleAssignment/>; - if ( ${localVarName} != null ) { + <@includeModel object=nullCheckLocalVarType/> ${nullCheckLocalVarName} = <@lib.handleAssignment/>; + if ( ${nullCheckLocalVarName} != null ) { <#nested> } <#if ext.defaultValueAssignment?? > else { - <@lib.handeDefaultAssigment/> + <@handeDefaultAssigment/> } @@ -104,4 +104,38 @@ Performs a default assignment with a default value. --> <#macro handleWriteAccesing> <#t><#if fieldAssignment><#else>() - \ No newline at end of file + +<#-- + macro: initTargetObject + + purpose: To factorize or construct a new target object +--> +<#macro initTargetObject><@compress single_line=true> + <#if factoryMethod??> + <@includeModel object=factoryMethod targetType=ext.targetType/> + <#else> + new <@constructTargetObject/>() + + +<#-- + macro: constructTargetObject + + purpose: Either call the constructor of the target object directly or of the implementing type. +--> +<#macro constructTargetObject><@compress single_line=true> + <#if ext.targetType.implementationType??> + <@includeModel object=ext.targetType.implementationType/> + <#else> + <@includeModel object=ext.targetType/> + + +<#-- + macro: sourceLocalVarAssignment + + purpose: assigment for source local variables +--> +<#macro sourceLocalVarAssignment> + <#if sourceLocalVarName??> + <@includeModel object=sourceType/> ${sourceLocalVarName} = ${sourceReference}; + +