#1306 Add new NullValuePropertyMappingStrategy which leaves @MappingTarget untouched (#1618)

This commit is contained in:
Sjaak Derksen 2018-10-25 22:14:26 +01:00 committed by GitHub
parent cfe0f6250c
commit 288813fc3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 1161 additions and 195 deletions

View File

@ -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.
*

View File

@ -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},

View File

@ -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},

View File

@ -242,6 +242,7 @@ public @interface Mapping {
* If not possible, MapStruct will try to apply a user defined mapping method.
* </li>
* </ul>
* <p>
* </li>
* <li>other
* <p>
@ -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;
}

View File

@ -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 = "<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 = "<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 = "<ANY_UNMAPPED>";

View File

@ -33,6 +33,6 @@ public enum NullValueCheckStrategy {
/**
* This option always includes a null check.
*/
ALWAYS,
ALWAYS;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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.<String>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.<String>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 );

View File

@ -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;
}
}

View File

@ -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() );

View File

@ -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<Type> 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<Type> getImportTypes() {
Set<Type> imported = new HashSet<Type>( super.getImportTypes() );
if ( isMapNullToDefault() && ( targetType.getImplementationType() != null ) ) {
imported.add( targetType.getImplementationType() );
}
return imported;
}
public boolean isIncludeElseBranch() {
return includeElseBranch;
}
public boolean isMapNullToDefault() {
return mapNullToDefault;
}
}

View File

@ -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<Type> 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;
}
}

View File

@ -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;
}

View File

@ -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<String> 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.<String>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<String> 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;
}

View File

@ -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<String> dependsOn =
mappingPrism.dependsOn() != null ? mappingPrism.dependsOn() : Collections.<String>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<String> 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.<String>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}";
}
}

View File

@ -20,5 +20,4 @@ public final class MappingConstantsPrism {
public static final String ANY_REMAINING = "<ANY_REMAINING>";
public static final String ANY_UNMAPPED = "<ANY_UNMAPPED>";
}

View File

@ -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;
}

View File

@ -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() );

View File

@ -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(<EXPRESSION>)\"." ),
PROPERTYMAPPING_INVALID_DEFAULT_EXPRESSION( "Value for default expression must be given in the form \"java(<EXPRESSION>)\"." ),
PROPERTYMAPPING_INVALID_PARAMETER_NAME( "Method has no source parameter named \"%s\". Method source parameters are: \"%s\"." ),

View File

@ -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>

View File

@ -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)

View File

@ -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/>;

View File

@ -5,12 +5,11 @@
*/
package org.mapstruct.ap.test.bugs._1273;
import java.util.ArrayList;
import java.util.List;
public class Dto {
List<Long> longs = new ArrayList<Long>();
List<Long> longs;
public List<Long> getLongs() {
return longs;

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 );

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<String> details;
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public List<String> getDetails() {
return details;
}
public void setDetails(List<String> details) {
this.details = details;
}
}

View File

@ -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<String> details;
public AddressDTO getAddress() {
return address;
}
public void setAddress(AddressDTO address) {
this.address = address;
}
public List<String> getDetails() {
return details;
}
public void setDetails(List<String> details) {
this.details = details;
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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<Customer, CustomerDTO> 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" );
}
}

View File

@ -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 {
}

View File

@ -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<String> details;
public HomeDTO getHomeDTO() {
return homeDTO;
}
public void setHomeDTO(HomeDTO homeDTO) {
this.homeDTO = homeDTO;
}
public List<String> getDetails() {
return details;
}
public void setDetails(List<String> details) {
this.details = details;
}
}

View File

@ -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,

View File

@ -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<String> list = source.getStrings();
domain.setStrings( new HashSet<String>( list ) );
}
else {
domain.setStrings( null );
}
if ( source.hasStringsWithDefault() ) {
List<String> list1 = source.getStringsWithDefault();
domain.setStringsWithDefault( new ArrayList<String>( list1 ) );
@ -58,9 +49,6 @@ public class DomainDtoWithNcvsAlwaysMapperImpl implements DomainDtoWithNcvsAlway
List<String> list2 = source.getStringsInitialized();
domain.setStringsInitialized( new HashSet<String>( list2 ) );
}
else {
domain.setStringsInitialized( null );
}
return domain;
}

View File

@ -32,9 +32,6 @@ public class DomainDtoWithNvmsDefaultMapperImpl implements DomainDtoWithNvmsDefa
if ( list != null ) {
domain.setStrings( new HashSet<String>( list ) );
}
else {
domain.setStrings( new HashSet<String>() );
}
List<String> list1 = source.getStringsWithDefault();
if ( list1 != null ) {
domain.setStringsWithDefault( new ArrayList<String>( list1 ) );
@ -46,9 +43,6 @@ public class DomainDtoWithNvmsDefaultMapperImpl implements DomainDtoWithNvmsDefa
if ( list2 != null ) {
domain.setStringsInitialized( new HashSet<String>( list2 ) );
}
else {
domain.setStringsInitialized( new HashSet<String>() );
}
}
return domain;

View File

@ -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<String>( list ) );
}
else {
domain.setStrings( null );
}
List<String> list1 = source.getStringsWithDefault();
if ( list1 != null ) {
domain.setStringsWithDefault( new ArrayList<String>( list1 ) );
@ -48,9 +45,6 @@ public class DomainDtoWithNvmsNullMapperImpl implements DomainDtoWithNvmsNullMap
if ( list2 != null ) {
domain.setStringsInitialized( new HashSet<String>( list2 ) );
}
else {
domain.setStringsInitialized( null );
}
return domain;
}

View File

@ -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<String> list = source.getStrings();
domain.setStrings( new HashSet<String>( list ) );
}
else {
domain.setStrings( null );
}
if ( source.hasStringsWithDefault() ) {
List<String> list1 = source.getStringsWithDefault();
domain.setStringsWithDefault( new ArrayList<String>( list1 ) );
@ -48,9 +45,6 @@ public class DomainDtoWithPresenceCheckMapperImpl implements DomainDtoWithPresen
List<String> list2 = source.getStringsInitialized();
domain.setStringsInitialized( new HashSet<String>( list2 ) );
}
else {
domain.setStringsInitialized( null );
}
return domain;
}