#1685 completing nullvaluepropertymappingstrategy (#1697)

This commit is contained in:
Sjaak Derksen 2019-01-27 21:09:44 +01:00 committed by GitHub
parent abd1cfb6a1
commit 57caee250f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 739 additions and 118 deletions

View File

@ -10,6 +10,11 @@ package org.mapstruct;
*
* <b>Note:</b> This strategy is not in effect when the a specific source presence check method is defined
* in the service provider interface (SPI).
* <p>
* <b>Note</b>: some types of mappings (collections, maps), in which MapStruct is instructed to use a getter or adder
* as target accessor see {@link CollectionMappingStrategy}, MapStruct will always generate a source property null
* check, regardless the value of the {@link NullValueCheckStrategy} to avoid addition of {@code null} to the target
* collection or map.
*
* @author Sean Huang
*/

View File

@ -12,9 +12,15 @@ package org.mapstruct;
* 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
* The enum <b>only applies to update methods</b>: methods that update a pre-existing target (annotated with
* {@code @}{@link MappingTarget}).
*
* <p>
* <b>Note</b>: some types of mappings (collections, maps), in which MapStruct is instructed to use a getter or adder
* as target accessor see {@link CollectionMappingStrategy}, MapStruct will always generate a source property
* null check, regardless the value of the {@link NullValuePropertyMappingStrategy} to avoid addition of {@code null}
* to the target collection or map. Since the target is assumed to be initialised this strategy will not be applied.
*
* @author Sjaak Derksen
* @since 1.3
*/
@ -27,6 +33,8 @@ public enum NullValuePropertyMappingStrategy {
/**
* If a source bean property equals {@code null} the target bean property will be set to its default value.
* Make sure that a {@link Mapping#defaultValue()} is defined if no empty constructor is available on
* the default value.
*/
SET_TO_DEFAULT,

View File

@ -2138,12 +2138,22 @@ MapStruct offers control over the property to set in an `@MappingTarget` annotat
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`).
1. By specifying `nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT` on `@Mapping`, `@BeanMapping`, `@Mapper` or `@MappingConfig`, the mapping result can be altered to return *default* values. Please note that a default constructor is required. If not available, use the `@Mapping#defaultValue`. For `List` MapStruct generates an `ArrayList`, for `Map` mapstruct generates a `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`.
[NOTE]
====
Some types of mappings (collections, maps), in which MapStruct is instructed to use a getter or adder as target accessor see `CollectionMappingStrategy`, MapStruct will always generate a source property
null check, regardless the value of the `NullValuePropertyMappingStrategy` to avoid addition of `null` to the target collection or map. Since the target is assumed to be initialised this strategy will not be applied.
====
[TIP]
====
`NullValuePropertyMappingStrategy` also applies when the presense checker returns `not present`.
====
[[checking-source-property-for-null-arguments]]
=== Controlling checking result for 'null' properties in bean mapping
@ -2171,6 +2181,11 @@ Some frameworks generate bean properties that have a source presence checker. Of
The source presence checker name can be changed in the MapStruct service provider interface (SPI). It can also be deactivated in this way.
====
[NOTE]
====
Some types of mappings (collections, maps), in which MapStruct is instructed to use a getter or adder as target accessor see `CollectionMappingStrategy`, MapStruct will always generate a source property
null check, regardless the value of the `NullValueheckStrategy` to avoid addition of `null` to the target collection or map.
====
[[exceptions]]
=== Exceptions

View File

@ -21,6 +21,9 @@ import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism;
import org.mapstruct.ap.internal.util.Message;
import org.mapstruct.ap.internal.util.accessor.Accessor;
import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.SET_TO_DEFAULT;
import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.SET_TO_NULL;
/**
* A builder that is used for creating an assignment to a collection.
*
@ -149,7 +152,8 @@ public class CollectionAssignmentBuilder {
PropertyMapping.TargetWriteAccessorType.isFieldAssignment( targetAccessorType ),
targetType,
true,
nvpms
nvpms == SET_TO_NULL && !targetType.isPrimitive(),
nvpms == SET_TO_DEFAULT
);
}
else if ( method.isUpdateMethod() && !targetImmutable ) {

View File

@ -49,7 +49,8 @@ import org.mapstruct.ap.internal.util.ValueProvider;
import org.mapstruct.ap.internal.util.accessor.Accessor;
import static org.mapstruct.ap.internal.model.common.Assignment.AssignmentType.DIRECT;
import static org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism.ALWAYS;
import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.SET_TO_DEFAULT;
import static org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism.SET_TO_NULL;
import static org.mapstruct.ap.internal.util.Collections.first;
import static org.mapstruct.ap.internal.util.Collections.last;
@ -285,10 +286,12 @@ public class PropertyMapping extends ModelElement {
// 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 );
// for update methods: determine null value property mapping strategy (determine value based on hierarchy)
if ( method.isUpdateMethod() ) {
NullValuePropertyMappingStrategyPrism nvpmsBean =
beanMapping != null ? beanMapping.getNullValuePropertyMappingStrategy() : null;
this.nvpms = mapperConfiguration.getNullValuePropertyMappingStrategy( nvpmsBean, nvpms );
}
// handle source
this.rightHandSide = getSourceRHS( sourceReference );
@ -463,14 +466,28 @@ public class PropertyMapping extends ModelElement {
return new UpdateWrapper(
rhs,
method.getThrownTypes(),
factory, isFieldAssignment(),
factory,
isFieldAssignment(),
targetType,
!rhs.isSourceReferenceParameter(),
nvpms
nvpms == SET_TO_NULL && !targetType.isPrimitive(),
nvpms == SET_TO_DEFAULT
);
}
else {
return new SetterWrapper( rhs, method.getThrownTypes(), nvcs, isFieldAssignment(), targetType );
boolean includeSourceNullCheck = SetterWrapper.doSourceNullCheck( rhs, nvcs, nvpms, targetType );
if ( !includeSourceNullCheck ) {
// solution for #834 introduced a local var and null check for nested properties always.
// however, a local var is not needed if there's no need to check for null.
rhs.setSourceLocalVarName( null );
}
return new SetterWrapper(
rhs,
method.getThrownTypes(),
isFieldAssignment(),
includeSourceNullCheck,
includeSourceNullCheck && nvpms == SET_TO_NULL && !targetType.isPrimitive(),
nvpms == SET_TO_DEFAULT );
}
}
@ -488,7 +505,14 @@ public class PropertyMapping extends ModelElement {
}
else {
// Possibly adding null to a target collection. So should be surrounded by an null check.
result = new SetterWrapper( result, method.getThrownTypes(), ALWAYS, isFieldAssignment(), targetType );
// TODO: what triggers this else branch? Should nvcs, nvpms be applied?
result = new SetterWrapper( result,
method.getThrownTypes(),
isFieldAssignment(),
true,
nvpms == SET_TO_NULL && !targetType.isPrimitive(),
nvpms == SET_TO_DEFAULT
);
}
return result;
}
@ -517,8 +541,9 @@ public class PropertyMapping extends ModelElement {
targetPropertyName,
arrayType,
targetType,
isFieldAssignment()
);
isFieldAssignment(),
nvpms == SET_TO_NULL && !targetType.isPrimitive(),
nvpms == SET_TO_DEFAULT );
return assignment;
}
@ -875,7 +900,8 @@ public class PropertyMapping extends ModelElement {
isFieldAssignment(),
targetType,
false,
null );
false,
false );
}
else {
assignment = new SetterWrapper( assignment, method.getThrownTypes(), isFieldAssignment() );

View File

@ -20,16 +20,22 @@ public class ArrayCopyWrapper extends AssignmentWrapper {
private final Type arraysType;
private final Type targetType;
private final boolean setExplicitlyToNull;
private final boolean setExplicitlyToDefault;
public ArrayCopyWrapper(Assignment rhs,
String targetPropertyName,
Type arraysType,
Type targetType,
boolean fieldAssignment) {
boolean fieldAssignment,
boolean setExplicitlyToNull,
boolean setExplicitlyToDefault) {
super( rhs, fieldAssignment );
this.arraysType = arraysType;
this.targetType = targetType;
rhs.setSourceLocalVarName( rhs.createLocalVarName( targetPropertyName ) );
this.setExplicitlyToDefault = setExplicitlyToDefault;
this.setExplicitlyToNull = setExplicitlyToNull;
}
@Override
@ -44,4 +50,12 @@ public class ArrayCopyWrapper extends AssignmentWrapper {
public boolean isIncludeSourceNullCheck() {
return true;
}
public boolean isSetExplicitlyToNull() {
return setExplicitlyToNull;
}
public boolean isSetExplicitlyToDefault() {
return setExplicitlyToDefault;
}
}

View File

@ -11,8 +11,11 @@ import java.util.List;
import org.mapstruct.ap.internal.model.common.Assignment;
import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism;
import org.mapstruct.ap.internal.prism.NullValuePropertyMappingStrategyPrism;
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;
/**
* Wraps the assignment in a target setter.
@ -23,22 +26,29 @@ public class SetterWrapper extends AssignmentWrapper {
private final List<Type> thrownTypesToExclude;
private final boolean includeSourceNullCheck;
private final boolean setExplicitlyToNull;
private final boolean setExplicitlyToDefault;
public SetterWrapper(Assignment rhs,
List<Type> thrownTypesToExclude,
NullValueCheckStrategyPrism nvms,
boolean fieldAssignment,
Type targetType) {
boolean includeSourceNullCheck,
boolean setExplicitlyToNull,
boolean setExplicitlyToDefault) {
super( rhs, fieldAssignment );
this.thrownTypesToExclude = thrownTypesToExclude;
this.includeSourceNullCheck = includeSourceNullCheck( rhs, nvms, targetType );
this.includeSourceNullCheck = includeSourceNullCheck;
this.setExplicitlyToDefault = setExplicitlyToDefault;
this.setExplicitlyToNull = setExplicitlyToNull;
}
public SetterWrapper(Assignment rhs, List<Type> thrownTypesToExclude, boolean fieldAssignment ) {
super( rhs, fieldAssignment );
this.thrownTypesToExclude = thrownTypesToExclude;
this.includeSourceNullCheck = false;
this.setExplicitlyToNull = false;
this.setExplicitlyToDefault = false;
}
@Override
@ -55,30 +65,40 @@ public class SetterWrapper extends AssignmentWrapper {
return result;
}
public boolean isSetExplicitlyToNull() {
return setExplicitlyToNull;
}
public boolean isSetExplicitlyToDefault() {
return setExplicitlyToDefault;
}
public boolean isIncludeSourceNullCheck() {
return includeSourceNullCheck;
}
/**
* Wraps the assignment in a target setter. include a null check when
*
* - Not if source is the parameter iso property, because the null check is than handled by the bean mapping
* - Not when source is primitive, you can't null check a primitive
* - The source property is fed to a conversion somehow before its assigned to the target
* - The user decided to ALLWAYS include a null check
* - When there's a source local variable name defined (e.g. nested source properties)
* - TODO: The last one I forgot..?
*
* @param rhs the source righthand side
* @param nvms null value check strategy
* @param targetType the target type
*
* @return include a null check
*/
private boolean includeSourceNullCheck(Assignment rhs, NullValueCheckStrategyPrism nvms, Type targetType) {
/**
* Wraps the assignment in a target setter. include a null check when
*
* - Not if source is the parameter iso property, because the null check is than handled by the bean mapping
* - Not when source is primitive, you can't null check a primitive
* - The source property is fed to a conversion somehow before its assigned to the target
* - The user decided to ALLWAYS include a null check
*
* @param rhs the source righthand side
* @param nvcs null value check strategy
* @param nvpms null value property mapping strategy
* @param targetType the target type
*
* @return include a null check
*/
public static boolean doSourceNullCheck(Assignment rhs, NullValueCheckStrategyPrism nvcs,
NullValuePropertyMappingStrategyPrism nvpms, Type targetType) {
return !rhs.isSourceReferenceParameter()
&& !rhs.getSourceType().isPrimitive()
&& (ALWAYS == nvms || rhs.getType().isConverted() || rhs.getSourceLocalVarName() != null
&& (ALWAYS == nvcs
|| SET_TO_DEFAULT == nvpms || IGNORE == nvpms
|| rhs.getType().isConverted()
|| (rhs.getType().isDirect() && targetType.isPrimitive()));
}
}

View File

@ -12,10 +12,6 @@ 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.
@ -28,8 +24,8 @@ 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;
private final boolean setExplicitlyToNull;
private final boolean setExplicitlyToDefault;
public UpdateWrapper( Assignment decoratedAssignment,
List<Type> thrownTypesToExclude,
@ -37,14 +33,15 @@ public class UpdateWrapper extends AssignmentWrapper {
boolean fieldAssignment,
Type targetType,
boolean includeSourceNullCheck,
NullValuePropertyMappingStrategyPrism nvpms) {
boolean setExplicitlyToNull,
boolean setExplicitlyToDefault ) {
super( decoratedAssignment, fieldAssignment );
this.thrownTypesToExclude = thrownTypesToExclude;
this.factoryMethod = factoryMethod;
this.targetImplementationType = determineImplType( factoryMethod, targetType );
this.includeSourceNullCheck = includeSourceNullCheck;
this.mapNullToDefault = nvpms == SET_TO_DEFAULT;
this.includeExplicitNullWhenSourceIsNull = nvpms == SET_TO_NULL;
this.setExplicitlyToDefault = setExplicitlyToDefault;
this.setExplicitlyToNull = setExplicitlyToNull;
}
private static Type determineImplType(Assignment factoryMethod, Type targetType) {
@ -97,11 +94,11 @@ public class UpdateWrapper extends AssignmentWrapper {
return includeSourceNullCheck;
}
public boolean isIncludeExplicitNullWhenSourceIsNull() {
return includeExplicitNullWhenSourceIsNull;
public boolean isSetExplicitlyToNull() {
return setExplicitlyToNull;
}
public boolean isMapNullToDefault() {
return mapNullToDefault;
public boolean isSetExplicitlyToDefault() {
return setExplicitlyToDefault;
}
}

View File

@ -14,9 +14,9 @@
<@assignToExistingTarget/>
<@lib.handleAssignment/>;
}
<#if mapNullToDefault || includeExplicitNullWhenSourceIsNull>
<#if setExplicitlyToDefault || setExplicitlyToNull>
else {
${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if mapNullToDefault><@lib.initTargetObject/><#else>null</#if></@lib.handleWrite>;
${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if setExplicitlyToDefault><@lib.initTargetObject/><#else>null</#if></@lib.handleWrite>;
}
</#if>
<#else>

View File

@ -30,12 +30,18 @@
</#macro>
<#--
local macro related to handleSourceReferenceNullCheck
note: the <#elseif setExplicitlyToDefault || setExplicitlyToNull> is only relevant for update mappings
the default value takes precedence
-->
<#macro elseDefaultAssignment>
<#if ext.defaultValueAssignment?? >
else {
<@handeDefaultAssigment/>
}
<#elseif setExplicitlyToDefault || setExplicitlyToNull>
else {
${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if setExplicitlyToDefault><@lib.initTargetObject/><#else>null</#if></@lib.handleWrite>;
}
</#if>
</#macro>
<#--
@ -142,7 +148,7 @@ Performs a default assignment with a default value.
<#if factoryMethod??>
<@includeModel object=factoryMethod targetType=ext.targetType/>
<#else>
new <@constructTargetObject/>()
<@constructTargetObject/>
</#if>
</@compress></#macro>
<#--
@ -152,9 +158,11 @@ Performs a default assignment with a default value.
-->
<#macro constructTargetObject><@compress single_line=true>
<#if ext.targetType.implementationType??>
<@includeModel object=ext.targetType.implementationType/>
new <@includeModel object=ext.targetType.implementationType/>()
<#elseif ext.targetType.arrayType>
new <@includeModel object=ext.targetType.componentType/>[0]
<#else>
<@includeModel object=ext.targetType/>
new <@includeModel object=ext.targetType/>()
</#if>
</@compress></#macro>
<#--

View File

@ -0,0 +1,56 @@
/*
* 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.bugs._1685;
import java.util.List;
public class ContactDataDTO {
private String email;
private String phone;
private String address;
private List<String> preferences;
private String[] settings;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public List<String> getPreferences() {
return preferences;
}
public void setPreferences(List<String> preferences) {
this.preferences = preferences;
}
public String[] getSettings() {
return settings;
}
public void setSettings(String[] settings) {
this.settings = settings;
}
}

View File

@ -0,0 +1,117 @@
/*
* 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.bugs._1685;
import org.junit.Rule;
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.runner.AnnotationProcessorTestRunner;
import org.mapstruct.ap.testutil.runner.GeneratedSource;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(AnnotationProcessorTestRunner.class)
@IssueKey("1685")
@WithClasses({
User.class,
UserDTO.class,
UserMapper.class,
ContactDataDTO.class
})
public class Issue1685Test {
@Rule
public final GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor(
UserMapper.class
);
@Test
public void testSetToNullWhenNVPMSSetToNull() {
User target = new User();
target.setAddress( "address" );
target.setEmail( "email" );
target.setName( "name" );
target.setPhone( 12345 );
target.addPreference( "preference" );
target.setSettings( new String[]{ "test" } );
UserDTO source = new UserDTO();
source.setContactDataDTO( new ContactDataDTO() );
source.getContactDataDTO().setAddress( "newAddress" );
source.getContactDataDTO().setEmail( null );
source.getContactDataDTO().setPhone( null );
source.setName( null );
UserMapper.INSTANCE.updateUserFromUserDTO( source, target );
assertThat( target.getAddress() ).isEqualTo( "newAddress" );
assertThat( target.getEmail() ).isNull();
assertThat( target.getPhone() ).isNull();
assertThat( target.getName() ).isNull();
assertThat( target.getPreferences() ).containsOnly( "preference" );
assertThat( target.getSettings() ).isNull();
}
@Test
public void testIgnoreWhenNVPMSIgnore() {
User target = new User();
target.setAddress( "address" );
target.setEmail( "email" );
target.setName( "name" );
target.setPhone( 12345 );
target.addPreference( "preference" );
target.setSettings( new String[]{ "test" } );
UserDTO source = new UserDTO();
source.setContactDataDTO( new ContactDataDTO() );
source.getContactDataDTO().setAddress( "newAddress" );
source.getContactDataDTO().setEmail( null );
source.getContactDataDTO().setPhone( null );
source.setName( null );
UserMapper.INSTANCE.updateUserFromUserAndIgnoreDTO( source, target );
assertThat( target.getAddress() ).isEqualTo( "newAddress" );
assertThat( target.getEmail() ).isEqualTo( "email" );
assertThat( target.getPhone() ).isEqualTo( 12345 );
assertThat( target.getName() ).isEqualTo( "name" );
assertThat( target.getPreferences() ).containsOnly( "preference" );
assertThat( target.getSettings() ).containsExactly( "test" );
}
@Test
public void testSetToDefaultWhenNVPMSSetToDefault() {
User target = new User();
target.setAddress( "address" );
target.setEmail( "email" );
target.setName( "name" );
target.setPhone( 12345 );
target.addPreference( "preference" );
target.setSettings( new String[]{ "test" } );
UserDTO source = new UserDTO();
source.setContactDataDTO( new ContactDataDTO() );
source.getContactDataDTO().setAddress( "newAddress" );
source.getContactDataDTO().setEmail( null );
source.getContactDataDTO().setPhone( null );
source.setName( null );
UserMapper.INSTANCE.updateUserFromUserAndDefaultDTO( source, target );
assertThat( target.getAddress() ).isEqualTo( "newAddress" );
assertThat( target.getEmail() ).isEqualTo( "" );
assertThat( target.getPhone() ).isEqualTo( 0 );
assertThat( target.getName() ).isEqualTo( "" );
assertThat( target.getPreferences() ).containsOnly( "preference" );
assertThat( target.getSettings() ).isEmpty();
}
}

View File

@ -0,0 +1,68 @@
/*
* 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.bugs._1685;
import java.util.ArrayList;
import java.util.List;
public class User {
private String name;
private String email;
private Integer phone;
private String address;
private List<String> preferences = new ArrayList<>( );
private String[] settings;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getPhone() {
return phone;
}
public void setPhone(Integer phone) {
this.phone = phone;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public void addPreference(String preference) {
preferences.add( preference );
}
public List<String> getPreferences() {
return preferences;
}
public String[] getSettings() {
return settings;
}
public void setSettings(String[] settings) {
this.settings = settings;
}
}

View File

@ -0,0 +1,27 @@
/*
* 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.bugs._1685;
public class UserDTO {
private String name;
private ContactDataDTO contactDataDTO;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ContactDataDTO getContactDataDTO() {
return contactDataDTO;
}
public void setContactDataDTO(ContactDataDTO contactDataDTO) {
this.contactDataDTO = contactDataDTO;
}
}

View File

@ -0,0 +1,44 @@
/*
* 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.bugs._1685;
import org.mapstruct.BeanMapping;
import org.mapstruct.CollectionMappingStrategy;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
import org.mapstruct.NullValuePropertyMappingStrategy;
import org.mapstruct.factory.Mappers;
@Mapper( collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED )
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper( UserMapper.class );
@Mappings({
@Mapping(source = "email", target = "contactDataDTO.email"),
@Mapping(source = "phone", target = "contactDataDTO.phone"),
@Mapping(source = "address", target = "contactDataDTO.address"),
@Mapping(source = "preferences", target = "contactDataDTO.preferences"),
@Mapping(source = "settings", target = "contactDataDTO.settings")
})
UserDTO userToUserDTO(User user);
@InheritInverseConfiguration
void updateUserFromUserDTO(UserDTO userDTO, @MappingTarget User user);
@InheritInverseConfiguration
@BeanMapping( nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE )
void updateUserFromUserAndIgnoreDTO(UserDTO userDTO, @MappingTarget User user);
@InheritInverseConfiguration
@BeanMapping( nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT )
@Mapping( target = "phone", source = "contactDataDTO.phone", defaultValue = "0" )
void updateUserFromUserAndDefaultDTO(UserDTO userDTO, @MappingTarget User user);
}

View File

@ -98,7 +98,7 @@ public class NestedSimpleBeansMappingTest {
// result
assertThat( smartMapping.getName() ).isEqualTo( user.getName() );
assertThat( smartMapping.getCar().getYear() ).isEqualTo( user.getCar().getYear() );
assertThat( smartMapping.getCar().getName() ).isEqualTo( "Toyota" );
assertThat( smartMapping.getCar().getName() ).isNull();
assertThat( user.getCar().getName() ).isNull();
assertWheels( smartMapping.getCar().getWheels(), user.getCar().getWheels() );
assertCar( smartMapping.getSecondCar(), user.getSecondCar() );

View File

@ -14,6 +14,7 @@ import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
import org.mapstruct.NullValueCheckStrategy;
import org.mapstruct.NullValuePropertyMappingStrategy;
import org.mapstruct.ap.test.nestedsourceproperties._target.ChartEntry;
import org.mapstruct.ap.test.nestedsourceproperties.source.Chart;
import org.mapstruct.factory.Mappers;
@ -21,7 +22,10 @@ import org.mapstruct.factory.Mappers;
/**
* @author Sjaak Derksen
*/
@Mapper( nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS )
@Mapper(
nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE
)
public abstract class ChartEntryToArtistUpdate {
public static final ChartEntryToArtistUpdate MAPPER = Mappers.getMapper( ChartEntryToArtistUpdate.class );

View File

@ -19,7 +19,7 @@ public interface CustomerDefaultMapper {
@Mapping(source = "address", target = "homeDTO.addressDTO")
void mapCustomer(Customer customer, @MappingTarget UserDTO userDTO);
@Mapping(source = "houseNumber", target = "houseNo")
@Mapping(source = "houseNumber", target = "houseNo", defaultValue = "0")
void mapCustomerHouse(Address address, @MappingTarget AddressDTO addrDTO);
}

View File

@ -0,0 +1,241 @@
/*
* 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.bugs._1685;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Generated;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2019-01-27T12:40:32+0100",
comments = "version: , compiler: Eclipse JDT (Batch) 1.2.100.v20160418-1457, environment: Java 1.8.0_181 (Oracle Corporation)"
)
public class UserMapperImpl implements UserMapper {
@Override
public UserDTO userToUserDTO(User user) {
if ( user == null ) {
return null;
}
UserDTO userDTO = new UserDTO();
userDTO.setContactDataDTO( userToContactDataDTO( user ) );
userDTO.setName( user.getName() );
return userDTO;
}
@Override
public void updateUserFromUserDTO(UserDTO userDTO, User user) {
if ( userDTO == null ) {
return;
}
String[] settings1 = userDTOContactDataDTOSettings( userDTO );
if ( settings1 != null ) {
user.setSettings( Arrays.copyOf( settings1, settings1.length ) );
}
else {
user.setSettings( null );
}
if ( userDTOContactDataDTOPreferences( userDTO ) != null ) {
for ( String contactDataDTOPreference : userDTOContactDataDTOPreferences( userDTO ) ) {
user.addPreference( contactDataDTOPreference );
}
}
user.setAddress( userDTOContactDataDTOAddress( userDTO ) );
String phone = userDTOContactDataDTOPhone( userDTO );
if ( phone != null ) {
user.setPhone( Integer.parseInt( phone ) );
}
else {
user.setPhone( null );
}
user.setEmail( userDTOContactDataDTOEmail( userDTO ) );
user.setName( userDTO.getName() );
}
@Override
public void updateUserFromUserAndIgnoreDTO(UserDTO userDTO, User user) {
if ( userDTO == null ) {
return;
}
String[] settings1 = userDTOContactDataDTOSettings( userDTO );
if ( settings1 != null ) {
user.setSettings( Arrays.copyOf( settings1, settings1.length ) );
}
if ( userDTOContactDataDTOPreferences( userDTO ) != null ) {
for ( String contactDataDTOPreference : userDTOContactDataDTOPreferences( userDTO ) ) {
user.addPreference( contactDataDTOPreference );
}
}
String address = userDTOContactDataDTOAddress( userDTO );
if ( address != null ) {
user.setAddress( address );
}
String phone = userDTOContactDataDTOPhone( userDTO );
if ( phone != null ) {
user.setPhone( Integer.parseInt( phone ) );
}
String email = userDTOContactDataDTOEmail( userDTO );
if ( email != null ) {
user.setEmail( email );
}
if ( userDTO.getName() != null ) {
user.setName( userDTO.getName() );
}
}
@Override
public void updateUserFromUserAndDefaultDTO(UserDTO userDTO, User user) {
if ( userDTO == null ) {
return;
}
String[] settings1 = userDTOContactDataDTOSettings( userDTO );
if ( settings1 != null ) {
user.setSettings( Arrays.copyOf( settings1, settings1.length ) );
}
else {
user.setSettings( new String[0] );
}
if ( userDTOContactDataDTOPreferences( userDTO ) != null ) {
for ( String contactDataDTOPreference : userDTOContactDataDTOPreferences( userDTO ) ) {
user.addPreference( contactDataDTOPreference );
}
}
String address = userDTOContactDataDTOAddress( userDTO );
if ( address != null ) {
user.setAddress( address );
}
else {
user.setAddress( new String() );
}
String phone = userDTOContactDataDTOPhone( userDTO );
if ( phone != null ) {
user.setPhone( Integer.parseInt( phone ) );
}
else {
user.setPhone( 0 );
}
String email = userDTOContactDataDTOEmail( userDTO );
if ( email != null ) {
user.setEmail( email );
}
else {
user.setEmail( new String() );
}
if ( userDTO.getName() != null ) {
user.setName( userDTO.getName() );
}
else {
user.setName( new String() );
}
}
protected ContactDataDTO userToContactDataDTO(User user) {
if ( user == null ) {
return null;
}
ContactDataDTO contactDataDTO = new ContactDataDTO();
if ( user.getPhone() != null ) {
contactDataDTO.setPhone( String.valueOf( user.getPhone() ) );
}
String[] settings = user.getSettings();
if ( settings != null ) {
contactDataDTO.setSettings( Arrays.copyOf( settings, settings.length ) );
}
List<String> list = user.getPreferences();
if ( list != null ) {
contactDataDTO.setPreferences( new ArrayList<String>( list ) );
}
contactDataDTO.setAddress( user.getAddress() );
contactDataDTO.setEmail( user.getEmail() );
return contactDataDTO;
}
private String[] userDTOContactDataDTOSettings(UserDTO userDTO) {
if ( userDTO == null ) {
return null;
}
ContactDataDTO contactDataDTO = userDTO.getContactDataDTO();
if ( contactDataDTO == null ) {
return null;
}
String[] settings = contactDataDTO.getSettings();
if ( settings == null ) {
return null;
}
return settings;
}
private List<String> userDTOContactDataDTOPreferences(UserDTO userDTO) {
if ( userDTO == null ) {
return null;
}
ContactDataDTO contactDataDTO = userDTO.getContactDataDTO();
if ( contactDataDTO == null ) {
return null;
}
List<String> preferences = contactDataDTO.getPreferences();
if ( preferences == null ) {
return null;
}
return preferences;
}
private String userDTOContactDataDTOAddress(UserDTO userDTO) {
if ( userDTO == null ) {
return null;
}
ContactDataDTO contactDataDTO = userDTO.getContactDataDTO();
if ( contactDataDTO == null ) {
return null;
}
String address = contactDataDTO.getAddress();
if ( address == null ) {
return null;
}
return address;
}
private String userDTOContactDataDTOPhone(UserDTO userDTO) {
if ( userDTO == null ) {
return null;
}
ContactDataDTO contactDataDTO = userDTO.getContactDataDTO();
if ( contactDataDTO == null ) {
return null;
}
String phone = contactDataDTO.getPhone();
if ( phone == null ) {
return null;
}
return phone;
}
private String userDTOContactDataDTOEmail(UserDTO userDTO) {
if ( userDTO == null ) {
return null;
}
ContactDataDTO contactDataDTO = userDTO.getContactDataDTO();
if ( contactDataDTO == null ) {
return null;
}
String email = contactDataDTO.getEmail();
if ( email == null ) {
return null;
}
return email;
}
}

View File

@ -25,6 +25,9 @@ public class UserDtoUpdateMapperSmartImpl implements UserDtoUpdateMapperSmart {
if ( user.getName() != null ) {
userDto.setName( user.getName() );
}
else {
userDto.setName( null );
}
if ( user.getCar() != null ) {
if ( userDto.getCar() == null ) {
userDto.setCar( new CarDto() );
@ -88,6 +91,9 @@ public class UserDtoUpdateMapperSmartImpl implements UserDtoUpdateMapperSmart {
if ( car.getName() != null ) {
mappingTarget.setName( car.getName() );
}
else {
mappingTarget.setName( null );
}
mappingTarget.setYear( car.getYear() );
if ( mappingTarget.getWheels() != null ) {
List<WheelDto> list = wheelListToWheelDtoList( car.getWheels() );
@ -133,6 +139,9 @@ public class UserDtoUpdateMapperSmartImpl implements UserDtoUpdateMapperSmart {
if ( roof.getType() != null ) {
mappingTarget.setType( roofTypeToExternalRoofType( roof.getType() ) );
}
else {
mappingTarget.setType( null );
}
}
protected void houseToHouseDto(House house, HouseDto mappingTarget) {
@ -143,6 +152,9 @@ public class UserDtoUpdateMapperSmartImpl implements UserDtoUpdateMapperSmart {
if ( house.getName() != null ) {
mappingTarget.setName( house.getName() );
}
else {
mappingTarget.setName( null );
}
mappingTarget.setYear( house.getYear() );
if ( house.getRoof() != null ) {
if ( mappingTarget.getRoof() == null ) {

View File

@ -42,10 +42,7 @@ public class FishTankMapperImpl implements FishTankMapper {
fishTankDto.setFish( fishToFishDto( source.getFish() ) );
fishTankDto.setMaterial( fishTankToMaterialDto( source ) );
fishTankDto.setQuality( waterQualityToWaterQualityDto( source.getQuality() ) );
Ornament ornament = sourceInteriorOrnament( source );
if ( ornament != null ) {
fishTankDto.setOrnament( ornamentToOrnamentDto( ornament ) );
}
fishTankDto.setOrnament( ornamentToOrnamentDto( sourceInteriorOrnament( source ) ) );
fishTankDto.setPlant( waterPlantToWaterPlantDto( source.getPlant() ) );
fishTankDto.setName( source.getName() );
@ -63,10 +60,7 @@ public class FishTankMapperImpl implements FishTankMapper {
fishTankDto.setFish( fishToFishDto( source.getFish() ) );
fishTankDto.setMaterial( fishTankToMaterialDto1( source ) );
fishTankDto.setQuality( waterQualityToWaterQualityDto( source.getQuality() ) );
Ornament ornament = sourceInteriorOrnament( source );
if ( ornament != null ) {
fishTankDto.setOrnament( ornamentToOrnamentDto( ornament ) );
}
fishTankDto.setOrnament( ornamentToOrnamentDto( sourceInteriorOrnament( source ) ) );
fishTankDto.setPlant( waterPlantToWaterPlantDto( source.getPlant() ) );
fishTankDto.setName( source.getName() );
@ -84,10 +78,7 @@ public class FishTankMapperImpl implements FishTankMapper {
fishTank.setFish( fishDtoToFish( source.getFish() ) );
fishTank.setQuality( waterQualityDtoToWaterQuality( source.getQuality() ) );
fishTank.setInterior( fishTankDtoToInterior( source ) );
MaterialTypeDto materialType = sourceMaterialMaterialType( source );
if ( materialType != null ) {
fishTank.setMaterial( materialTypeDtoToMaterialType( materialType ) );
}
fishTank.setMaterial( materialTypeDtoToMaterialType( sourceMaterialMaterialType( source ) ) );
fishTank.setPlant( waterPlantDtoToWaterPlant( source.getPlant() ) );
fishTank.setName( source.getName() );
@ -264,10 +255,7 @@ public class FishTankMapperImpl implements FishTankMapper {
WaterQualityReport waterQualityReport = new WaterQualityReport();
String name = waterQualityReportDtoOrganisationName( waterQualityReportDto );
if ( name != null ) {
waterQualityReport.setOrganisationName( name );
}
waterQualityReport.setOrganisationName( waterQualityReportDtoOrganisationName( waterQualityReportDto ) );
waterQualityReport.setVerdict( waterQualityReportDto.getVerdict() );
return waterQualityReport;

View File

@ -33,18 +33,9 @@ public class ArtistToChartEntryImpl implements ArtistToChartEntry {
}
if ( song != null ) {
chartEntry.setSongTitle( song.getTitle() );
String city = songArtistLabelStudioCity( song );
if ( city != null ) {
chartEntry.setCity( city );
}
String name = songArtistLabelStudioName( song );
if ( name != null ) {
chartEntry.setRecordedAt( name );
}
String name1 = songArtistName( song );
if ( name1 != null ) {
chartEntry.setArtistName( name1 );
}
chartEntry.setCity( songArtistLabelStudioCity( song ) );
chartEntry.setRecordedAt( songArtistLabelStudioName( song ) );
chartEntry.setArtistName( songArtistName( song ) );
}
if ( position != null ) {
chartEntry.setPosition( position );
@ -62,18 +53,9 @@ public class ArtistToChartEntryImpl implements ArtistToChartEntry {
ChartEntry chartEntry = new ChartEntry();
chartEntry.setSongTitle( song.getTitle() );
String city = songArtistLabelStudioCity( song );
if ( city != null ) {
chartEntry.setCity( city );
}
String name = songArtistLabelStudioName( song );
if ( name != null ) {
chartEntry.setRecordedAt( name );
}
String name1 = songArtistName( song );
if ( name1 != null ) {
chartEntry.setArtistName( name1 );
}
chartEntry.setCity( songArtistLabelStudioCity( song ) );
chartEntry.setRecordedAt( songArtistLabelStudioName( song ) );
chartEntry.setArtistName( songArtistName( song ) );
return chartEntry;
}

View File

@ -68,27 +68,12 @@ public class ChartEntryToArtistImpl extends ChartEntryToArtist {
ChartEntry chartEntry = new ChartEntry();
String title = chartSongTitle( chart );
if ( title != null ) {
chartEntry.setSongTitle( title );
}
chartEntry.setSongTitle( chartSongTitle( chart ) );
chartEntry.setChartName( chart.getName() );
String city = chartSongArtistLabelStudioCity( chart );
if ( city != null ) {
chartEntry.setCity( city );
}
String name = chartSongArtistLabelStudioName( chart );
if ( name != null ) {
chartEntry.setRecordedAt( name );
}
String name1 = chartSongArtistName( chart );
if ( name1 != null ) {
chartEntry.setArtistName( name1 );
}
List<Integer> positions = chartSongPositions( chart );
if ( positions != null ) {
chartEntry.setPosition( mapPosition( positions ) );
}
chartEntry.setCity( chartSongArtistLabelStudioCity( chart ) );
chartEntry.setRecordedAt( chartSongArtistLabelStudioName( chart ) );
chartEntry.setArtistName( chartSongArtistName( chart ) );
chartEntry.setPosition( mapPosition( chartSongPositions( chart ) ) );
return chartEntry;
}