diff --git a/processor/src/main/java/org/mapstruct/ap/model/IterableMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/model/IterableMappingMethod.java index 9685a525f..738000505 100644 --- a/processor/src/main/java/org/mapstruct/ap/model/IterableMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/model/IterableMappingMethod.java @@ -18,6 +18,7 @@ */ package org.mapstruct.ap.model; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -28,7 +29,9 @@ import org.mapstruct.ap.model.assignment.Assignment; import org.mapstruct.ap.model.assignment.SetterWrapper; import org.mapstruct.ap.model.common.Parameter; import org.mapstruct.ap.model.common.Type; +import org.mapstruct.ap.model.common.TypeFactory; import org.mapstruct.ap.model.source.Method; +import org.mapstruct.ap.prism.MapNullToDefaultPrism; import org.mapstruct.ap.util.Strings; /** @@ -42,6 +45,8 @@ public class IterableMappingMethod extends MappingMethod { private final Assignment elementAssignment; private final MethodReference factoryMethod; private final boolean overridden; + private final boolean mapNullToDefault; + private final TypeFactory typeFactory; public static class Builder { @@ -102,17 +107,30 @@ public class IterableMappingMethod extends MappingMethod { // target accessor is setter, so decorate assignment as setter assignment = new SetterWrapper( assignment, method.getThrownTypes() ); + // mapNullToDefault + MapNullToDefaultPrism prism = MapNullToDefaultPrism.getInstanceOn( method.getExecutable() ); + boolean mapNullToDefault = ( prism != null ) && prism.value(); + MethodReference factoryMethod = AssignmentFactory.createFactoryMethod( method.getReturnType(), ctx ); - return new IterableMappingMethod( method, assignment, factoryMethod ); + return new IterableMappingMethod( + method, + assignment, + factoryMethod, + mapNullToDefault, + ctx.getTypeFactory() + ); } } - private IterableMappingMethod(Method method, Assignment parameterAssignment, MethodReference factoryMethod) { + private IterableMappingMethod(Method method, Assignment parameterAssignment, MethodReference factoryMethod, + boolean mapNullToDefault, TypeFactory typeFactory ) { super( method ); this.elementAssignment = parameterAssignment; this.factoryMethod = factoryMethod; this.overridden = method.overridesMethod(); + this.mapNullToDefault = mapNullToDefault; + this.typeFactory = typeFactory; } public Parameter getSourceParameter() { @@ -137,9 +155,21 @@ public class IterableMappingMethod extends MappingMethod { types.addAll( elementAssignment.getImportTypes() ); } + if (mapNullToDefault) { + types.add( typeFactory.getType( Collections.class ) ); + } + return types; } + public boolean isMapNullToDefault() { + return mapNullToDefault; + } + + public boolean isOverridden() { + return overridden; + } + public String getLoopVariableName() { return Strings.getSaveVariableName( getSourceParameter().getType().getTypeParameters().get( 0 ).getName(), @@ -190,7 +220,4 @@ public class IterableMappingMethod extends MappingMethod { return true; } - public boolean isOverridden() { - return overridden; - } } diff --git a/processor/src/main/resources/org.mapstruct.ap.model.IterableMappingMethod.ftl b/processor/src/main/resources/org.mapstruct.ap.model.IterableMappingMethod.ftl index dced2ff33..b1131aa3c 100644 --- a/processor/src/main/resources/org.mapstruct.ap.model.IterableMappingMethod.ftl +++ b/processor/src/main/resources/org.mapstruct.ap.model.IterableMappingMethod.ftl @@ -21,7 +21,11 @@ <#if overridden>@Override <#lt>${accessibility.keyword} <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, ) <@throws/> { if ( ${sourceParameter.name} == null ) { + <#if !mapNullToDefault> return<#if returnType.name != "void"> null; + <#else> + return Collections.<${resultType.typeParameters[0].name}>emptyList(); + } <#if existingInstanceMapping> diff --git a/processor/src/test/java/org/mapstruct/ap/test/mapnulltodefault/CarMapper.java b/processor/src/test/java/org/mapstruct/ap/test/mapnulltodefault/CarMapper.java index 2d3ff0482..b4b3f9b79 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/mapnulltodefault/CarMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/mapnulltodefault/CarMapper.java @@ -18,6 +18,7 @@ */ package org.mapstruct.ap.test.mapnulltodefault; +import java.util.List; import java.util.UUID; import org.mapstruct.Mapper; @@ -50,4 +51,7 @@ public interface CarMapper { CarDto carToCarDto(Car car, String model); + @MapNullToDefault + List carsToCarDtos(List cars); + } diff --git a/processor/src/test/java/org/mapstruct/ap/test/mapnulltodefault/CarMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/mapnulltodefault/CarMapperTest.java index a42408c7d..58a080bf8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/mapnulltodefault/CarMapperTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/mapnulltodefault/CarMapperTest.java @@ -18,6 +18,8 @@ */ package org.mapstruct.ap.test.mapnulltodefault; +import java.util.Arrays; +import java.util.List; import static org.fest.assertions.Assertions.assertThat; @@ -67,7 +69,7 @@ public class CarMapperTest { assertThat( carDto2.getCatalogId() ).isNotEmpty(); } - @Test + @Test public void shouldMapExpressionAndConstantRegardlessNullArgSeveralSources() { //given Car car = new Car( "Morris", 2 ); @@ -92,4 +94,25 @@ public class CarMapperTest { assertThat( carDto2.getCatalogId() ).isNotEmpty(); } + @Test + public void shouldMapIterableWithNullArg() { + + //given + Car car = new Car( "Morris", 2 ); + + //when + List carDtos1 = CarMapper.INSTANCE.carsToCarDtos( Arrays.asList( car ) ); + + //then + assertThat( carDtos1 ).isNotNull(); + assertThat( carDtos1.size() ).isEqualTo( 1 ); + + //when + List carDtos2 = CarMapper.INSTANCE.carsToCarDtos( null ); + + //then + assertThat( carDtos2 ).isNotNull(); + assertThat( carDtos2.isEmpty() ).isTrue(); + + } }