Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import java.util.Set;
import java.util.function.Supplier;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;

import org.mapstruct.ap.internal.gem.BuilderGem;
import org.mapstruct.ap.internal.gem.NullValueCheckStrategyGem;
Expand Down Expand Up @@ -471,7 +473,10 @@ private Assignment assignToPlainViaSetter(Type targetType, Assignment rhs) {
isFieldAssignment(),
includeSourceNullCheck,
includeSourceNullCheck && nvpms == SET_TO_NULL && !targetType.isPrimitive(),
nvpms == SET_TO_DEFAULT );
nvpms == SET_TO_DEFAULT,
hasTwoOrMoreSettersWithName(),
targetType
);
}
}

Expand Down Expand Up @@ -547,12 +552,32 @@ else if ( result.getSourceType().isStreamType() ) {
isFieldAssignment(),
true,
nvpms == SET_TO_NULL && !targetType.isPrimitive(),
nvpms == SET_TO_DEFAULT
nvpms == SET_TO_DEFAULT,
hasTwoOrMoreSettersWithName(),
targetType
);
}
return result;
}

private boolean hasTwoOrMoreSettersWithName() {
Element enclosingClass = this.targetWriteAccessor.getElement().getEnclosingElement();
if ( enclosingClass == null || !ElementKind.CLASS.equals( enclosingClass.getKind() ) ) {
return false;
}
String simpleWriteAccessorName = this.targetWriteAccessor.getSimpleName();
boolean firstMatchFound = false;
for ( Accessor setter : ctx.getTypeFactory().getType( enclosingClass.asType() ).getSetters() ) {
if ( setter.getSimpleName().equals( simpleWriteAccessorName ) ) {
if (firstMatchFound) {
return true;
}
firstMatchFound = true;
}
}
return false;
}

private Assignment assignToCollection(Type targetType, AccessorType targetAccessorType,
Assignment rhs) {
return new CollectionAssignmentBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
package org.mapstruct.ap.internal.model.assignment;

import java.util.ArrayList;
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;
Expand All @@ -22,19 +24,25 @@ public class SetterWrapper extends AssignmentWrapper {
private final boolean includeSourceNullCheck;
private final boolean setExplicitlyToNull;
private final boolean setExplicitlyToDefault;
private final boolean mustCastForNull;
private final Type targetImplementationType;

public SetterWrapper(Assignment rhs,
List<Type> thrownTypesToExclude,
boolean fieldAssignment,
boolean includeSourceNullCheck,
boolean setExplicitlyToNull,
boolean setExplicitlyToDefault) {
boolean setExplicitlyToDefault,
boolean mustCastForNull,
Type targetImplementationType) {

super( rhs, fieldAssignment );
this.thrownTypesToExclude = thrownTypesToExclude;
this.includeSourceNullCheck = includeSourceNullCheck;
this.setExplicitlyToDefault = setExplicitlyToDefault;
this.setExplicitlyToNull = setExplicitlyToNull;
this.mustCastForNull = mustCastForNull;
this.targetImplementationType = targetImplementationType;
}

public SetterWrapper(Assignment rhs, List<Type> thrownTypesToExclude, boolean fieldAssignment ) {
Expand All @@ -43,6 +51,8 @@ public SetterWrapper(Assignment rhs, List<Type> thrownTypesToExclude, boolean fi
this.includeSourceNullCheck = false;
this.setExplicitlyToNull = false;
this.setExplicitlyToDefault = false;
this.mustCastForNull = false;
this.targetImplementationType = null;
}

@Override
Expand All @@ -59,6 +69,15 @@ public List<Type> getThrownTypes() {
return result;
}

@Override
public Set<Type> getImportTypes() {
Set<Type> imported = new HashSet<>(super.getImportTypes());
if ( isSetExplicitlyToNull() && isMustCastForNull() ) {
imported.add( targetImplementationType );
}
return imported;
}

public boolean isSetExplicitlyToNull() {
return setExplicitlyToNull;
}
Expand All @@ -71,4 +90,7 @@ public boolean isIncludeSourceNullCheck() {
return includeSourceNullCheck;
}

public boolean isMustCastForNull() {
return mustCastForNull;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1061,7 +1061,7 @@ private TypeMirror boxed(TypeMirror possiblePrimitive) {
*
* @return an unmodifiable list of all setters
*/
private List<Accessor> getSetters() {
public List<Accessor> getSetters() {
if ( setters == null ) {
setters = Collections.unmodifiableList( filters.setterMethodsIn( getAllMethods() ) );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
}
<#elseif setExplicitlyToDefault || setExplicitlyToNull>
else {
<#if ext.targetBeanName?has_content>${ext.targetBeanName}.</#if>${ext.targetWriteAccessorName}<@lib.handleWrite><#if setExplicitlyToDefault><@lib.initTargetObject/><#else>${ext.targetType.null}</#if></@lib.handleWrite>;
<#if ext.targetBeanName?has_content>${ext.targetBeanName}.</#if>${ext.targetWriteAccessorName}<@lib.handleWrite><#if setExplicitlyToDefault><@lib.initTargetObject/><#else><#if mustCastForNull?? && mustCastForNull>(<@includeModel object=ext.targetType/>) </#if>${ext.targetType.null}</#if></@lib.handleWrite>;
}
</#if>
</#macro>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* 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._3949;

import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import org.mapstruct.NullValueCheckStrategy;
import org.mapstruct.NullValuePropertyMappingStrategy;
import org.mapstruct.factory.Mappers;

import java.time.LocalDate;

@Mapper(nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_NULL)
public interface Issue3949Mapper {

Issue3949Mapper INSTANCE = Mappers.getMapper( Issue3949Mapper.class );

void overwriteDate(@MappingTarget TargetDate target, DateSource dateSource);

void overwriteString(@MappingTarget TargetString target, StringSource stringSource);

void overwriteDateWithConversion(@MappingTarget TargetDate target, StringSource dateSource);

void overwriteStringWithConversion(@MappingTarget TargetString target, DateSource stringSource);

class DateSource {
public LocalDate getDate() {
return null;
}
}

class StringSource {
public String getDate() {
return null;
}
}

class TargetDate {
private LocalDate date = LocalDate.now();
private String string = "";

public void setDate(LocalDate date) {
this.date = date;
}

public void setDate(String date) {
this.string = date;
}

public LocalDate getDate() {
return date;
}

public String getString() {
return string;
}
}

class TargetString {
private LocalDate date = LocalDate.now();
private String string = "";

public void setDate(LocalDate date) {
this.date = date;
}

public void setDate(String date) {
this.string = date;
}

public String getDate() {
return string;
}

public LocalDate getDateValue() {
return date;
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* 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._3949;

import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.ProcessorTest;
import org.mapstruct.ap.testutil.WithClasses;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Tests if overloaded targets are correctly cast when set to null
*
* @author hduelme
*/
@IssueKey("3949")
public class Issue3949Test {

@ProcessorTest
@WithClasses({
Issue3949Mapper.class
})
void shouldCompileAndSetCorrectlyToNull() {
Issue3949Mapper.TargetDate shouldSetDateToNull = new Issue3949Mapper.TargetDate();
Issue3949Mapper.INSTANCE.overwriteDate( shouldSetDateToNull, new Issue3949Mapper.DateSource() );
assertThat( shouldSetDateToNull.getString() ).isNotNull();
assertThat( shouldSetDateToNull.getDate() ).isNull();

shouldSetDateToNull = new Issue3949Mapper.TargetDate();
Issue3949Mapper.INSTANCE.overwriteDateWithConversion( shouldSetDateToNull, new Issue3949Mapper.StringSource() );
assertThat( shouldSetDateToNull.getString() ).isNotNull();
assertThat( shouldSetDateToNull.getDate() ).isNull();

Issue3949Mapper.TargetString shouldSetStringToNull = new Issue3949Mapper.TargetString();
Issue3949Mapper.INSTANCE.overwriteString( shouldSetStringToNull, new Issue3949Mapper.StringSource() );
assertThat( shouldSetStringToNull.getDate() ).isNull();
assertThat( shouldSetStringToNull.getDateValue() ).isNotNull();

shouldSetStringToNull = new Issue3949Mapper.TargetString();
Issue3949Mapper.INSTANCE.overwriteStringWithConversion( shouldSetStringToNull,
new Issue3949Mapper.DateSource() );
assertThat( shouldSetStringToNull.getDate() ).isNull();
assertThat( shouldSetStringToNull.getDateValue() ).isNotNull();

}
}