From 728b42ac2582ecd29682bda1ba3042f27ad77862 Mon Sep 17 00:00:00 2001 From: thunderhook <8238759+thunderhook@users.noreply.github.com> Date: Sun, 22 Sep 2024 23:12:33 +0200 Subject: [PATCH] tried to support arrays as target - stopped because of null values in target array - intermediate method used for lists and then `resultList.toArray(new Employee[0]);` would be nice - or somehow using a list in the forged method anyways: ``` public Employee[] mapListToArray(List employees) { if (employees == null) { return null; } List resultList = new ArrayList<>(); for ( EmployeeDto employeeDto : employees ) { if ( countryIsNotNull( employeeDto ) ) { resultList.add(map(employeeDto)); } } return resultList.toArray(new Employee[0]); } ``` --- .../internal/model/IterableMappingMethod.java | 11 +--- .../internal/model/IterableMappingMethod.ftl | 59 +++++++++++------ .../IterableElementConditionMapper.java | 9 ++- .../IterableElementConditionTest.java | 66 +++++++++++++++---- 4 files changed, 103 insertions(+), 42 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/IterableMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/IterableMappingMethod.java index dd375b502..d638082ca 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/IterableMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/IterableMappingMethod.java @@ -83,11 +83,7 @@ public class IterableMappingMethod extends ContainerMappingMethod { } public boolean hasIterableConditionMethod() { - return getIterableConditionMethod() != null; - } - - public Method getIterableConditionMethod() { - return iterableConditionMethod; + return iterableConditionMethod != null; } public MethodReference getIterableConditionMethodReference() { @@ -101,10 +97,7 @@ public class IterableMappingMethod extends ContainerMappingMethod { ) ); - return MethodReference.forForgedMethod( - iterableConditionMethod, - parameterBindings - ); + return MethodReference.forForgedMethod( iterableConditionMethod, parameterBindings ); } private IterableMappingMethod(Method method, List annotations, diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/IterableMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/IterableMappingMethod.ftl index adc4005d8..3c03881a6 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/IterableMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/IterableMappingMethod.ftl @@ -11,7 +11,7 @@ <#if overridden>@Override <#lt>${accessibility.keyword} <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, )<@throws/> { - <#list beforeMappingReferencesWithoutMappingTarget as callback> +<#list beforeMappingReferencesWithoutMappingTarget as callback> <@includeModel object=callback targetBeanName=resultName targetType=resultType/> <#if !callback_has_next> @@ -41,19 +41,19 @@ } - <#if resultType.arrayType> - <#if !existingInstanceMapping> - <#assign elementTypeString><@includeModel object=resultElementType/> - ${elementTypeString}[] ${resultName} = new ${elementTypeString?keep_before('[]')}[<@iterableSize/>]${elementTypeString?replace('[^\\[\\]]+', '', 'r')}; - - <#else> +<#-- <#if resultType.arrayType>--> +<#-- <#if !existingInstanceMapping>--> +<#-- <#assign elementTypeString><@includeModel object=resultElementType/>--> +<#-- ${elementTypeString}[] ${resultName} = new ${elementTypeString?keep_before('[]')}[<@iterableSize/>]${elementTypeString?replace('[^\\[\\]]+', '', 'r')};--> +<#-- --> +<#-- <#else>--> <#if existingInstanceMapping> ${resultName}.clear(); <#else> <#-- Use the interface type on the left side, except it is java.lang.Iterable; use the implementation type - if present - on the right side --> <@iterableLocalVarDef/> ${resultName} = <@includeModel object=iterableCreation useSizeIfPossible=true/>; - +<#-- --> <#list beforeMappingReferencesWithMappingTarget as callback> <@includeModel object=callback targetBeanName=resultName targetType=resultType/> <#if !callback_has_next> @@ -61,17 +61,32 @@ <#if resultType.arrayType> - int ${index1Name} = 0; - for ( <@includeModel object=sourceElementType/> ${loopVariableName} : ${sourceParameter.name} ) { - <#if existingInstanceMapping> - if ( ( ${index1Name} >= ${resultName}.length ) || ( ${index1Name} >= <@iterableSize/> ) ) { - break; - } - - <@includeModel object=elementAssignment targetWriteAccessorName=resultName+"[${index1Name}]" targetType=resultElementType isTargetDefined=true/> - ${index1Name}++; - } - <#else> + + + <#if true> +<#-- <#if resultType.arrayType>--> +<#-- int ${index1Name} = 0;--> +<#-- for ( <@includeModel object=sourceElementType/> ${loopVariableName} : ${sourceParameter.name} ) {--> +<#-- <#if existingInstanceMapping>--> +<#-- if ( ( ${index1Name} >= ${resultName}.length ) || ( ${index1Name} >= <@iterableSize/> ) ) {--> +<#-- break;--> +<#-- }--> +<#-- --> +<#-- <#if hasIterableConditionMethod()>--> +<#-- if ( <@includeModel object=iterableConditionMethodReference /> ) {--> +<#-- <@includeModel object=elementAssignment targetWriteAccessorName=resultName+"[${index1Name}]" targetType=resultElementType isTargetDefined=true/>--> +<#-- ${index1Name}++;--> +<#-- }--> +<#-- <#else>--> +<#-- <@includeModel object=elementAssignment targetWriteAccessorName=resultName+"[${index1Name}]" targetType=resultElementType isTargetDefined=true/>--> +<#-- ${index1Name}++;--> +<#-- --> +<#-- }--> +<#-- Employee[] employeeTmp = employees.stream()--> +<#-- .filter(this::countryIsNotNull)--> +<#-- .map(this::map)--> +<#-- .toArray(Employee[]::new);--> +<#-- <#else>--> for ( <@includeModel object=sourceElementType/> ${loopVariableName} : ${sourceParameter.name} ) { <#if hasIterableConditionMethod()> if ( <@includeModel object=iterableConditionMethodReference /> ) { @@ -90,7 +105,11 @@ <#if returnType.name != "void"> - return ${resultName}; + <#if hasIterableConditionMethod() && resultType.arrayType> + ${resultName}.toArray( new <@includeModel object=resultElementType/>[0] ); + <#else> + return ${resultName}; + } <#macro throws> diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/iterable/IterableElementConditionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/iterable/IterableElementConditionMapper.java index 5e564a1d8..e27fa022f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/iterable/IterableElementConditionMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/iterable/IterableElementConditionMapper.java @@ -27,7 +27,14 @@ public interface IterableElementConditionMapper { @Mapping(target = "ssid", source = "uniqueIdNumber") Employee map(EmployeeDto employee); - List map(List employees); + List mapListToList(List employees); + + List mapArrayToList(EmployeeDto[] employees); + +// Employee[] mapListToArray(List employees); +// +// Employee[] mapArrayToArray(EmployeeDto[] employees); + @IterableElementCondition default boolean countryIsNotNull(EmployeeDto value) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/iterable/IterableElementConditionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/iterable/IterableElementConditionTest.java index 1fcb81e04..3cd7fb002 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conditional/iterable/IterableElementConditionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/iterable/IterableElementConditionTest.java @@ -6,7 +6,9 @@ package org.mapstruct.ap.test.conditional.iterable; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; import org.mapstruct.ap.test.conditional.Employee; import org.mapstruct.ap.test.conditional.EmployeeDto; @@ -22,16 +24,58 @@ import static org.assertj.core.api.Assertions.assertThat; @IssueKey("1610") @WithClasses({ Employee.class, - EmployeeDto.class + EmployeeDto.class, + IterableElementConditionMapper.class, }) public class IterableElementConditionTest { @ProcessorTest - @WithClasses({ - IterableElementConditionMapper.class - }) - public void conditionalMethodWithSourceParameter() { - IterableElementConditionMapper mapper = IterableElementConditionMapper.INSTANCE; + public void conditionalMethodListToList() { + + List result = IterableElementConditionMapper.INSTANCE.mapListToList( setupList() ); + + assertThatOnlyFilteredValuesMapped( result ); + } + + @ProcessorTest + public void conditionalMethodArrayToList() { + + List result = IterableElementConditionMapper.INSTANCE.mapArrayToList( setupArray() ); + + assertThatOnlyFilteredValuesMapped( result ); + } + +// @ProcessorTest +// public void conditionalMethodListToArray() { +// +// Employee[] result = IterableElementConditionMapper.INSTANCE.mapListToArray( setupList() ); +// +// assertThatOnlyFilteredValuesMapped( result ); +// } +// +// @ProcessorTest +// public void conditionalMethodArrayToArray() { +// +// Employee[] result = IterableElementConditionMapper.INSTANCE.mapArrayToArray( setupArray() ); +// +// assertThatOnlyFilteredValuesMapped( result ); +// } + + private static void assertThatOnlyFilteredValuesMapped(List result) { + assertThat( result ) + .singleElement() + .satisfies( + d -> assertThat( d.getName() ).isEqualTo( "Tester" ) + ); + } + + private static void assertThatOnlyFilteredValuesMapped(Employee[] result) { + assertThatOnlyFilteredValuesMapped( + Arrays.stream( result ).collect( Collectors.toList() ) + ); + } + + private static ArrayList setupList() { EmployeeDto dtoWithoutCountry = new EmployeeDto(); dtoWithoutCountry.setName( "Tester" ); @@ -47,13 +91,11 @@ public class IterableElementConditionTest { employees.add( dtoWithoutCountry ); employees.add( dtoWithCountry ); - List result = mapper.map( employees ); - assertThat( result ) - .singleElement() - .satisfies( - d -> assertThat(d.getName()).isEqualTo( "Tester" ) - ); + return employees; + } + private static EmployeeDto[] setupArray() { + return setupList().toArray( new EmployeeDto[0] ); } }