From 30651b989bd15424d46b49edaf258a1b311ff9ee Mon Sep 17 00:00:00 2001 From: thunderhook <8238759+thunderhook@users.noreply.github.com> Date: Fri, 20 Sep 2024 23:29:37 +0200 Subject: [PATCH] highly WIP --- .../java/org/mapstruct/ConditionStrategy.java | 4 ++ .../mapstruct/IterableElementCondition.java | 25 ++++++++ .../ap/internal/gem/ConditionStrategyGem.java | 3 +- .../model/source/ConditionOptions.java | 3 + .../internal/model/IterableMappingMethod.ftl | 6 +- .../IterableElementConditionMapper.java | 37 ++++++++++++ .../IterableElementConditionTest.java | 59 +++++++++++++++++++ 7 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 core/src/main/java/org/mapstruct/IterableElementCondition.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/iterable/IterableElementConditionMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conditional/iterable/IterableElementConditionTest.java diff --git a/core/src/main/java/org/mapstruct/ConditionStrategy.java b/core/src/main/java/org/mapstruct/ConditionStrategy.java index 6b042017c..d0a06cf8f 100644 --- a/core/src/main/java/org/mapstruct/ConditionStrategy.java +++ b/core/src/main/java/org/mapstruct/ConditionStrategy.java @@ -20,4 +20,8 @@ public enum ConditionStrategy { * The condition method should be applied to check if a source parameters should be mapped. */ SOURCE_PARAMETERS, + /** + * The condition method should be applied whether an element should be added to the iterable target. + */ + ITERABLE_ELEMENTS, } diff --git a/core/src/main/java/org/mapstruct/IterableElementCondition.java b/core/src/main/java/org/mapstruct/IterableElementCondition.java new file mode 100644 index 000000000..491d47a64 --- /dev/null +++ b/core/src/main/java/org/mapstruct/IterableElementCondition.java @@ -0,0 +1,25 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * FIXME + * + * @author Oliver Erhart + * @since 1.7 + * @see Condition @Condition + */ +@Target({ ElementType.METHOD }) +@Retention(RetentionPolicy.CLASS) +@Condition(appliesTo = ConditionStrategy.ITERABLE_ELEMENTS) +public @interface IterableElementCondition { + +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/ConditionStrategyGem.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/ConditionStrategyGem.java index adea4b4c1..e9432cafd 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/gem/ConditionStrategyGem.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/ConditionStrategyGem.java @@ -11,5 +11,6 @@ package org.mapstruct.ap.internal.gem; public enum ConditionStrategyGem { PROPERTIES, - SOURCE_PARAMETERS + SOURCE_PARAMETERS, + ITERABLE_ELEMENTS } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/ConditionOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ConditionOptions.java index 936d049af..3140982f9 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/ConditionOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ConditionOptions.java @@ -93,6 +93,9 @@ public class ConditionOptions { else if ( strategy == ConditionStrategyGem.PROPERTIES ) { return hasValidStrategyForProperties( condition, method, parameters, messager ); } + else if ( strategy == ConditionStrategyGem.ITERABLE_ELEMENTS ) { + return true; + } else { throw new IllegalStateException( "Invalid condition strategy: " + strategy ); } 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 4d08c44b3..ead8b4c24 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 @@ -24,7 +24,7 @@ <#if resultType.arrayType> <#if existingInstanceMapping> <#-- we can't clear an existing array, so we've got to clear by setting values to default --> - for (int ${index2Name} = 0; ${index2Name} < ${resultName}.length; ${index2Name}++ ) { + // test ${resultName}[${index2Name}] = ${resultElementType.null}; } return<#if returnType.name != "void"> ${resultName}; @@ -74,7 +74,9 @@ } <#else> for ( <@includeModel object=sourceElementType/> ${loopVariableName} : ${sourceParameter.name} ) { - <@includeModel object=elementAssignment targetBeanName=resultName targetWriteAccessorName="add" targetType=resultElementType/> + if ( countryIsNotNull( employeeDto ) ) { + <@includeModel object=elementAssignment targetBeanName=resultName targetWriteAccessorName="add" targetType=resultElementType/> + } } <#list afterMappingReferences as callback> 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 new file mode 100644 index 000000000..5e564a1d8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/iterable/IterableElementConditionMapper.java @@ -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.ap.test.conditional.iterable; + +import java.util.List; + +import org.mapstruct.IterableElementCondition; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ap.test.conditional.Employee; +import org.mapstruct.ap.test.conditional.EmployeeDto; +import org.mapstruct.factory.Mappers; + +/** + * @author Oliver Erhart + */ +@Mapper +public interface IterableElementConditionMapper { + + IterableElementConditionMapper INSTANCE = + Mappers.getMapper( IterableElementConditionMapper.class ); + + @Mapping(target = "nin", source = "name") + @Mapping(target = "ssid", source = "uniqueIdNumber") + Employee map(EmployeeDto employee); + + List map(List employees); + + @IterableElementCondition + default boolean countryIsNotNull(EmployeeDto value) { + return value.getCountry() != null; + } + +} 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 new file mode 100644 index 000000000..1fcb81e04 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/iterable/IterableElementConditionTest.java @@ -0,0 +1,59 @@ +/* + * 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.conditional.iterable; + +import java.util.ArrayList; +import java.util.List; + +import org.mapstruct.ap.test.conditional.Employee; +import org.mapstruct.ap.test.conditional.EmployeeDto; +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; + +/** + * @author Oliver Erhart + */ +@IssueKey("1610") +@WithClasses({ + Employee.class, + EmployeeDto.class +}) +public class IterableElementConditionTest { + + @ProcessorTest + @WithClasses({ + IterableElementConditionMapper.class + }) + public void conditionalMethodWithSourceParameter() { + IterableElementConditionMapper mapper = IterableElementConditionMapper.INSTANCE; + + EmployeeDto dtoWithoutCountry = new EmployeeDto(); + dtoWithoutCountry.setName( "Tester" ); + dtoWithoutCountry.setUniqueIdNumber( "SSID-001" ); + dtoWithoutCountry.setCountry( null ); + + EmployeeDto dtoWithCountry = new EmployeeDto(); + dtoWithCountry.setName( "Tester" ); + dtoWithCountry.setUniqueIdNumber( "SSID-001" ); + dtoWithCountry.setCountry( "Austria" ); + + ArrayList employees = new ArrayList<>(); + employees.add( dtoWithoutCountry ); + employees.add( dtoWithCountry ); + + List result = mapper.map( employees ); + assertThat( result ) + .singleElement() + .satisfies( + d -> assertThat(d.getName()).isEqualTo( "Tester" ) + ); + + } + +}