diff --git a/core/src/main/java/org/mapstruct/BeanMapping.java b/core/src/main/java/org/mapstruct/BeanMapping.java
index 2492477a2..b510b442d 100644
--- a/core/src/main/java/org/mapstruct/BeanMapping.java
+++ b/core/src/main/java/org/mapstruct/BeanMapping.java
@@ -54,7 +54,7 @@ public @interface BeanMapping {
String[] qualifiedByName() default { };
/**
- * The strategy to be applied when {@code null} is passed as source value to this bean mapping. If no
+ * The strategy to be applied when {@code null} is passed as source bean argument value to this bean mapping. If no
* strategy is configured, the strategy given via {@link MapperConfig#nullValueMappingStrategy()} or
* {@link Mapper#nullValueMappingStrategy()} will be applied, using {@link NullValueMappingStrategy#RETURN_NULL}
* by default.
@@ -63,6 +63,18 @@ public @interface BeanMapping {
*/
NullValueMappingStrategy nullValueMappingStrategy() default NullValueMappingStrategy.RETURN_NULL;
+ /**
+ * The strategy to be applied when a source bean property is {@code null} or not present. If no strategy is
+ * configured, the strategy given via {@link MapperConfig#nullValuePropertyMappingStrategy()} or
+ * {@link Mapper#nullValuePropertyMappingStrategy()} will be applied,
+ * {@link NullValuePropertyMappingStrategy#SET_TO_NULL} will be used by default.
+ *
+ * @return The strategy to be applied when {@code null} is passed as source property value or the source property
+ * is not present.
+ */
+ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy()
+ default NullValuePropertyMappingStrategy.SET_TO_NULL;
+
/**
* Determines when to include a null check on the source property value of a bean mapping.
*
diff --git a/core/src/main/java/org/mapstruct/Mapper.java b/core/src/main/java/org/mapstruct/Mapper.java
index c86485e94..7c3f8f9e5 100644
--- a/core/src/main/java/org/mapstruct/Mapper.java
+++ b/core/src/main/java/org/mapstruct/Mapper.java
@@ -129,14 +129,25 @@ public @interface Mapper {
CollectionMappingStrategy collectionMappingStrategy() default CollectionMappingStrategy.ACCESSOR_ONLY;
/**
- * The strategy to be applied when {@code null} is passed as source value to the methods of this mapper. If no
- * strategy is configured, the strategy given via {@link MapperConfig#nullValueMappingStrategy()} will be applied,
- * using {@link NullValueMappingStrategy#RETURN_NULL} by default.
+ * The strategy to be applied when {@code null} is passed as source argument value to the methods of this mapper.
+ * If no strategy is configured, the strategy given via {@link MapperConfig#nullValueMappingStrategy()} will be
+ * applied, using {@link NullValueMappingStrategy#RETURN_NULL} by default.
*
* @return The strategy to be applied when {@code null} is passed as source value to the methods of this mapper.
*/
NullValueMappingStrategy nullValueMappingStrategy() default NullValueMappingStrategy.RETURN_NULL;
+ /**
+ * The strategy to be applied when a source bean property is {@code null} or not present. If no strategy is
+ * configured, the strategy given via {@link MapperConfig#nullValuePropertyMappingStrategy()} will be applied,
+ * {@link NullValuePropertyMappingStrategy#SET_TO_NULL} will be used by default.
+ *
+ * @return The strategy to be applied when {@code null} is passed as source property value or the source property
+ * is not present.
+ */
+ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy() default
+ NullValuePropertyMappingStrategy.SET_TO_NULL;
+
/**
* The strategy to use for applying method-level configuration annotations of prototype methods in the interface
* specified with {@link #config()}. Annotations that can be inherited are for example {@link Mapping},
diff --git a/core/src/main/java/org/mapstruct/MapperConfig.java b/core/src/main/java/org/mapstruct/MapperConfig.java
index cbcd15115..85991f527 100644
--- a/core/src/main/java/org/mapstruct/MapperConfig.java
+++ b/core/src/main/java/org/mapstruct/MapperConfig.java
@@ -115,13 +115,23 @@ public @interface MapperConfig {
CollectionMappingStrategy collectionMappingStrategy() default CollectionMappingStrategy.ACCESSOR_ONLY;
/**
- * The strategy to be applied when {@code null} is passed as source value to mapping methods. If no strategy is
- * configured, {@link NullValueMappingStrategy#RETURN_NULL} will be used by default.
+ * The strategy to be applied when {@code null} is passed as source argument value to mapping methods. If no
+ * strategy is configured, {@link NullValueMappingStrategy#RETURN_NULL} will be used by default.
*
* @return The strategy to be applied when {@code null} is passed as source value to mapping methods.
*/
NullValueMappingStrategy nullValueMappingStrategy() default NullValueMappingStrategy.RETURN_NULL;
+ /**
+ * The strategy to be applied when a source bean property is {@code null} or not present. If no strategy is
+ * configured, {@link NullValuePropertyMappingStrategy#SET_TO_NULL} will be used by default.
+ *
+ * @return The strategy to be applied when {@code null} is passed as source property value or the source property
+ * is not present.
+ */
+ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy() default
+ NullValuePropertyMappingStrategy.SET_TO_NULL;
+
/**
* The strategy to use for applying method-level configuration annotations of prototype methods in the interface
* annotated with this annotation. Annotations that can be inherited are for example {@link Mapping},
diff --git a/core/src/main/java/org/mapstruct/Mapping.java b/core/src/main/java/org/mapstruct/Mapping.java
index b6a4dc87e..66f4f825e 100644
--- a/core/src/main/java/org/mapstruct/Mapping.java
+++ b/core/src/main/java/org/mapstruct/Mapping.java
@@ -242,6 +242,7 @@ public @interface Mapping {
* If not possible, MapStruct will try to apply a user defined mapping method.
*
*
+ *
other
*
@@ -267,4 +268,18 @@ public @interface Mapping {
*/
NullValueCheckStrategy nullValueCheckStrategy() default ON_IMPLICIT_CONVERSION;
+ /**
+ * The strategy to be applied when the source property is {@code null} or not present. If no strategy is configured,
+ * the strategy given via {@link MapperConfig#nullValuePropertyMappingStrategy()},
+ * {@link BeanMapping#nullValuePropertyMappingStrategy()} or
+ * {@link Mapper#nullValuePropertyMappingStrategy()} will be applied.
+ *
+ * {@link NullValuePropertyMappingStrategy#SET_TO_NULL} will be used by default.
+ *
+ * @return The strategy to be applied when {@code null} is passed as source property value or the source property
+ * is not present.
+ */
+ NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy()
+ default NullValuePropertyMappingStrategy.SET_TO_NULL;
+
}
diff --git a/core/src/main/java/org/mapstruct/MappingConstants.java b/core/src/main/java/org/mapstruct/MappingConstants.java
index f679d1157..8b0da9a72 100644
--- a/core/src/main/java/org/mapstruct/MappingConstants.java
+++ b/core/src/main/java/org/mapstruct/MappingConstants.java
@@ -16,17 +16,18 @@ public final class MappingConstants {
}
/**
- * Represents a {@code null} source or target.
+ * In an {@link ValueMapping} this represents a {@code null} source or target.
*/
public static final String NULL = "";
/**
- * Represents any source that is not already mapped by either a defined mapping or by means of name based mapping.
+ * In an {@link ValueMapping} this represents any source that is not already mapped by either a defined mapping or
+ * by means of name based mapping.
*/
public static final String ANY_REMAINING = "";
/**
- * Represents any source that is not already mapped by a defined mapping.
+ * In an {@link ValueMapping} this represents any source that is not already mapped by a defined mapping.
*/
public static final String ANY_UNMAPPED = "";
diff --git a/core/src/main/java/org/mapstruct/NullValueCheckStrategy.java b/core/src/main/java/org/mapstruct/NullValueCheckStrategy.java
index 8f31a35f7..8d5b53c00 100644
--- a/core/src/main/java/org/mapstruct/NullValueCheckStrategy.java
+++ b/core/src/main/java/org/mapstruct/NullValueCheckStrategy.java
@@ -33,6 +33,6 @@ public enum NullValueCheckStrategy {
/**
* This option always includes a null check.
*/
- ALWAYS,
+ ALWAYS;
}
diff --git a/core/src/main/java/org/mapstruct/NullValuePropertyMappingStrategy.java b/core/src/main/java/org/mapstruct/NullValuePropertyMappingStrategy.java
new file mode 100644
index 000000000..41ee737d0
--- /dev/null
+++ b/core/src/main/java/org/mapstruct/NullValuePropertyMappingStrategy.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct;
+
+/**
+ * Strategy for dealing with {@code null} or not present properties in the source bean. The
+ * {@link NullValuePropertyMappingStrategy} can be defined on {@link MapperConfig}, {@link Mapper}, {@link BeanMapping}
+ * and {@link Mapping}.
+ * Precedence is arranged in the reverse order. So {@link Mapping} will override {@link BeanMapping}, will
+ * overide {@link Mapper}
+ *
+ * The enum only applies to update method: methods that update a pre-existing target (annotated with
+ * {@code @}{@link MappingTarget}).
+ *
+ * @author Sjaak Derksen
+ */
+public enum NullValuePropertyMappingStrategy {
+
+ /**
+ * If a source bean property equals {@code null} the target bean property will be set explicitly to {@code null}.
+ */
+ SET_TO_NULL,
+
+ /**
+ * If a source bean property equals {@code null} the target bean property will be set to its default value.
+ */
+ SET_TO_DEFAULT,
+
+ /**
+ * If a source bean property equals {@code null} the target bean property will be ignored and retain its
+ * existing value.
+ */
+ IGNORE;
+}
diff --git a/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc b/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc
index 50804de70..bf2e384bf 100644
--- a/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc
+++ b/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc
@@ -2107,6 +2107,20 @@ However, by specifying `nullValueMappingStrategy = NullValueMappingStrategy.RETU
The strategy works in a hierarchical fashion. Setting `nullValueMappingStrategy` on mapping method level will override `@Mapper#nullValueMappingStrategy`, and `@Mapper#nullValueMappingStrategy` will override `@MappingConfig#nullValueMappingStrategy`.
+[[mapping-result-for-null-properties]]
+=== Controlling mapping result for 'null' properties in bean mappings (update mapping methods only).
+
+MapStruct offers control over the property to set in an `@MappingTarget` annotated target bean when the source property equals `null` or the presence check method yields absent.
+
+By default the source property will be set to null. However:
+
+1. By specifying `nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT` on `@Mapping`, `@BeanMapping`, `@Mapper` or `@MappingConfig`, the mapping result can be altered to return *default* values (`Object`, `ArrayList`, `HashMap`).
+
+2. By specifying `nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE` on `@Mapping`, `@BeanMapping`, `@Mapper` or `@MappingConfig`, the mapping result will be equal to the original value of the `@MappingTarget` annotated target.
+
+The strategy works in a hierarchical fashion. Setting `Mapping#nullValuePropertyMappingStrategy` on mapping level will override `nullValuePropertyMappingStrategy` on mapping method level will override `@Mapper#nullValuePropertyMappingStrategy`, and `@Mapper#nullValuePropertyMappingStrategy` will override `@MappingConfig#nullValuePropertyMappingStrategy`.
+
+
[[checking-source-property-for-null-arguments]]
=== Controlling checking result for 'null' properties in bean mapping
@@ -2122,7 +2136,7 @@ First calling a mapping method on the source property is not protected by a null
The option `nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS` will always include a null check when source is non primitive, unless a source presence checker is defined on the source bean.
-The strategy works in a hierarchical fashion. `@Mapper#nullValueCheckStrategy` will override `@MappingConfig#nullValueCheckStrategy`.
+The strategy works in a hierarchical fashion. `@Mapping#nullValueCheckStrategy` will override `@BeanMapping#nullValueCheckStrategy`, `@BeanMapping#nullValueCheckStrategy` will override `@Mapper#nullValueCheckStrategy` and `@Mapper#nullValueCheckStrategy` will override `@MappingConfig#nullValueCheckStrategy`.
[[source-presence-check]]
=== Source presence checking
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java
index 1dff9c196..0cd484122 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java
@@ -477,7 +477,8 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
.dependsOn( mapping.getDependsOn() )
.defaultValue( mapping.getDefaultValue() )
.defaultJavaExpression( mapping.getDefaultJavaExpression() )
- .nullValueCheckStrategyPrism( mapping.getNullValueCheckStrategy() )
+ .nullValueCheckStrategy( mapping.getNullValueCheckStrategy() )
+ .nullValuePropertyMappingStrategy( mapping.getNullValuePropertyMappingStrategy() )
.build();
handledTargets.add( propertyName );
unprocessedSourceParameters.remove( sourceRef.getParameter() );
@@ -594,8 +595,9 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
.existingVariableNames( existingVariableNames )
.dependsOn( mapping != null ? mapping.getDependsOn() : Collections.emptyList() )
.forgeMethodWithMappingOptions( extractAdditionalOptions( targetPropertyName, false ) )
- .nullValueCheckStrategyPrism( mapping != null ? mapping.getNullValueCheckStrategy()
- : null )
+ .nullValueCheckStrategy( mapping != null ? mapping.getNullValueCheckStrategy() : null )
+ .nullValuePropertyMappingStrategy( mapping != null ?
+ mapping.getNullValuePropertyMappingStrategy() : null )
.build();
unprocessedSourceParameters.remove( sourceParameter );
@@ -659,7 +661,9 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
.existingVariableNames( existingVariableNames )
.dependsOn( mapping != null ? mapping.getDependsOn() : Collections.emptyList() )
.forgeMethodWithMappingOptions( extractAdditionalOptions( targetProperty.getKey(), false ) )
- .nullValueCheckStrategyPrism( mapping != null ? mapping.getNullValueCheckStrategy() : null )
+ .nullValueCheckStrategy( mapping != null ? mapping.getNullValueCheckStrategy() : null )
+ .nullValuePropertyMappingStrategy( mapping != null ?
+ mapping.getNullValuePropertyMappingStrategy() : null )
.build();
propertyMappings.add( propertyMapping );
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java
index 7649c373f..dcef911e8 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java
@@ -17,7 +17,7 @@ import org.mapstruct.ap.internal.model.source.Method;
import org.mapstruct.ap.internal.model.source.SelectionParameters;
import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism;
import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism;
-import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism;
+import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism;
import org.mapstruct.ap.internal.util.Message;
import org.mapstruct.ap.internal.util.accessor.Accessor;
@@ -57,7 +57,8 @@ public class CollectionAssignmentBuilder {
private PropertyMapping.TargetWriteAccessorType targetAccessorType;
private Assignment assignment;
private SourceRHS sourceRHS;
- private NullValueCheckStrategyPrism nullValueCheckStrategy;
+ private NullValueCheckStrategyPrism nvcs;
+ private NullValuePropertyMappingStrategyPrism nvpms;
public CollectionAssignmentBuilder mappingBuilderContext(MappingBuilderContext ctx) {
this.ctx = ctx;
@@ -109,8 +110,13 @@ public class CollectionAssignmentBuilder {
return this;
}
- public CollectionAssignmentBuilder nullValueCheckStrategy( NullValueCheckStrategyPrism nullValueCheckStrategy ) {
- this.nullValueCheckStrategy = nullValueCheckStrategy;
+ public CollectionAssignmentBuilder nullValueCheckStrategy( NullValueCheckStrategyPrism nvcs ) {
+ this.nvcs = nvcs;
+ return this;
+ }
+
+ public CollectionAssignmentBuilder nullValuePropertyMappingStrategy( NullValuePropertyMappingStrategyPrism nvpms ) {
+ this.nvpms = nvpms;
return this;
}
@@ -143,7 +149,7 @@ public class CollectionAssignmentBuilder {
PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType ),
targetType,
true,
- mapNullToDefault()
+ nvpms
);
}
else if ( method.isUpdateMethod() && !targetImmutable ) {
@@ -152,22 +158,21 @@ public class CollectionAssignmentBuilder {
result,
method.getThrownTypes(),
targetType,
- nullValueCheckStrategy,
+ nvcs,
+ nvpms,
ctx.getTypeFactory(),
- PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType ),
- mapNullToDefault()
+ PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType )
);
}
else if ( result.getType() == Assignment.AssignmentType.DIRECT ||
- nullValueCheckStrategy == NullValueCheckStrategyPrism.ALWAYS ) {
+ nvcs == NullValueCheckStrategyPrism.ALWAYS ) {
result = new SetterWrapperForCollectionsAndMapsWithNullCheck(
result,
method.getThrownTypes(),
targetType,
ctx.getTypeFactory(),
- PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType ),
- mapNullToDefault()
+ PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType )
);
}
else {
@@ -201,9 +206,4 @@ public class CollectionAssignmentBuilder {
return result;
}
- private boolean mapNullToDefault() {
- return method.getMapperConfiguration().getNullValueMappingStrategy()
- == NullValueMappingStrategyPrism.RETURN_DEFAULT;
- }
-
}
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 10cfbeb88..c8c0b00cd 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
@@ -38,6 +38,7 @@ import org.mapstruct.ap.internal.model.source.SelectionParameters;
import org.mapstruct.ap.internal.model.source.SourceReference;
import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism;
import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism;
+import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism;
import org.mapstruct.ap.internal.util.AccessorNamingUtils;
import org.mapstruct.ap.internal.util.Executables;
import org.mapstruct.ap.internal.util.MapperConfiguration;
@@ -197,7 +198,9 @@ public class PropertyMapping extends ModelElement {
private MappingOptions forgeMethodWithMappingOptions;
private boolean forceUpdateMethod;
private boolean forgedNamedBased = true;
- private NullValueCheckStrategyPrism nullValueCheckStrategyPrism;
+ private NullValueCheckStrategyPrism nvcs;
+ private NullValueMappingStrategyPrism nvms;
+ private NullValuePropertyMappingStrategyPrism nvpms;
PropertyMappingBuilder() {
super( PropertyMappingBuilder.class );
@@ -254,13 +257,33 @@ public class PropertyMapping extends ModelElement {
return this;
}
- public PropertyMappingBuilder nullValueCheckStrategyPrism(
- NullValueCheckStrategyPrism nullValueCheckStrategyPrism) {
- this.nullValueCheckStrategyPrism = nullValueCheckStrategyPrism;
+ public PropertyMappingBuilder nullValueCheckStrategy(NullValueCheckStrategyPrism nvcs ) {
+ this.nvcs = nvcs;
+ return this;
+ }
+
+ public PropertyMappingBuilder nullValuePropertyMappingStrategy( NullValuePropertyMappingStrategyPrism nvpms ) {
+ this.nvpms = nvpms;
return this;
}
public PropertyMapping build() {
+
+ MapperConfiguration mapperConfiguration = method.getMapperConfiguration();
+ BeanMapping beanMapping = method.getMappingOptions().getBeanMapping();
+
+ // null value check strategy (determine true value based on hierarchy)
+ NullValueCheckStrategyPrism nvcsBean = beanMapping != null ? beanMapping.getNullValueCheckStrategy() : null;
+ this.nvcs = mapperConfiguration.getNullValueCheckStrategy( nvcsBean, nvcs );
+
+ // null value mapping strategy
+ this.nvms = mapperConfiguration.getNullValueMappingStrategy();
+
+ // null value property mapping strategy (determine true value based on hierarchy)
+ NullValuePropertyMappingStrategyPrism nvpmsBean =
+ beanMapping != null ? beanMapping.getNullValuePropertyMappingStrategy() : null;
+ this.nvpms = mapperConfiguration.getNullValuePropertyMappingStrategy( nvpmsBean, nvpms );
+
// handle source
this.rightHandSide = getSourceRHS( sourceReference );
rightHandSide.setUseElementAsSourceTypeForMatching(
@@ -425,25 +448,22 @@ public class PropertyMapping extends ModelElement {
);
}
- boolean mapNullToDefault = method.getMapperConfiguration().
- getNullValueMappingStrategy() == NullValueMappingStrategyPrism.RETURN_DEFAULT;
-
Assignment factory = ObjectFactoryMethodResolver
.getFactoryMethod( method, targetType, SelectionParameters.forSourceRHS( rightHandSide ), ctx );
- return new UpdateWrapper( rhs, method.getThrownTypes(), factory, isFieldAssignment(), targetType,
- !rhs.isSourceReferenceParameter(), mapNullToDefault );
+ return new UpdateWrapper(
+ rhs,
+ method.getThrownTypes(),
+ factory, isFieldAssignment(),
+ targetType,
+ !rhs.isSourceReferenceParameter(),
+ nvpms
+ );
}
else {
- return new SetterWrapper( rhs, method.getThrownTypes(), getNvcs(), isFieldAssignment(), targetType );
+ return new SetterWrapper( rhs, method.getThrownTypes(), nvcs, isFieldAssignment(), targetType );
}
}
- private NullValueCheckStrategyPrism getNvcs() {
- BeanMapping beanMapping = method.getMappingOptions().getBeanMapping();
- NullValueCheckStrategyPrism nvcsBean = beanMapping != null ? beanMapping.getNullValueCheckStrategy() : null;
- return method.getMapperConfiguration().getNullValueCheckStrategy( nvcsBean, nullValueCheckStrategyPrism );
- }
-
private Assignment assignToPlainViaAdder( Assignment rightHandSide) {
Assignment result = rightHandSide;
@@ -473,7 +493,8 @@ public class PropertyMapping extends ModelElement {
.targetAccessorType( targetAccessorType )
.rightHandSide( rightHandSide )
.assignment( rhs )
- .nullValueCheckStrategy( getNvcs() )
+ .nullValueCheckStrategy( nvcs )
+ .nullValuePropertyMappingStrategy( nvpms )
.build();
}
@@ -831,14 +852,17 @@ public class PropertyMapping extends ModelElement {
);
}
- boolean mapNullToDefault = method.getMapperConfiguration().
- getNullValueMappingStrategy() == NullValueMappingStrategyPrism.RETURN_DEFAULT;
-
Assignment factoryMethod =
ObjectFactoryMethodResolver.getFactoryMethod( method, targetType, null, ctx );
- assignment = new UpdateWrapper( assignment, method.getThrownTypes(), factoryMethod,
- isFieldAssignment(), targetType, false, mapNullToDefault );
+ assignment = new UpdateWrapper(
+ assignment,
+ method.getThrownTypes(),
+ factoryMethod,
+ isFieldAssignment(),
+ targetType,
+ false,
+ null );
}
else {
assignment = new SetterWrapper( assignment, method.getThrownTypes(), isFieldAssignment() );
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.java
index 01875b7e6..d567b2d28 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.java
@@ -6,13 +6,18 @@
package org.mapstruct.ap.internal.model.assignment;
import static org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism.ALWAYS;
+import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.IGNORE;
+import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.SET_TO_DEFAULT;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import org.mapstruct.ap.internal.model.common.Assignment;
import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.model.common.TypeFactory;
import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism;
+import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism;
/**
* This wrapper handles the situation where an assignment is done for an update method.
@@ -29,28 +34,45 @@ import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism;
public class ExistingInstanceSetterWrapperForCollectionsAndMaps
extends SetterWrapperForCollectionsAndMapsWithNullCheck {
- private final boolean includeSourceNullCheck;
+ private final boolean includeElseBranch;
+ private final boolean mapNullToDefault;
+ private final Type targetType;
public ExistingInstanceSetterWrapperForCollectionsAndMaps(Assignment decoratedAssignment,
List thrownTypesToExclude,
Type targetType,
- NullValueCheckStrategyPrism nvms,
+ NullValueCheckStrategyPrism nvcs,
+ NullValuePropertyMappingStrategyPrism nvpms,
TypeFactory typeFactory,
- boolean fieldAssignment,
- boolean mapNullToDefault) {
+ boolean fieldAssignment) {
super(
decoratedAssignment,
thrownTypesToExclude,
targetType,
typeFactory,
- fieldAssignment,
- mapNullToDefault
+ fieldAssignment
);
- this.includeSourceNullCheck = ALWAYS == nvms;
+ this.mapNullToDefault = SET_TO_DEFAULT == nvpms;
+ this.targetType = targetType;
+ this.includeElseBranch = ALWAYS != nvcs && IGNORE != nvpms;
}
- public boolean isIncludeSourceNullCheck() {
- return includeSourceNullCheck;
+ @Override
+ public Set getImportTypes() {
+ Set imported = new HashSet( super.getImportTypes() );
+ if ( isMapNullToDefault() && ( targetType.getImplementationType() != null ) ) {
+ imported.add( targetType.getImplementationType() );
+ }
+ return imported;
}
+
+ public boolean isIncludeElseBranch() {
+ return includeElseBranch;
+ }
+
+ public boolean isMapNullToDefault() {
+ return mapNullToDefault;
+ }
+
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.java
index 7746a7bed..5e3f4d63f 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.java
@@ -26,14 +26,12 @@ public class SetterWrapperForCollectionsAndMapsWithNullCheck extends WrapperForC
private final Type targetType;
private final TypeFactory typeFactory;
- private final boolean mapNullToDefault;
public SetterWrapperForCollectionsAndMapsWithNullCheck(Assignment decoratedAssignment,
List thrownTypesToExclude,
Type targetType,
TypeFactory typeFactory,
- boolean fieldAssignment,
- boolean mapNullToDefault) {
+ boolean fieldAssignment) {
super(
decoratedAssignment,
thrownTypesToExclude,
@@ -42,7 +40,6 @@ public class SetterWrapperForCollectionsAndMapsWithNullCheck extends WrapperForC
);
this.targetType = targetType;
this.typeFactory = typeFactory;
- this.mapNullToDefault = mapNullToDefault;
}
@Override
@@ -63,9 +60,6 @@ public class SetterWrapperForCollectionsAndMapsWithNullCheck extends WrapperForC
if (isDirectAssignment() || getSourcePresenceCheckerReference() == null ) {
imported.addAll( getNullCheckLocalVarType().getImportTypes() );
}
- if ( isMapNullToDefault() && ( targetType.getImplementationType() != null ) ) {
- imported.add( targetType.getImplementationType() );
- }
return imported;
}
@@ -77,8 +71,4 @@ public class SetterWrapperForCollectionsAndMapsWithNullCheck extends WrapperForC
return "java.util.EnumSet".equals( targetType.getFullyQualifiedName() );
}
- public boolean isMapNullToDefault() {
- return mapNullToDefault;
- }
-
}
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 4de30e32e..8176f6a8f 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
@@ -12,6 +12,10 @@ import java.util.Set;
import org.mapstruct.ap.internal.model.common.Assignment;
import org.mapstruct.ap.internal.model.common.Type;
+import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism;
+
+import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.SET_TO_DEFAULT;
+import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.SET_TO_NULL;
/**
* Wraps the assignment in a target setter.
@@ -24,6 +28,7 @@ public class UpdateWrapper extends AssignmentWrapper {
private final Assignment factoryMethod;
private final Type targetImplementationType;
private final boolean includeSourceNullCheck;
+ private final boolean includeExplicitNullWhenSourceIsNull;
private final boolean mapNullToDefault;
public UpdateWrapper( Assignment decoratedAssignment,
@@ -32,13 +37,14 @@ public class UpdateWrapper extends AssignmentWrapper {
boolean fieldAssignment,
Type targetType,
boolean includeSourceNullCheck,
- boolean mapNullToDefault ) {
+ NullValuePropertyMappingStrategyPrism nvpms) {
super( decoratedAssignment, fieldAssignment );
this.thrownTypesToExclude = thrownTypesToExclude;
this.factoryMethod = factoryMethod;
this.targetImplementationType = determineImplType( factoryMethod, targetType );
this.includeSourceNullCheck = includeSourceNullCheck;
- this.mapNullToDefault = mapNullToDefault;
+ this.mapNullToDefault = nvpms == SET_TO_DEFAULT;
+ this.includeExplicitNullWhenSourceIsNull = nvpms == SET_TO_NULL;
}
private static Type determineImplType(Assignment factoryMethod, Type targetType) {
@@ -91,6 +97,10 @@ public class UpdateWrapper extends AssignmentWrapper {
return includeSourceNullCheck;
}
+ public boolean isIncludeExplicitNullWhenSourceIsNull() {
+ return includeExplicitNullWhenSourceIsNull;
+ }
+
public boolean isMapNullToDefault() {
return mapNullToDefault;
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMapping.java
index ce91d9acd..f2dc9527d 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMapping.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/BeanMapping.java
@@ -15,6 +15,7 @@ import org.mapstruct.ap.internal.prism.BeanMappingPrism;
import org.mapstruct.ap.internal.prism.BuilderPrism;
import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism;
import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism;
+import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism;
import org.mapstruct.ap.internal.prism.ReportingPolicyPrism;
import org.mapstruct.ap.internal.util.FormattingMessager;
import org.mapstruct.ap.internal.util.Message;
@@ -33,6 +34,7 @@ public class BeanMapping {
private final boolean ignoreByDefault;
private final List ignoreUnmappedSourceProperties;
private final BuilderPrism builder;
+ private final NullValuePropertyMappingStrategyPrism nullValuePropertyMappingStrategy;
/**
* creates a mapping for inheritance. Will set ignoreByDefault to false.
@@ -44,6 +46,7 @@ public class BeanMapping {
return new BeanMapping(
map.selectionParameters,
map.nullValueMappingStrategy,
+ map.nullValuePropertyMappingStrategy,
map.nullValueCheckStrategy,
map.reportingPolicy,
false,
@@ -66,6 +69,11 @@ public class BeanMapping {
? null
: NullValueMappingStrategyPrism.valueOf( beanMapping.nullValueMappingStrategy() );
+ NullValuePropertyMappingStrategyPrism nullValuePropertyMappingStrategy =
+ null == beanMapping.values.nullValuePropertyMappingStrategy()
+ ? null
+ : NullValuePropertyMappingStrategyPrism.valueOf( beanMapping.nullValuePropertyMappingStrategy() );
+
NullValueCheckStrategyPrism nullValueCheckStrategy =
null == beanMapping.values.nullValueCheckStrategy()
? null
@@ -79,8 +87,8 @@ public class BeanMapping {
if ( !resultTypeIsDefined && beanMapping.qualifiedBy().isEmpty() && beanMapping.qualifiedByName().isEmpty()
&& beanMapping.ignoreUnmappedSourceProperties().isEmpty()
- && ( nullValueMappingStrategy == null ) && ( nullValueCheckStrategy == null ) && !ignoreByDefault
- && builderMapping == null ) {
+ && ( nullValueMappingStrategy == null ) && ( nullValuePropertyMappingStrategy == null )
+ && ( nullValueCheckStrategy == null ) && !ignoreByDefault && builderMapping == null ) {
messager.printMessage( method, Message.BEANMAPPING_NO_ELEMENTS );
}
@@ -96,6 +104,7 @@ public class BeanMapping {
return new BeanMapping(
cmp,
nullValueMappingStrategy,
+ nullValuePropertyMappingStrategy,
nullValueCheckStrategy,
null,
ignoreByDefault,
@@ -115,6 +124,7 @@ public class BeanMapping {
null,
null,
null,
+ null,
ReportingPolicyPrism.IGNORE,
false,
Collections.emptyList(),
@@ -123,11 +133,12 @@ public class BeanMapping {
}
private BeanMapping(SelectionParameters selectionParameters, NullValueMappingStrategyPrism nvms,
- NullValueCheckStrategyPrism nvcs,
+ NullValuePropertyMappingStrategyPrism nvpms, NullValueCheckStrategyPrism nvcs,
ReportingPolicyPrism reportingPolicy, boolean ignoreByDefault,
List ignoreUnmappedSourceProperties, BuilderPrism builder) {
this.selectionParameters = selectionParameters;
this.nullValueMappingStrategy = nvms;
+ this.nullValuePropertyMappingStrategy = nvpms;
this.nullValueCheckStrategy = nvcs;
this.reportingPolicy = reportingPolicy;
this.ignoreByDefault = ignoreByDefault;
@@ -143,6 +154,10 @@ public class BeanMapping {
return nullValueMappingStrategy;
}
+ public NullValuePropertyMappingStrategyPrism getNullValuePropertyMappingStrategy() {
+ return nullValuePropertyMappingStrategy;
+ }
+
public NullValueCheckStrategyPrism getNullValueCheckStrategy() {
return nullValueCheckStrategy;
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Mapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Mapping.java
index aed9855b5..f9bd4d4a3 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Mapping.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Mapping.java
@@ -27,6 +27,7 @@ import org.mapstruct.ap.internal.model.common.TypeFactory;
import org.mapstruct.ap.internal.prism.MappingPrism;
import org.mapstruct.ap.internal.prism.MappingsPrism;
import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism;
+import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism;
import org.mapstruct.ap.internal.util.AccessorNamingUtils;
import org.mapstruct.ap.internal.util.FormattingMessager;
import org.mapstruct.ap.internal.util.Message;
@@ -58,6 +59,7 @@ public class Mapping {
private final AnnotationValue targetAnnotationValue;
private final AnnotationValue dependsOnAnnotationValue;
private final NullValueCheckStrategyPrism nullValueCheckStrategy;
+ private final NullValuePropertyMappingStrategyPrism nullValuePropertyMappingStrategy;
private SourceReference sourceReference;
private TargetReference targetReference;
@@ -89,70 +91,7 @@ public class Mapping {
public static Mapping fromMappingPrism(MappingPrism mappingPrism, ExecutableElement element,
FormattingMessager messager, Types typeUtils) {
- if ( mappingPrism.target().isEmpty() ) {
- messager.printMessage(
- element,
- mappingPrism.mirror,
- mappingPrism.values.target(),
- Message.PROPERTYMAPPING_EMPTY_TARGET
- );
- return null;
- }
-
- if ( !mappingPrism.source().isEmpty() && mappingPrism.values.constant() != null ) {
- messager.printMessage(
- element,
- mappingPrism.mirror,
- Message.PROPERTYMAPPING_SOURCE_AND_CONSTANT_BOTH_DEFINED );
- return null;
- }
- else if ( !mappingPrism.source().isEmpty() && mappingPrism.values.expression() != null ) {
- messager.printMessage(
- element,
- mappingPrism.mirror,
- Message.PROPERTYMAPPING_SOURCE_AND_EXPRESSION_BOTH_DEFINED );
- return null;
- }
- else if ( mappingPrism.values.expression() != null && mappingPrism.values.constant() != null ) {
- messager.printMessage(
- element,
- mappingPrism.mirror,
- Message.PROPERTYMAPPING_EXPRESSION_AND_CONSTANT_BOTH_DEFINED );
- return null;
- }
- else if ( mappingPrism.values.expression() != null && mappingPrism.values.defaultValue() != null ) {
- messager.printMessage(
- element,
- mappingPrism.mirror,
- Message.PROPERTYMAPPING_EXPRESSION_AND_DEFAULT_VALUE_BOTH_DEFINED );
- return null;
- }
- else if ( mappingPrism.values.constant() != null && mappingPrism.values.defaultValue() != null ) {
- messager.printMessage(
- element,
- mappingPrism.mirror,
- Message.PROPERTYMAPPING_CONSTANT_AND_DEFAULT_VALUE_BOTH_DEFINED );
- return null;
- }
- else if ( mappingPrism.values.expression() != null && mappingPrism.values.defaultExpression() != null) {
- messager.printMessage(
- element,
- mappingPrism.mirror,
- Message.PROPERTYMAPPING_EXPRESSION_AND_DEFAULT_EXPRESSION_BOTH_DEFINED );
- return null;
- }
- else if ( mappingPrism.values.constant() != null && mappingPrism.values.defaultExpression() != null) {
- messager.printMessage(
- element,
- mappingPrism.mirror,
- Message.PROPERTYMAPPING_CONSTANT_AND_DEFAULT_EXPRESSION_BOTH_DEFINED );
- return null;
- }
- else if ( mappingPrism.values.defaultValue() != null && mappingPrism.values.defaultExpression() != null) {
- messager.printMessage(
- element,
- mappingPrism.mirror,
- Message.PROPERTYMAPPING_DEFAULT_VALUE_AND_DEFAULT_EXPRESSION_BOTH_DEFINED );
+ if (!isConsistent( mappingPrism, element, messager ) ) {
return null;
}
@@ -168,7 +107,6 @@ public class Mapping {
List dependsOn =
mappingPrism.dependsOn() != null ? mappingPrism.dependsOn() : Collections.emptyList();
-
FormattingParameters formattingParam = new FormattingParameters(
dateFormat,
numberFormat,
@@ -188,6 +126,11 @@ public class Mapping {
? null
: NullValueCheckStrategyPrism.valueOf( mappingPrism.nullValueCheckStrategy() );
+ NullValuePropertyMappingStrategyPrism nullValuePropertyMappingStrategy =
+ null == mappingPrism.values.nullValuePropertyMappingStrategy()
+ ? null
+ : NullValuePropertyMappingStrategyPrism.valueOf( mappingPrism.nullValuePropertyMappingStrategy() );
+
return new Mapping(
source,
constant,
@@ -203,7 +146,8 @@ public class Mapping {
selectionParams,
mappingPrism.values.dependsOn(),
dependsOn,
- nullValueCheckStrategy
+ nullValueCheckStrategy,
+ nullValuePropertyMappingStrategy
);
}
@@ -223,17 +167,144 @@ public class Mapping {
null,
null,
new ArrayList(),
+ null,
null
);
}
+ private static boolean isConsistent(MappingPrism mappingPrism, ExecutableElement element,
+ FormattingMessager messager) {
+
+ if ( mappingPrism.target().isEmpty() ) {
+ messager.printMessage(
+ element,
+ mappingPrism.mirror,
+ mappingPrism.values.target(),
+ Message.PROPERTYMAPPING_EMPTY_TARGET
+ );
+ return false;
+ }
+
+ if ( !mappingPrism.source().isEmpty() && mappingPrism.values.constant() != null ) {
+ messager.printMessage(
+ element,
+ mappingPrism.mirror,
+ Message.PROPERTYMAPPING_SOURCE_AND_CONSTANT_BOTH_DEFINED
+ );
+ return false;
+ }
+ else if ( !mappingPrism.source().isEmpty() && mappingPrism.values.expression() != null ) {
+ messager.printMessage(
+ element,
+ mappingPrism.mirror,
+ Message.PROPERTYMAPPING_SOURCE_AND_EXPRESSION_BOTH_DEFINED
+ );
+ return false;
+ }
+ else if ( mappingPrism.values.expression() != null && mappingPrism.values.constant() != null ) {
+ messager.printMessage(
+ element,
+ mappingPrism.mirror,
+ Message.PROPERTYMAPPING_EXPRESSION_AND_CONSTANT_BOTH_DEFINED
+ );
+ return false;
+ }
+ else if ( mappingPrism.values.expression() != null && mappingPrism.values.defaultValue() != null ) {
+ messager.printMessage(
+ element,
+ mappingPrism.mirror,
+ Message.PROPERTYMAPPING_EXPRESSION_AND_DEFAULT_VALUE_BOTH_DEFINED
+ );
+ return false;
+ }
+ else if ( mappingPrism.values.constant() != null && mappingPrism.values.defaultValue() != null ) {
+ messager.printMessage(
+ element,
+ mappingPrism.mirror,
+ Message.PROPERTYMAPPING_CONSTANT_AND_DEFAULT_VALUE_BOTH_DEFINED
+ );
+ return false;
+ }
+ else if ( mappingPrism.values.expression() != null && mappingPrism.values.defaultExpression() != null ) {
+ messager.printMessage(
+ element,
+ mappingPrism.mirror,
+ Message.PROPERTYMAPPING_EXPRESSION_AND_DEFAULT_EXPRESSION_BOTH_DEFINED
+ );
+ return false;
+ }
+ else if ( mappingPrism.values.constant() != null && mappingPrism.values.defaultExpression() != null ) {
+ messager.printMessage(
+ element,
+ mappingPrism.mirror,
+ Message.PROPERTYMAPPING_CONSTANT_AND_DEFAULT_EXPRESSION_BOTH_DEFINED
+ );
+ return false;
+ }
+ else if ( mappingPrism.values.defaultValue() != null && mappingPrism.values.defaultExpression() != null ) {
+ messager.printMessage(
+ element,
+ mappingPrism.mirror,
+ Message.PROPERTYMAPPING_DEFAULT_VALUE_AND_DEFAULT_EXPRESSION_BOTH_DEFINED
+ );
+ return false;
+ }
+ else if ( mappingPrism.values.nullValuePropertyMappingStrategy() != null
+ && mappingPrism.values.defaultValue() != null ) {
+ messager.printMessage(
+ element,
+ mappingPrism.mirror,
+ Message.PROPERTYMAPPING_DEFAULT_VALUE_AND_NVPMS
+ );
+ return false;
+ }
+ else if ( mappingPrism.values.nullValuePropertyMappingStrategy() != null
+ && mappingPrism.values.constant() != null ) {
+ messager.printMessage(
+ element,
+ mappingPrism.mirror,
+ Message.PROPERTYMAPPING_CONSTANT_VALUE_AND_NVPMS
+ );
+ return false;
+ }
+ else if ( mappingPrism.values.nullValuePropertyMappingStrategy() != null
+ && mappingPrism.values.expression() != null ) {
+ messager.printMessage(
+ element,
+ mappingPrism.mirror,
+ Message.PROPERTYMAPPING_EXPRESSION_VALUE_AND_NVPMS
+ );
+ return false;
+ }
+ else if ( mappingPrism.values.nullValuePropertyMappingStrategy() != null
+ && mappingPrism.values.defaultExpression() != null ) {
+ messager.printMessage(
+ element,
+ mappingPrism.mirror,
+ Message.PROPERTYMAPPING_DEFAULT_EXPERSSION_AND_NVPMS
+ );
+ return false;
+ }
+ else if ( mappingPrism.values.nullValuePropertyMappingStrategy() != null
+ && mappingPrism.ignore() != null && mappingPrism.ignore() ) {
+ messager.printMessage(
+ element,
+ mappingPrism.mirror,
+ Message.PROPERTYMAPPING_IGNORE_AND_NVPMS
+ );
+ return false;
+ }
+ return true;
+ }
+
@SuppressWarnings("checkstyle:parameternumber")
private Mapping( String sourceName, String constant, String javaExpression, String defaultJavaExpression,
String targetName, String defaultValue, boolean isIgnored, AnnotationMirror mirror,
AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue,
FormattingParameters formattingParameters, SelectionParameters selectionParameters,
AnnotationValue dependsOnAnnotationValue, List dependsOn,
- NullValueCheckStrategyPrism nullValueCheckStrategy ) {
+ NullValueCheckStrategyPrism nullValueCheckStrategy,
+ NullValuePropertyMappingStrategyPrism nullValuePropertyMappingStrategy ) {
this.sourceName = sourceName;
this.constant = constant;
this.javaExpression = javaExpression;
@@ -249,6 +320,7 @@ public class Mapping {
this.dependsOnAnnotationValue = dependsOnAnnotationValue;
this.dependsOn = dependsOn;
this.nullValueCheckStrategy = nullValueCheckStrategy;
+ this.nullValuePropertyMappingStrategy = nullValuePropertyMappingStrategy;
}
private Mapping( Mapping mapping, TargetReference targetReference ) {
@@ -269,6 +341,7 @@ public class Mapping {
this.sourceReference = mapping.sourceReference;
this.targetReference = targetReference;
this.nullValueCheckStrategy = mapping.nullValueCheckStrategy;
+ this.nullValuePropertyMappingStrategy = mapping.nullValuePropertyMappingStrategy;
}
private Mapping( Mapping mapping, SourceReference sourceReference ) {
@@ -289,6 +362,7 @@ public class Mapping {
this.sourceReference = sourceReference;
this.targetReference = mapping.targetReference;
this.nullValueCheckStrategy = mapping.nullValueCheckStrategy;
+ this.nullValuePropertyMappingStrategy = mapping.nullValuePropertyMappingStrategy;
}
private static String getExpression(MappingPrism mappingPrism, ExecutableElement element,
@@ -458,6 +532,10 @@ public class Mapping {
return nullValueCheckStrategy;
}
+ public NullValuePropertyMappingStrategyPrism getNullValuePropertyMappingStrategy() {
+ return nullValuePropertyMappingStrategy;
+ }
+
public Mapping popTargetReference() {
if ( targetReference != null ) {
TargetReference newTargetReference = targetReference.pop();
@@ -506,7 +584,8 @@ public class Mapping {
selectionParameters,
dependsOnAnnotationValue,
Collections.emptyList(),
- nullValueCheckStrategy
+ nullValueCheckStrategy,
+ nullValuePropertyMappingStrategy
);
reverse.init(
@@ -548,7 +627,8 @@ public class Mapping {
selectionParameters,
dependsOnAnnotationValue,
dependsOn,
- nullValueCheckStrategy
+ nullValueCheckStrategy,
+ nullValuePropertyMappingStrategy
);
if ( sourceReference != null ) {
@@ -568,5 +648,6 @@ public class Mapping {
"\n targetName='" + targetName + "\'," +
"\n}";
}
+
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/prism/MappingConstantsPrism.java b/processor/src/main/java/org/mapstruct/ap/internal/prism/MappingConstantsPrism.java
index 959f571bb..c03bac6a7 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/prism/MappingConstantsPrism.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/prism/MappingConstantsPrism.java
@@ -20,5 +20,4 @@ public final class MappingConstantsPrism {
public static final String ANY_REMAINING = "";
public static final String ANY_UNMAPPED = "";
-
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/prism/NullValuePropertyMappingStrategyPrism.java b/processor/src/main/java/org/mapstruct/ap/internal/prism/NullValuePropertyMappingStrategyPrism.java
new file mode 100644
index 000000000..bc0cdae9e
--- /dev/null
+++ b/processor/src/main/java/org/mapstruct/ap/internal/prism/NullValuePropertyMappingStrategyPrism.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.internal.prism;
+
+
+/**
+ * Prism for the enum {@link org.mapstruct.NullValuePropertyMappingStrategy}
+ *
+ * @author Sjaak Derksen
+ */
+public enum NullValuePropertyMappingStrategyPrism {
+
+ SET_TO_NULL,
+ SET_TO_DEFAULT,
+ IGNORE;
+}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/MapperConfiguration.java b/processor/src/main/java/org/mapstruct/ap/internal/util/MapperConfiguration.java
index d92d0c17e..49b0aded8 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/util/MapperConfiguration.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/util/MapperConfiguration.java
@@ -22,6 +22,7 @@ import org.mapstruct.ap.internal.prism.MapperPrism;
import org.mapstruct.ap.internal.prism.MappingInheritanceStrategyPrism;
import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism;
import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism;
+import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism;
import org.mapstruct.ap.internal.prism.ReportingPolicyPrism;
/**
@@ -163,6 +164,25 @@ public class MapperConfiguration {
}
}
+ public NullValuePropertyMappingStrategyPrism getNullValuePropertyMappingStrategy(
+ NullValuePropertyMappingStrategyPrism beanPrism,
+ NullValuePropertyMappingStrategyPrism mappingPrism) {
+ if ( mappingPrism != null ) {
+ return mappingPrism;
+ }
+ else if ( beanPrism != null ) {
+ return beanPrism;
+ }
+ else if ( mapperConfigPrism != null && mapperPrism.values.nullValueCheckStrategy() == null ) {
+ return NullValuePropertyMappingStrategyPrism.valueOf(
+ mapperConfigPrism.nullValuePropertyMappingStrategy()
+ );
+ }
+ else {
+ return NullValuePropertyMappingStrategyPrism.valueOf( mapperPrism.nullValuePropertyMappingStrategy() );
+ }
+ }
+
public InjectionStrategyPrism getInjectionStrategy() {
if ( mapperConfigPrism != null && mapperPrism.values.injectionStrategy() == null ) {
return InjectionStrategyPrism.valueOf( mapperConfigPrism.injectionStrategy() );
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java
index 155ea39a5..445c0b3b0 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java
@@ -15,7 +15,7 @@ import javax.tools.Diagnostic;
public enum Message {
// CHECKSTYLE:OFF
- BEANMAPPING_NO_ELEMENTS( "'nullValueMappingStrategy', 'resultType' and 'qualifiedBy' are undefined in @BeanMapping, define at least one of them." ),
+ BEANMAPPING_NO_ELEMENTS( "'nullValueMappingStrategy', 'nullValuePropertyMappingStrategy', 'resultType' and 'qualifiedBy' are undefined in @BeanMapping, define at least one of them." ),
BEANMAPPING_NOT_ASSIGNABLE( "%s not assignable to: %s." ),
BEANMAPPING_ABSTRACT( "The result type %s may not be an abstract class nor interface." ),
BEANMAPPING_UNKNOWN_PROPERTY_IN_RESULTTYPE( "Unknown property \"%s\" in result type %s. Did you mean \"%s\"?" ),
@@ -44,6 +44,11 @@ public enum Message {
PROPERTYMAPPING_EXPRESSION_AND_DEFAULT_EXPRESSION_BOTH_DEFINED( "Expression and default expression are both defined in @Mapping, either define an expression or a default expression." ),
PROPERTYMAPPING_CONSTANT_AND_DEFAULT_EXPRESSION_BOTH_DEFINED( "Constant and default expression are both defined in @Mapping, either define a constant or a default expression." ),
PROPERTYMAPPING_DEFAULT_VALUE_AND_DEFAULT_EXPRESSION_BOTH_DEFINED( "Default value and default expression are both defined in @Mapping, either define a default value or a default expression." ),
+ PROPERTYMAPPING_DEFAULT_VALUE_AND_NVPMS( "Default value and nullValuePropertyMappingStrategy are both defined in @Mapping, either define a defaultValue or an nullValuePropertyMappingStrategy." ),
+ PROPERTYMAPPING_EXPRESSION_VALUE_AND_NVPMS( "Expression and nullValuePropertyMappingStrategy are both defined in @Mapping, either define an expression or an nullValuePropertyMappingStrategy." ),
+ PROPERTYMAPPING_CONSTANT_VALUE_AND_NVPMS( "Constant and nullValuePropertyMappingStrategy are both defined in @Mapping, either define a constant or an nullValuePropertyMappingStrategy." ),
+ PROPERTYMAPPING_DEFAULT_EXPERSSION_AND_NVPMS( "DefaultExpression and nullValuePropertyMappingStrategy are both defined in @Mapping, either define a defaultExpression or an nullValuePropertyMappingStrategy." ),
+ PROPERTYMAPPING_IGNORE_AND_NVPMS( "Ignore and nullValuePropertyMappingStrategy are both defined in @Mapping, either define ignore or an nullValuePropertyMappingStrategy." ),
PROPERTYMAPPING_INVALID_EXPRESSION( "Value for expression must be given in the form \"java()\"." ),
PROPERTYMAPPING_INVALID_DEFAULT_EXPRESSION( "Value for default expression must be given in the form \"java()\"." ),
PROPERTYMAPPING_INVALID_PARAMETER_NAME( "Method has no source parameter named \"%s\". Method source parameters are: \"%s\"." ),
diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.ftl
index 17eb9de51..cee722e2d 100644
--- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.ftl
+++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.ftl
@@ -14,7 +14,7 @@
${ext.targetBeanName}.${ext.targetReadAccessorName}.clear();
${ext.targetBeanName}.${ext.targetReadAccessorName}.<#if ext.targetType.collectionType>addAll<#else>putAll#if>( <@lib.handleWithAssignmentOrNullCheckVar/> );
@lib.handleLocalVarNullCheck>
- <#if !ext.defaultValueAssignment?? && !sourcePresenceCheckerReference?? && !includeSourceNullCheck>else {<#-- the opposite (defaultValueAssignment) case is handeld inside lib.handleLocalVarNullCheck -->
+ <#if !ext.defaultValueAssignment?? && !sourcePresenceCheckerReference?? && includeElseBranch>else {<#-- the opposite (defaultValueAssignment) case is handeld inside lib.handleLocalVarNullCheck -->
${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if mapNullToDefault><@lib.initTargetObject/><#else>null#if>@lib.handleWrite>;
}
#if>
diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.ftl
index 6e38d7fbf..c26557af9 100644
--- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.ftl
+++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.ftl
@@ -10,10 +10,6 @@
<@lib.sourceLocalVarAssignment/>
<@lib.handleExceptions>
<@callTargetWriteAccessor/>
- <#if !ext.defaultValueAssignment??>else {<#-- the opposite (defaultValueAssignment) case is handeld inside lib.handleLocalVarNullCheck -->
- ${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if mapNullToDefault><@lib.initTargetObject/><#else>null#if>@lib.handleWrite>;
- }
- #if>
@lib.handleExceptions>
<#--
assigns the target via the regular target write accessor (usually the setter)
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 833120b01..9dfbc0858 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
@@ -13,9 +13,11 @@
<@assignToExistingTarget/>
<@lib.handleAssignment/>;
}
+ <#if mapNullToDefault || includeExplicitNullWhenSourceIsNull>
else {
${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if mapNullToDefault><@lib.initTargetObject/><#else>null#if>@lib.handleWrite>;
}
+ #if>
<#else>
<@assignToExistingTarget/>
<@lib.handleAssignment/>;
diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1273/Dto.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1273/Dto.java
index d91aaa827..9c912c221 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1273/Dto.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1273/Dto.java
@@ -5,12 +5,11 @@
*/
package org.mapstruct.ap.test.bugs._1273;
-import java.util.ArrayList;
import java.util.List;
public class Dto {
- List longs = new ArrayList();
+ List longs;
public List getLongs() {
return longs;
diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1273/EntityMapperReturnDefault.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1273/EntityMapperReturnDefault.java
index 3f0d1e003..0b31e1d73 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1273/EntityMapperReturnDefault.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1273/EntityMapperReturnDefault.java
@@ -5,12 +5,20 @@
*/
package org.mapstruct.ap.test.bugs._1273;
-import org.mapstruct.Mapper;
-import org.mapstruct.NullValueMappingStrategy;
+import java.util.ArrayList;
-@Mapper( nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT )
+import org.mapstruct.Mapper;
+import org.mapstruct.ObjectFactory;
+
+@Mapper
public interface EntityMapperReturnDefault {
Dto asTarget(Entity entity);
+ @ObjectFactory
+ default Dto createDto() {
+ Dto result = new Dto();
+ result.setLongs( new ArrayList<>( ) );
+ return result;
+ }
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNcvsAlwaysMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNcvsAlwaysMapper.java
index f7a995a35..11be51583 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNcvsAlwaysMapper.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNcvsAlwaysMapper.java
@@ -5,12 +5,15 @@
*/
package org.mapstruct.ap.test.bugs._913;
+import org.mapstruct.BeanMapping;
import org.mapstruct.InheritConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
+import org.mapstruct.Named;
import org.mapstruct.NullValueCheckStrategy;
+import org.mapstruct.ObjectFactory;
import org.mapstruct.factory.Mappers;
/**
@@ -22,6 +25,7 @@ public interface DomainDtoWithNcvsAlwaysMapper {
DomainDtoWithNcvsAlwaysMapper INSTANCE = Mappers.getMapper( DomainDtoWithNcvsAlwaysMapper.class );
+ @BeanMapping( qualifiedByName = "DomainObjectFactory" )
@Mappings({
@Mapping(target = "strings", source = "strings"),
@Mapping(target = "longs", source = "strings"),
@@ -36,4 +40,13 @@ public interface DomainDtoWithNcvsAlwaysMapper {
@InheritConfiguration( name = "create" )
Domain updateWithReturn(DtoWithPresenceCheck source, @MappingTarget Domain target);
+
+ @ObjectFactory
+ @Named( "DomainObjectFactory" )
+ default Domain createNullDomain() {
+ Domain domain = new Domain();
+ domain.setLongs( null );
+ domain.setStrings( null );
+ return domain;
+ }
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapper.java
index 7fef9d8b9..7cd3cfede 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapper.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapper.java
@@ -11,13 +11,17 @@ import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
import org.mapstruct.NullValueMappingStrategy;
+import org.mapstruct.NullValuePropertyMappingStrategy;
import org.mapstruct.factory.Mappers;
/**
*
* @author Sjaak Derksen
*/
-@Mapper( nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT, uses = Helper.class )
+@Mapper(
+ nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT,
+ nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT,
+ uses = Helper.class )
public interface DomainDtoWithNvmsDefaultMapper {
DomainDtoWithNvmsDefaultMapper INSTANCE = Mappers.getMapper( DomainDtoWithNvmsDefaultMapper.class );
diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsNullMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsNullMapper.java
index 4d6fd9eff..d288b666f 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsNullMapper.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsNullMapper.java
@@ -5,11 +5,14 @@
*/
package org.mapstruct.ap.test.bugs._913;
+import org.mapstruct.BeanMapping;
import org.mapstruct.InheritConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
+import org.mapstruct.Named;
+import org.mapstruct.ObjectFactory;
import org.mapstruct.factory.Mappers;
/**
@@ -22,6 +25,7 @@ public interface DomainDtoWithNvmsNullMapper {
DomainDtoWithNvmsNullMapper INSTANCE = Mappers.getMapper( DomainDtoWithNvmsNullMapper.class );
+ @BeanMapping( qualifiedByName = "DomainObjectFactory" )
@Mappings({
@Mapping(target = "strings", source = "strings"),
@Mapping(target = "longs", source = "strings"),
@@ -36,4 +40,13 @@ public interface DomainDtoWithNvmsNullMapper {
@InheritConfiguration( name = "create" )
Domain updateWithReturn(Dto source, @MappingTarget Domain target);
+
+ @ObjectFactory
+ @Named( "DomainObjectFactory" )
+ default Domain createNullDomain() {
+ Domain domain = new Domain();
+ domain.setLongs( null );
+ domain.setStrings( null );
+ return domain;
+ }
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapper.java
index ddb0a60ce..83b8539a4 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapper.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapper.java
@@ -5,11 +5,14 @@
*/
package org.mapstruct.ap.test.bugs._913;
+import org.mapstruct.BeanMapping;
import org.mapstruct.InheritConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
+import org.mapstruct.Named;
+import org.mapstruct.ObjectFactory;
import org.mapstruct.factory.Mappers;
/**
@@ -22,6 +25,7 @@ public interface DomainDtoWithPresenceCheckMapper {
DomainDtoWithPresenceCheckMapper INSTANCE = Mappers.getMapper( DomainDtoWithPresenceCheckMapper.class );
+ @BeanMapping( qualifiedByName = "DomainObjectFactory" )
@Mappings({
@Mapping(target = "strings", source = "strings"),
@Mapping(target = "longs", source = "strings"),
@@ -36,4 +40,13 @@ public interface DomainDtoWithPresenceCheckMapper {
@InheritConfiguration( name = "create" )
Domain updateWithReturn(DtoWithPresenceCheck source, @MappingTarget Domain target);
+
+ @ObjectFactory
+ @Named( "DomainObjectFactory" )
+ default Domain createNullDomain() {
+ Domain domain = new Domain();
+ domain.setLongs( null );
+ domain.setStrings( null );
+ return domain;
+ }
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/Address.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/Address.java
new file mode 100644
index 000000000..86c76f4d5
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/Address.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.nullvaluepropertymapping;
+
+public class Address {
+
+ private Integer houseNumber;
+
+ public Integer getHouseNumber() {
+ return houseNumber;
+ }
+
+ public void setHouseNumber(Integer houseNumber) {
+ this.houseNumber = houseNumber;
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/AddressDTO.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/AddressDTO.java
new file mode 100644
index 000000000..3fca3b046
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/AddressDTO.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.nullvaluepropertymapping;
+
+public class AddressDTO {
+
+ private Integer houseNo;
+
+ public Integer getHouseNo() {
+ return houseNo;
+ }
+
+ public void setHouseNo(Integer houseNo) {
+ this.houseNo = houseNo;
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/Customer.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/Customer.java
new file mode 100644
index 000000000..ac750aeb5
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/Customer.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.nullvaluepropertymapping;
+
+import java.util.List;
+
+public class Customer {
+
+ private Address address;
+ private List details;
+
+ public Address getAddress() {
+ return address;
+ }
+
+ public void setAddress(Address address) {
+ this.address = address;
+ }
+
+ public List getDetails() {
+ return details;
+ }
+
+ public void setDetails(List details) {
+ this.details = details;
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerDTO.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerDTO.java
new file mode 100644
index 000000000..b2475cd4c
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerDTO.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.nullvaluepropertymapping;
+
+import java.util.List;
+
+public class CustomerDTO {
+
+ private AddressDTO address;
+ private List details;
+
+ public AddressDTO getAddress() {
+ return address;
+ }
+
+ public void setAddress(AddressDTO address) {
+ this.address = address;
+ }
+
+ public List getDetails() {
+ return details;
+ }
+
+ public void setDetails(List details) {
+ this.details = details;
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerDefaultMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerDefaultMapper.java
new file mode 100644
index 000000000..019327ede
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerDefaultMapper.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.nullvaluepropertymapping;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.NullValuePropertyMappingStrategy;
+import org.mapstruct.factory.Mappers;
+
+@Mapper(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT)
+public interface CustomerDefaultMapper {
+
+ CustomerDefaultMapper INSTANCE = Mappers.getMapper( CustomerDefaultMapper.class );
+
+ @Mapping(source = "address", target = "homeDTO.addressDTO")
+ void mapCustomer(Customer customer, @MappingTarget UserDTO userDTO);
+
+ @Mapping(source = "houseNumber", target = "houseNo")
+ void mapCustomerHouse(Address address, @MappingTarget AddressDTO addrDTO);
+
+}
+
diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerMapper.java
new file mode 100644
index 000000000..56b930c51
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerMapper.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.nullvaluepropertymapping;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.NullValuePropertyMappingStrategy;
+import org.mapstruct.factory.Mappers;
+
+@Mapper(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
+public interface CustomerMapper {
+
+ CustomerMapper INSTANCE = Mappers.getMapper( CustomerMapper.class );
+
+ @Mapping(source = "address", target = "homeDTO.addressDTO")
+ void mapCustomer(Customer customer, @MappingTarget UserDTO userDTO);
+
+ @Mapping(source = "houseNumber", target = "houseNo")
+ void mapCustomerHouse(Address address, @MappingTarget AddressDTO addrDTO);
+
+}
+
diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsOnBeanMappingMethodMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsOnBeanMappingMethodMapper.java
new file mode 100644
index 000000000..780108463
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsOnBeanMappingMethodMapper.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.nullvaluepropertymapping;
+
+import org.mapstruct.BeanMapping;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.NullValuePropertyMappingStrategy;
+import org.mapstruct.factory.Mappers;
+
+@Mapper
+public interface CustomerNvpmsOnBeanMappingMethodMapper {
+
+ CustomerNvpmsOnBeanMappingMethodMapper INSTANCE = Mappers.getMapper( CustomerNvpmsOnBeanMappingMethodMapper.class );
+
+ @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
+ void map(Customer customer, @MappingTarget CustomerDTO mappingTarget);
+
+ @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
+ @Mapping(source = "houseNumber", target = "houseNo")
+ void mapCustomerHouse(Address address, @MappingTarget AddressDTO addrDTO);
+
+}
+
diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsOnConfigMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsOnConfigMapper.java
new file mode 100644
index 000000000..941b7d2b4
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsOnConfigMapper.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.nullvaluepropertymapping;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.factory.Mappers;
+
+@Mapper(config = NvpmsConfig.class)
+public interface CustomerNvpmsOnConfigMapper {
+
+ CustomerNvpmsOnConfigMapper INSTANCE = Mappers.getMapper( CustomerNvpmsOnConfigMapper.class );
+
+ void map(Customer customer, @MappingTarget CustomerDTO mappingTarget);
+
+ @Mapping(source = "houseNumber", target = "houseNo")
+ void mapCustomerHouse(Address address, @MappingTarget AddressDTO addrDTO);
+
+}
+
diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsOnMapperMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsOnMapperMapper.java
new file mode 100644
index 000000000..592a687c6
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsOnMapperMapper.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.nullvaluepropertymapping;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.NullValuePropertyMappingStrategy;
+import org.mapstruct.factory.Mappers;
+
+@Mapper(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
+public interface CustomerNvpmsOnMapperMapper {
+
+ CustomerNvpmsOnMapperMapper INSTANCE = Mappers.getMapper( CustomerNvpmsOnMapperMapper.class );
+
+ void map(Customer customer, @MappingTarget CustomerDTO mappingTarget);
+
+ @Mapping(source = "houseNumber", target = "houseNo")
+ void mapCustomerHouse(Address address, @MappingTarget AddressDTO addrDTO);
+
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsPropertyMappingMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsPropertyMappingMapper.java
new file mode 100644
index 000000000..28eaf8f33
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/CustomerNvpmsPropertyMappingMapper.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.nullvaluepropertymapping;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.factory.Mappers;
+
+import static org.mapstruct.NullValuePropertyMappingStrategy.IGNORE;
+
+@Mapper
+public interface CustomerNvpmsPropertyMappingMapper {
+
+ CustomerNvpmsPropertyMappingMapper INSTANCE = Mappers.getMapper( CustomerNvpmsPropertyMappingMapper.class );
+
+ @Mapping( target = "address", nullValuePropertyMappingStrategy = IGNORE)
+ @Mapping( target = "details", nullValuePropertyMappingStrategy = IGNORE)
+ void map(Customer customer, @MappingTarget CustomerDTO mappingTarget);
+
+ @Mapping(source = "houseNumber", target = "houseNo")
+ void mapCustomerHouse(Address address, @MappingTarget AddressDTO addrDTO);
+
+}
+
diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper1.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper1.java
new file mode 100644
index 000000000..3c2dd5134
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper1.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.nullvaluepropertymapping;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.factory.Mappers;
+
+import static org.mapstruct.NullValuePropertyMappingStrategy.IGNORE;
+
+@Mapper
+public interface ErroneousCustomerMapper1 {
+
+ ErroneousCustomerMapper1 INSTANCE = Mappers.getMapper( ErroneousCustomerMapper1.class );
+
+ @Mapping(target = "details", defaultValue = "test", nullValuePropertyMappingStrategy = IGNORE)
+ @Mapping(target = "address", nullValuePropertyMappingStrategy = IGNORE)
+ void map(Customer customer, @MappingTarget CustomerDTO mappingTarget);
+
+ @Mapping(source = "houseNumber", target = "houseNo")
+ void mapCustomerHouse(Address address, @MappingTarget AddressDTO addrDTO);
+
+}
+
diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper2.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper2.java
new file mode 100644
index 000000000..4b7f2bec7
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper2.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.nullvaluepropertymapping;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.factory.Mappers;
+
+import static org.mapstruct.NullValuePropertyMappingStrategy.IGNORE;
+
+@Mapper
+public interface ErroneousCustomerMapper2 {
+
+ ErroneousCustomerMapper2 INSTANCE = Mappers.getMapper( ErroneousCustomerMapper2.class );
+
+ @Mapping(target = "details", expression = "java(getTest())", nullValuePropertyMappingStrategy = IGNORE)
+ @Mapping(target = "address", nullValuePropertyMappingStrategy = IGNORE)
+ void map(Customer customer, @MappingTarget CustomerDTO mappingTarget);
+
+ @Mapping(source = "houseNumber", target = "houseNo")
+ void mapCustomerHouse(Address address, @MappingTarget AddressDTO addrDTO);
+
+}
+
diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper3.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper3.java
new file mode 100644
index 000000000..755b46853
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper3.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.nullvaluepropertymapping;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.factory.Mappers;
+
+import static org.mapstruct.NullValuePropertyMappingStrategy.IGNORE;
+
+@Mapper
+public interface ErroneousCustomerMapper3 {
+
+ ErroneousCustomerMapper3 INSTANCE = Mappers.getMapper( ErroneousCustomerMapper3.class );
+
+ @Mapping(target = "details", defaultExpression = "java(getTest())", nullValuePropertyMappingStrategy = IGNORE)
+ @Mapping(target = "address", nullValuePropertyMappingStrategy = IGNORE)
+ void map(Customer customer, @MappingTarget CustomerDTO mappingTarget);
+
+ @Mapping(source = "houseNumber", target = "houseNo")
+ void mapCustomerHouse(Address address, @MappingTarget AddressDTO addrDTO);
+
+}
+
diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper4.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper4.java
new file mode 100644
index 000000000..88fe1a7cb
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper4.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.nullvaluepropertymapping;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.factory.Mappers;
+
+import static org.mapstruct.NullValuePropertyMappingStrategy.IGNORE;
+
+@Mapper
+public interface ErroneousCustomerMapper4 {
+
+ ErroneousCustomerMapper4 INSTANCE = Mappers.getMapper( ErroneousCustomerMapper4.class );
+
+ @Mapping(target = "details", constant = "test", nullValuePropertyMappingStrategy = IGNORE)
+ @Mapping(target = "address", nullValuePropertyMappingStrategy = IGNORE)
+ void map(Customer customer, @MappingTarget CustomerDTO mappingTarget);
+
+ @Mapping(source = "houseNumber", target = "houseNo")
+ void mapCustomerHouse(Address address, @MappingTarget AddressDTO addrDTO);
+
+}
+
diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper5.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper5.java
new file mode 100644
index 000000000..05ed1c1fe
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/ErroneousCustomerMapper5.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.nullvaluepropertymapping;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.factory.Mappers;
+
+import static org.mapstruct.NullValuePropertyMappingStrategy.IGNORE;
+
+@Mapper
+public interface ErroneousCustomerMapper5 {
+
+ ErroneousCustomerMapper5 INSTANCE = Mappers.getMapper( ErroneousCustomerMapper5.class );
+
+ @Mapping(target = "details", ignore = true, nullValuePropertyMappingStrategy = IGNORE)
+ @Mapping(target = "address", nullValuePropertyMappingStrategy = IGNORE)
+ void map(Customer customer, @MappingTarget CustomerDTO mappingTarget);
+
+ @Mapping(source = "houseNumber", target = "houseNo")
+ void mapCustomerHouse(Address address, @MappingTarget AddressDTO addrDTO);
+
+}
+
diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/HomeDTO.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/HomeDTO.java
new file mode 100644
index 000000000..ad283e5ef
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/HomeDTO.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.nullvaluepropertymapping;
+
+public class HomeDTO {
+
+ private AddressDTO addressDTO;
+
+ public AddressDTO getAddressDTO() {
+ return addressDTO;
+ }
+
+ public void setAddressDTO(AddressDTO addressDTO) {
+ this.addressDTO = addressDTO;
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/NullValuePropertyMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/NullValuePropertyMappingTest.java
new file mode 100644
index 000000000..63bb6f92e
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/NullValuePropertyMappingTest.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.nullvaluepropertymapping;
+
+import java.util.Arrays;
+import java.util.function.BiConsumer;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mapstruct.ap.testutil.IssueKey;
+import org.mapstruct.ap.testutil.WithClasses;
+import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
+import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
+import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
+import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author Sjaak Derksen
+ */
+@IssueKey("1306")
+@RunWith(AnnotationProcessorTestRunner.class)
+@WithClasses({
+ Address.class,
+ Customer.class,
+ CustomerDTO.class,
+ AddressDTO.class,
+ HomeDTO.class,
+ UserDTO.class
+})
+public class NullValuePropertyMappingTest {
+
+ @Test
+ @WithClasses(CustomerMapper.class)
+ public void testStrategyAppliedOnForgedMethod() {
+
+ Customer customer = new Customer();
+ customer.setAddress( null );
+
+ UserDTO userDTO = new UserDTO();
+ userDTO.setHomeDTO( new HomeDTO() );
+ userDTO.getHomeDTO().setAddressDTO( new AddressDTO() );
+ userDTO.getHomeDTO().getAddressDTO().setHouseNo( 5 );
+ userDTO.setDetails( Arrays.asList( "green hair" ) );
+
+ CustomerMapper.INSTANCE.mapCustomer( customer, userDTO );
+
+ assertThat( userDTO.getHomeDTO() ).isNotNull();
+ assertThat( userDTO.getHomeDTO().getAddressDTO() ).isNotNull();
+ assertThat( userDTO.getHomeDTO().getAddressDTO().getHouseNo() ).isEqualTo( 5 );
+ assertThat( userDTO.getDetails() ).isNotNull();
+ assertThat( userDTO.getDetails() ).containsExactly( "green hair" );
+ }
+
+ @Test
+ @WithClasses({ NvpmsConfig.class, CustomerNvpmsOnConfigMapper.class })
+ public void testHierarchyIgnoreOnConfig() {
+ testConfig( ( Customer s, CustomerDTO t ) -> CustomerNvpmsOnConfigMapper.INSTANCE.map( s, t ) );
+ }
+
+ @Test
+ @WithClasses(CustomerNvpmsOnMapperMapper.class)
+ public void testHierarchyIgnoreOnMapping() {
+ testConfig( ( Customer s, CustomerDTO t ) -> CustomerNvpmsOnMapperMapper.INSTANCE.map( s, t ) );
+ }
+
+ @Test
+ @WithClasses(CustomerNvpmsOnBeanMappingMethodMapper.class)
+ public void testHierarchyIgnoreOnBeanMappingMethod() {
+ testConfig( ( Customer s, CustomerDTO t ) -> CustomerNvpmsOnBeanMappingMethodMapper.INSTANCE.map( s, t ) );
+ }
+
+ @Test
+ @WithClasses(CustomerNvpmsPropertyMappingMapper.class)
+ public void testHierarchyIgnoreOnPropertyMappingMehtod() {
+ testConfig( ( Customer s, CustomerDTO t ) -> CustomerNvpmsPropertyMappingMapper.INSTANCE.map( s, t ) );
+ }
+
+ @Test
+ @WithClasses(CustomerDefaultMapper.class)
+ public void testStrategyDefaultAppliedOnForgedMethod() {
+
+ Customer customer = new Customer();
+ customer.setAddress( null );
+
+ UserDTO userDTO = new UserDTO();
+ userDTO.setHomeDTO( new HomeDTO() );
+ userDTO.getHomeDTO().setAddressDTO( new AddressDTO() );
+ userDTO.getHomeDTO().getAddressDTO().setHouseNo( 5 );
+ userDTO.setDetails( Arrays.asList( "green hair" ) );
+
+ CustomerDefaultMapper.INSTANCE.mapCustomer( customer, userDTO );
+
+ assertThat( userDTO.getHomeDTO() ).isNotNull();
+ assertThat( userDTO.getHomeDTO().getAddressDTO() ).isNotNull();
+ assertThat( userDTO.getHomeDTO().getAddressDTO().getHouseNo() ).isNull();
+ assertThat( userDTO.getDetails() ).isNotNull();
+ assertThat( userDTO.getDetails() ).isEmpty();
+ }
+
+ @Test
+ @Ignore // test gives different results for JDK and JDT
+ @WithClasses(ErroneousCustomerMapper1.class)
+ @ExpectedCompilationOutcome(
+ value = CompilationResult.FAILED,
+ diagnostics = {
+ @Diagnostic(type = ErroneousCustomerMapper1.class,
+ kind = javax.tools.Diagnostic.Kind.ERROR,
+ line = 20,
+ messageRegExp = "Default value and nullValuePropertyMappingStrategy are both defined in @Mapping, " +
+ "either define a defaultValue or an nullValuePropertyMappingStrategy.")
+ }
+ )
+ public void testBothDefaultValueAndNvpmsDefined() {
+ }
+
+ @Test
+ @Ignore // test gives different results for JDK and JDT
+ @WithClasses(ErroneousCustomerMapper2.class)
+ @ExpectedCompilationOutcome(
+ value = CompilationResult.FAILED,
+ diagnostics = {
+ @Diagnostic(type = ErroneousCustomerMapper2.class,
+ kind = javax.tools.Diagnostic.Kind.ERROR,
+ line = 20,
+ messageRegExp = "Expression and nullValuePropertyMappingStrategy are both defined in @Mapping, " +
+ "either define an expression or an nullValuePropertyMappingStrategy.")
+ }
+ )
+ public void testBothExpressionAndNvpmsDefined() {
+ }
+
+ @Test
+ @Ignore // test gives different results for JDK and JDT
+ @WithClasses(ErroneousCustomerMapper3.class)
+ @ExpectedCompilationOutcome(
+ value = CompilationResult.FAILED,
+ diagnostics = {
+ @Diagnostic(type = ErroneousCustomerMapper3.class,
+ kind = javax.tools.Diagnostic.Kind.ERROR,
+ line = 20,
+ messageRegExp = "DefaultExpression and nullValuePropertyMappingStrategy are both defined in " +
+ "@Mapping, either define a defaultExpression or an nullValuePropertyMappingStrategy.")
+ }
+ )
+ public void testBothDefaultExpressionAndNvpmsDefined() {
+ }
+
+ @Test
+ @Ignore // test gives different results for JDK and JDT
+ @WithClasses(ErroneousCustomerMapper4.class)
+ @ExpectedCompilationOutcome(
+ value = CompilationResult.FAILED,
+ diagnostics = {
+ @Diagnostic(type = ErroneousCustomerMapper4.class,
+ kind = javax.tools.Diagnostic.Kind.ERROR,
+ line = 20,
+ messageRegExp = "Constant and nullValuePropertyMappingStrategy are both defined in @Mapping, " +
+ "either define a constant or an nullValuePropertyMappingStrategy.")
+ }
+ )
+ public void testBothConstantAndNvpmsDefined() {
+ }
+
+ @Test
+ @Ignore // test gives different results for JDK and JDT
+ @WithClasses(ErroneousCustomerMapper5.class)
+ @ExpectedCompilationOutcome(
+ value = CompilationResult.FAILED,
+ diagnostics = {
+ @Diagnostic(type = ErroneousCustomerMapper5.class,
+ kind = javax.tools.Diagnostic.Kind.ERROR,
+ line = 20,
+ messageRegExp = "Ignore and nullValuePropertyMappingStrategy are both defined in @Mapping, " +
+ "either define ignore or an nullValuePropertyMappingStrategy.")
+ }
+ )
+ public void testBothIgnoreAndNvpmsDefined() {
+ }
+
+ private void testConfig(BiConsumer customerMapper) {
+
+ Customer customer = new Customer();
+ customer.setAddress( null );
+
+ CustomerDTO customerDto = new CustomerDTO();
+ customerDto.setAddress( new AddressDTO() );
+ customerDto.getAddress().setHouseNo( 5 );
+ customerDto.setDetails( Arrays.asList( "green hair" ) );
+
+ customerMapper.accept( customer, customerDto );
+
+ assertThat( customerDto.getAddress() ).isNotNull();
+ assertThat( customerDto.getAddress().getHouseNo() ).isEqualTo( 5 );
+ assertThat( customerDto.getDetails() ).isNotNull();
+ assertThat( customerDto.getDetails() ).containsExactly( "green hair" );
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/NvpmsConfig.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/NvpmsConfig.java
new file mode 100644
index 000000000..bf89b1e98
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/NvpmsConfig.java
@@ -0,0 +1,13 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.nullvaluepropertymapping;
+
+import org.mapstruct.MapperConfig;
+import org.mapstruct.NullValuePropertyMappingStrategy;
+
+@MapperConfig( nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE )
+public interface NvpmsConfig {
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/UserDTO.java b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/UserDTO.java
new file mode 100644
index 000000000..9dc9af6ef
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/nullvaluepropertymapping/UserDTO.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.nullvaluepropertymapping;
+
+import java.util.List;
+
+public class UserDTO {
+
+ private HomeDTO homeDTO;
+ private List details;
+
+ public HomeDTO getHomeDTO() {
+ return homeDTO;
+ }
+
+ public void setHomeDTO(HomeDTO homeDTO) {
+ this.homeDTO = homeDTO;
+ }
+
+ public List getDetails() {
+ return details;
+ }
+
+ public void setDetails(List details) {
+ this.details = details;
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java
index 7454b36f8..570922b6c 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java
@@ -174,8 +174,8 @@ public class QualifierTest {
@Diagnostic(type = ErroneousMovieFactoryMapper.class,
kind = Kind.ERROR,
line = 24,
- messageRegExp = "'nullValueMappingStrategy', 'resultType' and 'qualifiedBy' are undefined in " +
- "@BeanMapping, define at least one of them."),
+ messageRegExp = "'nullValueMappingStrategy', 'nullValuePropertyMappingStrategy', 'resultType' and " +
+ "'qualifiedBy' are undefined in @BeanMapping, define at least one of them."),
@Diagnostic(type = ErroneousMovieFactoryMapper.class,
kind = Kind.ERROR,
line = 24,
diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNcvsAlwaysMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNcvsAlwaysMapperImpl.java
index 3e45e345e..bd1575a20 100644
--- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNcvsAlwaysMapperImpl.java
+++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNcvsAlwaysMapperImpl.java
@@ -26,27 +26,18 @@ public class DomainDtoWithNcvsAlwaysMapperImpl implements DomainDtoWithNcvsAlway
return null;
}
- Domain domain = new Domain();
+ Domain domain = createNullDomain();
if ( source.hasStringsInitialized() ) {
domain.setLongsInitialized( stringListToLongSet( source.getStringsInitialized() ) );
}
- else {
- domain.setLongsInitialized( null );
- }
if ( source.hasStrings() ) {
domain.setLongs( stringListToLongSet( source.getStrings() ) );
}
- else {
- domain.setLongs( null );
- }
if ( source.hasStrings() ) {
List list = source.getStrings();
domain.setStrings( new HashSet( list ) );
}
- else {
- domain.setStrings( null );
- }
if ( source.hasStringsWithDefault() ) {
List list1 = source.getStringsWithDefault();
domain.setStringsWithDefault( new ArrayList( list1 ) );
@@ -58,9 +49,6 @@ public class DomainDtoWithNcvsAlwaysMapperImpl implements DomainDtoWithNcvsAlway
List list2 = source.getStringsInitialized();
domain.setStringsInitialized( new HashSet( list2 ) );
}
- else {
- domain.setStringsInitialized( null );
- }
return domain;
}
diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapperImpl.java
index 137282b10..3ed50cb86 100644
--- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapperImpl.java
+++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsDefaultMapperImpl.java
@@ -32,9 +32,6 @@ public class DomainDtoWithNvmsDefaultMapperImpl implements DomainDtoWithNvmsDefa
if ( list != null ) {
domain.setStrings( new HashSet( list ) );
}
- else {
- domain.setStrings( new HashSet() );
- }
List list1 = source.getStringsWithDefault();
if ( list1 != null ) {
domain.setStringsWithDefault( new ArrayList( list1 ) );
@@ -46,9 +43,6 @@ public class DomainDtoWithNvmsDefaultMapperImpl implements DomainDtoWithNvmsDefa
if ( list2 != null ) {
domain.setStringsInitialized( new HashSet( list2 ) );
}
- else {
- domain.setStringsInitialized( new HashSet() );
- }
}
return domain;
diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsNullMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsNullMapperImpl.java
index b1b8b0963..34867626a 100644
--- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsNullMapperImpl.java
+++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithNvmsNullMapperImpl.java
@@ -26,7 +26,7 @@ public class DomainDtoWithNvmsNullMapperImpl implements DomainDtoWithNvmsNullMap
return null;
}
- Domain domain = new Domain();
+ Domain domain = createNullDomain();
domain.setLongsInitialized( stringListToLongSet( source.getStringsInitialized() ) );
domain.setLongs( stringListToLongSet( source.getStrings() ) );
@@ -34,9 +34,6 @@ public class DomainDtoWithNvmsNullMapperImpl implements DomainDtoWithNvmsNullMap
if ( list != null ) {
domain.setStrings( new HashSet( list ) );
}
- else {
- domain.setStrings( null );
- }
List list1 = source.getStringsWithDefault();
if ( list1 != null ) {
domain.setStringsWithDefault( new ArrayList( list1 ) );
@@ -48,9 +45,6 @@ public class DomainDtoWithNvmsNullMapperImpl implements DomainDtoWithNvmsNullMap
if ( list2 != null ) {
domain.setStringsInitialized( new HashSet( list2 ) );
}
- else {
- domain.setStringsInitialized( null );
- }
return domain;
}
diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapperImpl.java
index 085e89d5b..c44a9bc36 100644
--- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapperImpl.java
+++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_913/DomainDtoWithPresenceCheckMapperImpl.java
@@ -26,7 +26,7 @@ public class DomainDtoWithPresenceCheckMapperImpl implements DomainDtoWithPresen
return null;
}
- Domain domain = new Domain();
+ Domain domain = createNullDomain();
domain.setLongsInitialized( stringListToLongSet( source.getStringsInitialized() ) );
domain.setLongs( stringListToLongSet( source.getStrings() ) );
@@ -34,9 +34,6 @@ public class DomainDtoWithPresenceCheckMapperImpl implements DomainDtoWithPresen
List list = source.getStrings();
domain.setStrings( new HashSet( list ) );
}
- else {
- domain.setStrings( null );
- }
if ( source.hasStringsWithDefault() ) {
List list1 = source.getStringsWithDefault();
domain.setStringsWithDefault( new ArrayList( list1 ) );
@@ -48,9 +45,6 @@ public class DomainDtoWithPresenceCheckMapperImpl implements DomainDtoWithPresen
List list2 = source.getStringsInitialized();
domain.setStringsInitialized( new HashSet( list2 ) );
}
- else {
- domain.setStringsInitialized( null );
- }
return domain;
}